PHP curl可以从服务器端模拟一个http请求,例如抓取网页、模拟登陆等。根据选项设置,可以在curl_exec的返回结果中获取到响应头和body,但这没有响应的状态吗。想要获取状态吗,需要在执行curl_exec后再通过curl_getinfo来获取。例如:
$ch = curl_init ();
curl_setopt($ch, CURLOPT_URL, 'http://www.google.com.hk');
curl_setopt($ch, CURLOPT_TIMEOUT, 200);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_NOBODY, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, FALSE);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_exec($ch);
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
另外curl_getinfo还可以获取很多其他信息,具体请参考:http://php.net/manual/en/function.curl-getinfo.php 。
PHPMailer是别人封装好的一个发送邮件的库,用起来很方便。其支持mail、sendmail和smtp的方式。官网地址:http://phpmailer.worxware.com/,可以到https://code.google.com/a/apache-extras.org/p/phpmailer/downloads/list去下载最新版本的。下面通过gmail smtp发送邮件为例来说明smtp使用方法。
function sendMail($subject, $body, $to, $ccs = array()) {
require_once './class.phpmailer.php';
$mail = new PHPMailer();
//设定邮件编码,默认ISO-8859-1,也可以直接去源代码中修改
$mail->CharSet = 'UTF-8';
// 使用smtp的方式发送
$mail->IsSMTP();
//smtp服务器需要认证
$mail->SMTPAuth = TRUE;
//安全协议 gmail 是采用ssl的
$mail->SMTPSecure = "ssl";
//smtp服务器
$mail->Host = 'smtp.gmail.com';
//smtp服务器端口,普通是25
$mail->Port = 465;
//smtp 认证用户名和密码
$mail->Username = 'yourgmailaccount@gmail.com';
$mail->Password = "yourpassword";
//发件人地址和名字,名字可以省略
$mail->SetFrom('yourgmailaccount@gmail.com', 'display name');
// 邮件标题
$mail->Subject = $subject;
// 邮件内容,支持HTML格式
$mail->MsgHTML($body);
// 收件人地址
$mail->AddAddress($to);
// 抄送人
foreach ($ccs as $cc) {
$mail->AddCC($cc);
}
if(!$mail->Send()) {
echo "error info:" . $mail->ErrorInfo;
}
}
默认配置下,php输出是先到输出缓冲区(output_buffering),只要数据还没有真正发送到浏览器(严格来说是tcp buffer),那么还是有机会清空先前的缓冲区里面的数据,使用内置的ob_clean函数即可。注意:ob_clean 只是清空当前缓冲区的数据,如果先前输出的数据大于缓冲区,那么一部分数据已经发送,发送的这部分数据是无法清空的。另外如果禁用php输出缓冲区,那么ob_clean起不到任何效果的。
php 中遍历一个array时可以使用for或foreach,foreach的语法为:foreach ($arr as $k => $v)。遍历数组,把index赋给$k,数组的值赋给$v,那么此处的赋值是传值还是传引用呢。先看下面的例子:
$arr = array(
array('id' => 1, 'name' => 'name1'),
array('id' => 2, 'name' => 'name2'),
);
foreach ($arr as $obj) {
$obj['id'] = $obj['id'];
$obj['name'] = $obj['name'] . '-modify';
}
print_r($arr); //输出的结果
Array(
[0] => Array (
[id] => 1
[name] => name1
)
[1] => Array(
[id] => 2
[name] => name2
)
)
观察可以发现在foreach循环中对$arr操作并没有影响到$arr的元素,所以这里的赋值是传值而不是传引用。那如果需要修改$arr中元素的值该怎么办呢?可以在变量前面加一个”&”符号,例如:
foreach ($arr as &$obj) {
$obj['id'] = $obj['id'];
$obj['name'] = $obj['name'] . '-modify';
}
再看另外一个例子,array里面存放的是object,
$arr = array(
(object)(array('id' => 1, 'name' => 'name1')),
(object)(array('id' => 2, 'name' => 'name2')),
);
foreach ($arr as $obj) {
$obj->name = $obj->name . '-modify';
}
print_r($arr); //输出的结果
Array
(
[0] => stdClass Object
(
[id] => 1
[name] => name1-modify
)
[1] => stdClass Object
(
[id] => 2
[name] => name2-modify
)
)
此时可以看到原始数组中的object对象已经修改了,所以这里的赋值又是传引用而不是传值
综合上述,得出的结论:如果数组里面存放的是普通类型的元素就是采用传值的方式,存放对象类型元素采用的方式为传地址。
今天遇到一个问题,调用对方接口,按照接口说明,返回为数字0,1,2等等,其中0标示成功,其他表示不同的错误代码。程序通过 if ($ret == 0) 进行判断,开始程序是好的,今天出现问题了,由于对方接口修改,直接返回字母字符串作为错误信息提示,然后我这边就悲剧了,上述判断永远都为TRUE。
原因在于php是弱类型语言,所以可以对两个不同类型的变量进行比较操作,但最终进行比较前,php会把某一方转换称另一方一样的类型,这点很重要。如果是字符串和数字进行比较,那么php会把字符串强制转换称数字,对于纯字母的字符串,那么转换后就是0了,所以if ($ret == 0) 成了。
附PHP官方介绍:http://php.net/manual/zh/language.operators.comparison.php
php提供内置函数is_dir来检查传入的路径参数是否为目录,如果是目录则返回true。也就是说传入的参数是文件或者不存在都为false,因此当前参数不能说不是文件夹就是文件。另外需要注意的是参数支持相对路径和绝对路径。如果是绝对路径,那么就直接判断其是否为文件夹,但是相对路径就不一样了,参数指定的路径是相对当前工作目录的。例如传入的参数为“test”,那么他实际检查的是脚本所在的路径下是否有名为test的目录。由于当前工作目录不是很明晰的,为了避免犯错,最好是使用绝对路径。
HTTP_HOST是客户端请求时封装在HTTP request header中,而SERVER_NAME是web服务器中配置文件定义的。这两个值有时是相同的,但不能说他们就是一样的,因为这个起决于服务器的WEB server 配置。例如使用nginx作为为web 服务器,配置如下:
server {
listen 80;
server_name *.netingcn.com;
....
}
假设访问 http://www.netingcn.com/test.php,这是使用$_SERVER['HTTP_HOST']得到的结果为:www.netingcn.com,而$_SERVER['SERVER_NAME']则是*.netingcn.com
在已知参数名的情况下,获取参数值,使用正则表达式能很容易做到。js的实现方法如下:
function getValue(url, name) {
var reg = new RegExp('(\\?|&)' + name + '=([^&?]*)', 'i');
var arr = url.match(reg);
if (arr) {
return arr[2];
}
return null;
}
如果想获取所有的参数名和其对应的值,同样也可以使用正则表达式的方法,js实现方法:
function getKeyValue(url) {
var result = {};
var reg = new RegExp('([\\?|&])(.+?)=([^&?]*)', 'ig');
var arr = reg.exec(url);
while (arr) {
result[arr[2]] = arr[3];
arr = reg.exec(url);
}
return result;
}
注意在js中用于匹配的还有一个叫match,match是字符串的方法,而exec是RegExp对象的方法。使用字符串的match方法,同时正则表达式指定为全局匹配,那么正则表达式中的分组将没有用,返回的结果是所有匹配正则表达式的子字符串。exec方法没有使用全局匹配标志时,返回的时间第一个匹配的子字符,如果使用了全局匹配标志,第一次执行从头开始匹配符号的字符串,再次调用,就从上一次匹配结果后开始匹配。
下面提供php的实现方法:
function getKeyValue($url) {
$result = array();
$mr = preg_match_all('/(\?|&)(.+?)=([^&?]*)/i', $url, $matchs);
if ($mr !== FALSE) {
for ($i = 0; $i < $mr; $i++) {
$result[$matchs[2][$i]] = $matchs[3][$i];
}
}
return $result;
}
php的preg_match_all方法把匹配的结果存放在第三个指定的参数中,是一个二维数组。第一维度是分组信息的数组,即第一个数组存放的是所有匹配的完整字符串,第二个数组存放的是第一个()对应的值得,第二维度是分组的值。
php设置cookie可以使用setcookie函数或header函数。使用header方式时候需要注意字符串的顺序,如果顺序不对,可能会出现意想不到的问题。正确的顺序为
name=value; [expires=date; [path=path]; [domain=domainname]; [secure]]
例如:在netingcn.com下设置一个名为cookiename,值为cookieValue,过期时间为一年的cookie
header("Set-Cookie: cookiename=cookieValue; expires=" . gmstrftime("%A, %d-%b-%Y %H:%M:%S GMT", time() + (86400 * 365)) . '; path=/; domain=netingcn.com');
如果把domain、path放到expires前面,在firefox通过httpfox查看时,发现过期时间是“End Of Session”,但退出浏览器后重新打开,发现该cookie还是存在的,chrome显示的也是类似。虽说浏览器最终还是设置了一个一年过期的cookie,不过domain信息为当前页面的完整域,不是设置的顶级域。所以一定要注意顺序。
另外,如果用header的方式设置多个cookie,需要设置header的第二个参数为FALSE,下面是header函数的声明:
void header ( string string [, bool replace [, int http_response_code]] )
第二个参数replace 指明是替换掉前一条类似的标头还是增加一条相同类型的标头。默认为替换,但如果将其设为 FALSE 则可以强制发送多个同类标头。
通过源代码编译安装php的memcached扩展,一路顺利,最终生成了memcached.so,同时在php.ini中添加了extension=memcached.so,使用命令php -v查看时提示PHP Warning: PHP Startup: Unable to load dynamic library。以前在多台机器上安装扩展都是这么设置就可以,但是这台机器上就不行,后来发现是此机器的php.ini默认配置中使用了extension_dir,解决办法就是注释掉php.ini中的extension_dir配置。
关于memcached扩展的安装可以参看这里:http://www.netingcn.com/php-memcached.html