当PHP运行在FastCGI模式时,PHP FPM提供了一个名为fastcgi_finish_request的方法.按照文档上的说法,此方法可以提高请求的处理速度,如果有些处理可以在页面生成完后再进行,就可以使用这个方法.
听起来可能有些茫然,我们通过几个例子来说明一下:
代码如下 | 复制代码 |
<?php echo '例子:'; fastcgi_finish_request(); /* 响应完成, 关闭连接 */ /* 记录日志 */ file_put_contents('log.txt', '生存还是毁灭,这是个问题.'); ?> |
通过浏览器访问此脚本, 结果发现并没有输出相应的字符串,但却生成了相应的文件.由此说明在调用fastcgi_finish_request后,客户端响应就已经结束,但与此同时服务端脚本却继续运行!
合理利用这个特性可以大大提升用户体验,趁热打铁再来一个例子:
代码如下 | 复制代码 |
<?php echo '例子:'; file_put_contents('log.txt', date('Y-m-d H:i:s') . " 上传视频n", FILE_APPEND); fastcgi_finish_request(); sleep(1); file_put_contents('log.txt', date('Y-m-d H:i:s') . " 转换格式n", FILE_APPEND); sleep(1); file_put_contents('log.txt', date('Y-m-d H:i:s') . " 提取图片n", FILE_APPEND); ?> |
代码里用sleep模拟一些耗时的操作,浏览时没有被堵塞,程序却都执行了,具体看日志.
末了给您提个醒,Yahoo在Best Practices for Speeding Up Your Web Site中提到了Flush the Buffer Early,也就是利用PHP中的flush方法把内容尽快发到客户端去,它和本文介绍的fastcgi_finish_request有些许的类似.
另外, 从代码的可移植性讲的话, 可以在代码中附上如下代码:
代码如下 | 复制代码 |
if (!function_exists("fastcgi_finish_request")) { function fastcgi_finish_request() { } } |
不会造成代码部署在非fpm环境下造成问题.
PHP+AJAX 域名查询预备知识
本查询系统利用 PHP 和 JQUery 的 Ajax 功能实现了对域名信息的查询(这里主要实现了域名是否已经注册的查询)。系统主要用到了万网提供的域名查询 API 接口,相关知识点罗列如下:
JQUery Ajax 的实现:这部分内容具体可以参见 JQuery API 文档或本站即将推出的《JQUery 教程》。
file_get_contents 函数:把整个文件读入一个字符串,这里用于读取一个网页(万网 API 返回结果页面)。
simplexml_load_string 函数:用于解析一个 xml 文档到对象中。
strrpos 函数:用于定位字符串第一次出现的位置,这里用来搜索关键字。
域名查询系统需求分析
根据用户输入的域名,查询该域名是否已经被注册。
对域名注册信息(whois)进行查询,该功能本教程没有实现,可以参考已有功能来实现。
页面/文件信息
domain.html:表单提交及查询结果信息显示页面。
domain_check.php:处理查询域名信息的 PHP 文件。
万网域名查询 API 接口
接口采用HTTP,POST,GET协议。
调用URL:http://panda.www.net.cn/cgi-bin/check.cgi
参数名称:area_domain 值为标准域名,例:5idev.com
调用举例:http://panda.www.net.cn/cgi-bin/check.cgi?area_domain=5idev.com
返回XML:
代码如下 | 复制代码 |
<?xml version="1.0" encoding="gb2312"?> <property> <returncode>200</returncode> <key>5idev.com</key> <original>211 : Domain name is not available</original> </property> |
XML 结果说明:
returncod:接口调用状态。
key:表示当前 check 的域名。
original:域名 check 的结果。
original 有4个结果:
210 : Domain name is available:表示域名可以注册
211 : Domain name is not available:表示域名已经被注册
212 : Domain name is invalid:表示域名参数传输错误
214 : Unknown error:表示未知错误或查询异常
domain.html 页面关键代码
domain.html 页面实现了表单 Ajax 提交及域名查询结果信息显示。完整的代码如下(注意是utf-8编码):
代码如下 | 复制代码 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1 <body> </body> |
这里利用 JAuery 实现了 Ajax GET 方式的表单提交,并对输入的表单进行了初步非空检测。
域名查询 PHP 代码
下面是完整的用于查询域名信息和回应 Ajax 请求 PHP 源代码:
代码如下 | 复制代码 |
<?php $domain = htmlspecialchars(trim($_GET['domain'])); if( !$domain ){ echo '请输入要查询的域名,如:5idev.com'; exit; } // 调用万网域名查询API $area_domain = iconv("utf-8", "gb2312",$domain); $domain_api = 'http://panda.www.net.cn/cgi-bin/check.cgi?area_domain='.$area_domain; $contents = file_get_contents($domain_api); $xml = simplexml_load_string($contents); if (!empty($xml)) { switch($xml->original) { case '210 : Domain name is available':$result = '该域名可以注册';break; case '211 : Domain name is not available':$result = '该域名已经被注册';break; case '212 : Domain name is invalid':$result = '域名参数错误,请输入的域名格式';break; case '214 : Unknown error':$result='查询异常,请稍后再试';break; } } else { // 备用,只能查询国际域名 $url = 'http://www.checkdomain.com/cgi-bin/checkdomain.pl?domain='.$_GET<'domain'>; $fp = file_get_contents($url); if( strpos($fp, ', has already been registered') ){ $result = '该域名已经被注册'; } else { $result = '该域名可以注册'; } } echo '<b>'.$domain.'</b>:'.$result; ?> |
需要注意的几个问题
支持中文域名查询。
由于域名要在页面显示,因此利用 htmlspecialchars 函数做了特殊 html 代码转换,以防止非法输入,更严格的可以使用正则表达式做检测。
由于万网接口提供的是 gb2312 编码,因此在这里利用进行了 iconv() 函数进行了编码转换,如果不使用 utf-8 编码则无需转换。
如果使用 gb2312 编码,在返回 Ajax 结果是可能需要将显示结果进行 gb2312 到 utf-8 编码的转换。
当万网接口无法返回结果时,启用备用接口进行查询,但只能查询国际域名。
一般处理Ajax 回应时会传送的信息种类有:数据、成功信息、错误信息、失败信息以及处理状态,传递的信息种类并不一致,再加上除了数据之外,通常还希望能传递处理状态,这种情况大部分会选择是以JSON 的方式传递这两个信息,以下是常见的几种格式:
但以执行状态跟操作行为作一个归纳,可以区分以下几种传递结果:
{ code: 1, msg: "OK" } { success: true , result: "data" , errorMsg: "" } { status: 'success' , result: [], errorMsg: "" } //...
但以执行状态跟操作行为作一个归纳,可以区分以下几种回传结果:
数据操作 HTTP Method 成功 错误/失败
读取(Read) GET 数据 错误/失败信息
新增 (Create) POST 成功信息 错误/失败信息
修改 (Update)
删除 (Delete)
从上面的归纳可以看出规律性,接着只要有方法可以传送处理的状态,以及能够区分数据的种类,其实就单纯很多,而HTTP Status Codes 就是用来传递HTTP 的处理状态,如果利用这个方式来传递自定义的处理状态,这样HTTP Content 就可以很单纯传递数据,让数据格式不受限于JSON,还可以使用其他格式(text, xml, html),而且XMLHttpRequest 本身就有处理HTTP Status Codes 的能力,而jQuery.ajax 也有提供error status 的处理,所以可以利用这个来定义状态的处理,在HTTP Status Codes 有几个已经定义状态,很适合用来传递处理状态的信息:
400Bad Request错误的请求适用在表单内容的错误,如必填栏位未填、Email 格式错误
403Forbidden没有权限,被禁止的适用在没有登录或权限不足
500Internal Server Error内部服务器错误适用在程序的错误
jQuery 接收信息的范例
代码如下 | 复制代码 |
$.ajax({ type: "POST" , url: document.location, success: function (data, textStatus, jqXHR) { alert(data); }, error: function (jqXHR, textStatus, errorThrown) { alert(jqXHR.responseText); } }); |
PHP 传递错误信息的范例
代码如下 | 复制代码 |
if (php_sapi_name() == 'cgi' ){ header ( "Status: 400 Bad Request" ); } else { header ( "HTTP/1.0 400 Bad Request" ); } exit ( "储存失败!!" ); |
C# MVC 传递错误信息的范例
代码如下 | 复制代码 |
Response.TrySkipIisCustomErrors = true ; Response.StatusCode = 400; return Content( "储存失败!!" ); |
有时我们为了方便、安全、快速,会把上传的文件单独放一台主机用二级域名访问,但是PHP如何把上传的文件放到另外一台主机呢?这就要跨域跨主机上传了,现在我们用实例来告诉你如何实现。
如何跨网域跨主机跨server上传文件?一般最基本的上传方式是:
1.使用者把文件上传到 web server
2. web server 把上传的文件 利用 move_uploaded_file() 函式,将档案移到指定的文件夹内
但是,有时候我们需要把上传的档案放到另一台专门放文件的 file server,这时候,就无法利用 move_uploaded_file() 去搬移文件了,而需要利用 ftp 去传送文件至 file server,方法很简单...
直接看程式码:
代码如下 | 复制代码 |
$file = $_FILES['file']; $file_tmp = $file['tmp_name']; $file_name = $file['name']; if(is_uploaded_file($file_tmp)){ //确定user有"上传"文件 $file_ext = strrchr($file_name,'.'); //上传文件的副文件名 $file_name_new = date('YmdHis').$file_ext; $host = '127.0.0.1'; $port = '21'; $user = 'admin'; $pass = '123456'; $link = ftp_connect($host,$port); $login = ftp_login($link,$user,$pass); ftp_chdir($link,'filedir'); //切换到要放文件的文件夹 if(ftp_put($link,$file_name_new,$file_tmp,FTP_BINARY)){ $msg = '上传成功'; }else{ $msg = '上传失败'; } }else{ $msg = '上传失败'; } ftp_close($link); echo $msg; |
两地距离我们通常用到最多的就是调用百度地图的api来实现了,但有时我们并不需要这接口了只需要简单的处理一即可,这时可以利用php来实现,具体如下。
目前在做一个交友项目,需要知道两个用户之间的距离。百度了一下,操作如下:
我们最容易获取到用户地理位置的信息就是ip。
我们通过百度api获取用户经纬度,用ip获取经纬度api:
http://developer.baidu.com/map/index.php?title=webapi/ip-api
得到经纬两个用户经纬度之后就可以计算两用户之间的距离了。计算如下:
代码如下 | 复制代码 |
/** * @desc 根据两点间的经纬度计算距离 * @param float $lat 纬度值 * @param float $lng 经度值 */ function getDistance($lat1, $lng1, $lat2, $lng2) { $earthRadius = 6367000; //approximate radius of earth in meters $lat1 = ($lat1 * pi() ) / 180; $lng1 = ($lng1 * pi() ) / 180; $lat2 = ($lat2 * pi() ) / 180; $lng2 = ($lng2 * pi() ) / 180; $calcLongitude = $lng2 - $lng1; $calcLatitude = $lat2 - $lat1; $stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2); $stepTwo = 2 * asin(min(1, sqrt($stepOne))); $calculatedDistance = $earthRadius * $stepTwo; return round($calculatedDistance); } |
代码未经测试。测试后再更新本篇文章告知结果。