档案

Posts Tagged ‘PHP’

PHP排序算法类

2011-08-22 留下评论

sort.php

<?php
/**
* 四种排序算法设计(PHP)
*
* 1) 插入排序(Insertion Sort)的基本思想是:
每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子文件中的适当位置,直到全部记录插入完成为止。
2) 选择排序(Selection Sort)的基本思想是:
每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子文件的最后,直到全部记录排序完毕。
3) 冒泡排序的基本思想是:
两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止。
4) 快速排序实质上和冒泡排序一样,都是属于交换排序的一种应用。所以基本思想和上面的冒泡排序是一样的。
*
* @author quanshuidingdang
*/
class Sort {
private $arr = array();
private $sort = ‘insert’;
private $marker = ‘_sort’;

private $debug = TRUE;

/**
* 构造函数
*
* @param array 例如: $config = array (
‘arr’ => array(22,3,41,18) , //需要排序的数组值
‘sort’ => ‘insert’, //可能值: insert, select, bubble, quick
‘debug’ => TRUE //可能值: TRUE, FALSE
)
*/
public function __construct($config = array()) {
if ( count($config) > 0) {
$this->_init($config);
}
}

/**
* 获取排序结果
*/
public function display() {
return $this->arr;
}

/**
* 初始化
*
* @param array
* @return bool
*/
private function _init($config = array()) {
//参数判断
if ( !is_array($config) OR count($config) == 0) {
if ($this->debug === TRUE) {
$this->_log(“sort_init_param_invaild”);
}
return FALSE;
}

//初始化成员变量
foreach ($config as $key => $val) {
if ( isset($this->$key)) {
$this->$key = $val;
}
}

//调用相应的成员方法完成排序
$method = $this->sort . $this->marker;
if ( ! method_exists($this, $method)) {
if ($this->debug === TRUE) {
$this->_log(“sort_method_invaild”);
}
return FALSE;
}

if ( FALSE === ($this->arr = $this->$method($this->arr)))
return FALSE;
return TRUE;
}

/**
* 插入排序
*
* @param array
* @return bool
*/
private function insert_sort($arr) {
//参数判断
if ( ! is_array($arr) OR count($arr) == 0) {
if ($this->debug === TRUE) {
$this->_log(“sort_array(insert)_invaild”);
}
return FALSE;
}

//具体实现
$count = count($arr);
for ($i = 1; $i < $count; $i++) {
$tmp = $arr[$i];
for($j = $i-1; $j >= 0; $j–) {
if($arr[$j] > $tmp) {
$arr[$j+1] = $arr[$j];
$arr[$j] = $tmp;
}
}
}
return $arr;
}

/**
* 选择排序
*
* @param array
* @return bool
*/
private function select_sort($arr) {
//参数判断
if ( ! is_array($arr) OR count($arr) == 0) {
if ($this->debug === TRUE) {
$this->_log(“sort_array(select)_invaild”);
}
return FALSE;
}

//具体实现
$count = count($arr);
for ($i = 0; $i < $count-1; $i++) {
$min = $i;
for ($j = $i+1; $j < $count; $j++) {
if ($arr[$min] > $arr[$j]) $min = $j;
}
if ($min != $i) {
$tmp = $arr[$min];
$arr[$min] = $arr[$i];
$arr[$i] = $tmp;
}
}
return $arr;
}

/**
* 冒泡排序
*
* @param array
* @return bool
*/
private function bubble_sort($arr) {
//参数判断
if ( ! is_array($arr) OR count($arr) == 0) {
if ($this->debug === TRUE) {
$this->_log(“sort_array(bubble)_invaild”);
}
return FALSE;
}

//具体实现
$count = count($arr);
for ($i = 0; $i < $count; $i++) {
for ($j = $count-1; $j > $i; $j–) {
if ($arr[$j] < $arr[$j-1]) {
$tmp = $arr[$j];
$arr[$j] = $arr[$j-1];
$arr[$j-1] = $tmp;
}
}
}
return $arr;
}

/**
* 快速排序
*
* @param array
* @return bool
*/
private function quick_sort($arr) {
//具体实现
if (count($arr) <= 1) return $arr;
$key = $arr[0];
$left_arr = array();
$right_arr = array();
for ($i = 1; $i < count($arr); $i++){
if ($arr[$i] <= $key)
$left_arr[] = $arr[$i];
else
$right_arr[] = $arr[$i];
}
$left_arr = $this->quick_sort($left_arr);
$right_arr = $this->quick_sort($right_arr);

return array_merge($left_arr, array($key), $right_arr);
}

/**
* 日志记录
*/
private function _log($msg) {
$msg = ‘date[‘ . date(‘Y-m-d H:i:s’) . ‘] ‘ . $msg . ‘\n’;
return @file_put_contents(‘sort_err.log’, $msg, FILE_APPEND);
}
}

/*End of file sort.php*/
/*Location htdocs/sort.php */

sort_demo.php

<?php

require_once(‘sort.php’);

$config = array (
‘arr’ => array(23, 22, 41, 18, 20, 12, 200303,2200,1192) , //需要排序的数组值
‘sort’ => ‘select’, //可能值: insert, select, bubble, quick
‘debug’ => TRUE //可能值: TRUE, FALSE
);

$sort = new Sort($config);
//var_dump($config[‘arr’]);
var_dump($sort->display());

/*End of php*/

分类:PHP 标签:,

PHP文件上传代码

2011-08-21 留下评论

<?php
// basic php file upload example
// http://php.snippetdb.com

if ($_POST[‘upload’]){ //process uploaded file
$uploadDir = ‘/domains/path-com/path/www_root/_crop/full-’; //file upload path
$uploadFile = $uploadDir . $_FILES[‘uploadfile’][‘name’];
echo “<pre>”;
if (move_uploaded_file($_FILES[‘uploadfile’][‘tmp_name’], $uploadFile) )
{
echo “File is valid, and was successfully uploaded. “;
echo “Here’s some more debugging info:\n”;
}
else
{
echo “ERROR! Here’s some debugging info:\n”;
echo “remember to check valid path, max filesize\n”;
echo “$-POST-upload: “.$_POST[‘upload’];
}
echo “</pre>”;

} else { //display upload form
?>
<form enctype=”multipart/form-data” action=”<?php echo $_SERVER[‘SCRIPT_NAME’]?>” method=”post”>
<input type=”hidden” name=”MAX_FILE_SIZE” value=”1111024000″>
<br>
<input name=”uploadfile” type=”file”>
<br>
<input name= “upload” type=”submit” value=”Upload File”>
</form>
<?php
}
?>

分类:PHP 标签:,

PHP网站备份程序

2011-08-21 留下评论

<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=gb2312″>
<title>网站程序备份</title>
</head>
<body>
<form name=”myform” method=”post” action=””>
<?php
error_reporting(E_ALL & ~E_NOTICE);
ini_set(‘memory_limit’, ‘2048M’);
echo “选择要压缩的文件或目录:<br>”;
$fdir = opendir(‘./’);
while($file=readdir($fdir))
{
if($file==’.’|| $file==’..’)
continue;
echo “<input name=’dfile[]’ type=’checkbox’ value=’$file’ “.($file==basename(__FILE__)?””:”checked”).”> “;

if(is_file($file))
{
echo “<font face=\”wingdings\” size=\”5\”>2</font>&nbsp;&nbsp;$file<br>”;
}
else
{
echo “<font face=\”wingdings\” size=\”5\”>0</font>&nbsp;$file<br>”;
}
}
?>
<br>
包含下列文件类型:
<input name=”file_type” type=”text” id=”file_type” value=”” size=”50″>
<font color=”red”>
(文件类型用”|”隔开,默认空则包含任意文件,例:如果需要打包php和jpg文件,则输入”php|jpg”)
</font>
<br>
压缩文件保存到目录:
<input name=”todir” type=”text” id=”todir” value=”__dwb2011__” size=”15″>
<font color=”red”>
(留空为本目录,必须有写入权限)
</font>
<br>
压缩文件名称:
<input name=”zipname” type=”text” id=”zipname” value=”dwb2011.zip” size=”15″>
<font color=”red”>
(.zip)
</font>
<br>
<br>
<input name=”myaction” type=”hidden” id=”myaction” value=”dozip”>
<input type=’button’ value=’反选’ onclick=’selrev();’>
<input type=”submit” name=”Submit” value=” 开始压缩 “>
<script language=’javascript’>
function selrev()
{
with(document.myform)
{
for(i=0;i<elements.length;i++)
{
thiselm = elements[i];
if(thiselm.name.match(/dfile\[]/))
thiselm.checked = !thiselm.checked;
}
}
}
</script>

<?php
error_reporting(E_ALL & ~E_NOTICE);
set_time_limit(0);
class PHPzip
{
var $file_count = 0 ;
var $datastr_len   = 0;
var $dirstr_len = 0;
var $filedata = ”; //该变量只被类外部程序访问
var $gzfilename;
var $fp;
var $dirstr=”;
var $filefilters = array();

function SetFileFilter($filetype)
{
$this->filefilters = explode(‘|’,$filetype);
}

//返回文件的修改时间格式.
//只为本类内部函数调用.
function unix2DosTime($unixtime = 0)
{
$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
if ($timearray[‘year’] < 1980)
{
$timearray[‘year’]    = 1980;
$timearray[‘mon’]     = 1;
$timearray[‘mday’]    = 1;
$timearray[‘hours’]   = 0;
$timearray[‘minutes’] = 0;
$timearray[‘seconds’] = 0;
}
return (($timearray[‘year’] – 1980) << 25) | ($timearray[‘mon’] << 21) | ($timearray[‘mday’] << 16) |  ($timearray[‘hours’] << 11) | ($timearray[‘minutes’] << 5) | ($timearray[‘seconds’] >> 1);
}

//初始化文件,建立文件目录,
//并返回文件的写入权限.
function startfile($path = ‘dodo.zip’)
{
$this->gzfilename=$path;
$mypathdir=array();

do
{
$mypathdir[] = $path = dirname($path);
}    while($path != ‘.’);

@end($mypathdir);

do
{
$path = @current($mypathdir);
@mkdir($path);
}    while(@prev($mypathdir));

if($this->fp=@fopen($this->gzfilename,”w”))
{
return true;
}

return false;
}

//添加一个文件到 zip 压缩包中.
function addfile($data, $name)
{
$name = str_replace(‘\’, ‘/’, $name);
if(strrchr($name,’/’)==’/’)
return $this->adddir($name);
if(!empty($this->filefilters))
{
if (!in_array(end(explode(“.”,$name)), $this->filefilters))
{
return;
}
}
$dtime = dechex($this->unix2DosTime());
$hexdtime = ‘\x’ . $dtime[6] . $dtime[7] . ‘\x’ . $dtime[4] . $dtime[5] . ‘\x’ . $dtime[2] . $dtime[3] . ‘\x’ . $dtime[0] . $dtime[1];
eval(‘$hexdtime = “‘ . $hexdtime . ‘”;’);

$unc_len = strlen($data);
$crc     = crc32($data);
$zdata   = gzcompress($data);
$c_len   = strlen($zdata);
$zdata   = substr(substr($zdata, 0, strlen($zdata) – 4), 2);

//新添文件内容格式化:
$datastr  = “\x50\x4b\x03\x04”;
$datastr .= “\x14\x00”;            // ver needed to extract
$datastr .= “\x00\x00”;            // gen purpose bit flag
$datastr .= “\x08\x00”;            // compression method
$datastr .= $hexdtime;             // last mod time and date
$datastr .= pack(‘V’, $crc);             // crc32
$datastr .= pack(‘V’, $c_len);           // compressed filesize
$datastr .= pack(‘V’, $unc_len);         // uncompressed filesize
$datastr .= pack(‘v’, strlen($name));    // length of filename
$datastr .= pack(‘v’, 0);                // extra field length
$datastr .= $name;
$datastr .= $zdata;
$datastr .= pack(‘V’, $crc);                 // crc32
$datastr .= pack(‘V’, $c_len);               // compressed filesize
$datastr .= pack(‘V’, $unc_len);             // uncompressed filesize
fwrite($this->fp,$datastr); //写入新的文件内容
$my_datastr_len = strlen($datastr);
unset($datastr);

//新添文件目录信息
$dirstr  = “\x50\x4b\x01\x02”;
$dirstr .= “\x00\x00”;                 // version made by
$dirstr .= “\x14\x00”;                 // version needed to extract
$dirstr .= “\x00\x00”;                 // gen purpose bit flag
$dirstr .= “\x08\x00”;                 // compression method
$dirstr .= $hexdtime;                  // last mod time & date
$dirstr .= pack(‘V’, $crc);            // crc32
$dirstr .= pack(‘V’, $c_len);          // compressed filesize
$dirstr .= pack(‘V’, $unc_len);        // uncompressed filesize
$dirstr .= pack(‘v’, strlen($name) );  // length of filename
$dirstr .= pack(‘v’, 0 );              // extra field length
$dirstr .= pack(‘v’, 0 );              // file comment length
$dirstr .= pack(‘v’, 0 );              // disk number start
$dirstr .= pack(‘v’, 0 );              // internal file attributes
$dirstr .= pack(‘V’, 32 );             // external file attributes – ‘archive’ bit set
$dirstr .= pack(‘V’,$this->datastr_len ); // relative offset of local header
$dirstr .= $name;
$this->dirstr .= $dirstr; //目录信息
$this -> file_count ++;
$this -> dirstr_len += strlen($dirstr);
$this -> datastr_len += $my_datastr_len;
}

function adddir($name)
{
$name = str_replace(“\\”, “/”, $name);
$datastr = “\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00”;
$datastr .= pack(“V”,0).pack(“V”,0).pack(“V”,0).pack(“v”, strlen($name) );
$datastr .= pack(“v”, 0 ).$name.pack(“V”, 0).pack(“V”, 0).pack(“V”, 0);
fwrite($this->fp,$datastr); //写入新的文件内容
$my_datastr_len = strlen($datastr);
unset($datastr);
$dirstr = “\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00”;
$dirstr .= pack(“V”,0).pack(“V”,0).pack(“V”,0).pack(“v”, strlen($name) );
$dirstr .= pack(“v”, 0 ).pack(“v”, 0 ).pack(“v”, 0 ).pack(“v”, 0 );
$dirstr .= pack(“V”, 16 ).pack(“V”,$this->datastr_len).$name;
$this->dirstr .= $dirstr; //目录信息
$this -> file_count ++;
$this -> dirstr_len += strlen($dirstr);
$this -> datastr_len += $my_datastr_len;
}

function createfile()
{
//压缩包结束信息,包括文件总数,目录信息读取指针位置等信息
$endstr = “\x50\x4b\x05\x06\x00\x00\x00\x00” .
pack(‘v’, $this -> file_count) .
pack(‘v’, $this -> file_count) .
pack(‘V’, $this -> dirstr_len) .
pack(‘V’, $this -> datastr_len) .
“\x00\x00”;
fwrite($this->fp,$this->dirstr.$endstr);
fclose($this->fp);
}
}

if(!trim($_REQUEST[zipname]))
$_REQUEST[zipname] = “dodozip.zip”;
else
$_REQUEST[zipname] = trim($_REQUEST[zipname]);

if(!strrchr(strtolower($_REQUEST[zipname]),’.’)==’.zip’)
$_REQUEST[zipname] .= “.zip”;

$_REQUEST[todir] = str_replace(‘\’,’/’,trim($_REQUEST[todir]));

if(!strrchr(strtolower($_REQUEST[todir]),’/’)==’/’)
$_REQUEST[todir] .= “/”;

if($_REQUEST[todir]==”/”)
$_REQUEST[todir] = “./”;

function listfiles($dir=”.”)
{
global $dodozip;
$sub_file_num = 0;
if(is_file(“$dir”))
{
if(realpath($dodozip ->gzfilename)!=realpath(“$dir”))
{
$dodozip -> addfile(implode(”,file(“$dir”)),”$dir”);
return 1;
}
return 0;
}

$handle=opendir(“$dir”);
while ($file = readdir($handle))
{
if($file==”.”||$file==”..”)
continue;

if(is_dir(“$dir/$file”))
{
$sub_file_num += listfiles(“$dir/$file”);
}
else
{
if(realpath($dodozip ->gzfilename)!=realpath(“$dir/$file”))
{
$dodozip -> addfile(implode(”,file(“$dir/$file”)),”$dir/$file”);
$sub_file_num ++;
}
}
}

closedir($handle);

if(!$sub_file_num)
$dodozip -> addfile(“”,”$dir/”);

return $sub_file_num;
}

function num_bitunit($num)
{
$bitunit=array(‘ B’,’ KB’,’ MB’,’ GB’);
for($key=0;$key<count($bitunit);$key++)
{
if($num>=pow(2,10*$key)-1)
{ //1023B 会显示为 1KB
$num_bitunit_str=(ceil($num/pow(2,10*$key)*100)/100).” $bitunit[$key]”;
}
}
return $num_bitunit_str;
}

if(is_array($_REQUEST[dfile]))
{
$dodozip = new PHPzip;
if($_REQUEST[“file_type”] != NULL)
$dodozip -> SetFileFilter($_REQUEST[“file_type”]);
if($dodozip -> startfile(“$_REQUEST[todir]$_REQUEST[zipname]”))
{
echo “正在添加压缩文件…<br><br>”;
$filenum = 0;
foreach($_REQUEST[dfile] as $file)
{
if(is_file($file))
{
if(!empty($dodozip -> filefilters))
if (!in_array(end(explode(“.”,$file)), $dodozip -> filefilters))
continue;
echo “<font face=\”wingdings\” size=\”5\”>2</font>&nbsp;&nbsp;$file<br>”;
}
else
{
echo “<font face=\”wingdings\” size=\”5\”>0</font>&nbsp;$file<br>”;
}
$filenum += listfiles($file);
}
$dodozip -> createfile();

echo “<br>压缩完成,共添加 $filenum 个文件.<br><a href=’$_REQUEST[todir]$_REQUEST[zipname]’ _fcksavedurl=’$_REQUEST[todir]$_REQUEST[zipname]’>$_REQUEST[todir]$_REQUEST[zipname] (“.num_bitunit(filesize(“$_REQUEST[todir]$_REQUEST[zipname]”)).”)</a>”;
}
else
{
echo “$_REQUEST[todir]$_REQUEST[zipname] 不能写入,请检查路径或权限是否正确.<br>”;
}
}
?>
</form>
</body>
</html>

分类:PHP 标签:, ,

PHP 在线输出PDF文件

2011-07-27 留下评论

<?php

if(!function_exists(‘read_pdf’)) {

function read_pdf($file) {

if(strtolower(substr(strrchr($file,’.’),1)) != ‘pdf’) {

echo ‘文件格式不对.’;

return;

}

if(!file_exists($file)) {

echo ‘文件不存在’;

return;

}

header(‘Content-type: application/pdf’);

header(‘filename=’.$file);

readfile($file);

}

}

/*End of PHP*/

分类:PHP 标签:,

php 利用cookie在客户端记录浏览历史

2011-07-27 留下评论

/* 记录浏览历史 */
if (!empty($_COOKIE[‘history’]))
{
if(stripos($_COOKIE[‘history’].’,’,$goods_id.’,’)===false){

$history = explode(‘,’, $_COOKIE[‘history’]);

array_unshift($history, $goods_id); //入栈

while (count($history) > 5) //大于容器容量
{
array_pop($history); //出栈
}

setcookie(‘history’, implode(‘,’, $history), gmtime() + 3600 * 24 * 30);
}
}
else
{
setcookie(‘history’, $goods_id, gmtime() + 3600 * 24 * 30);
}

分类:PHP 标签:,

PHP 读取 HTTP_RANGE 进行HTTP断点续传的函数

2011-07-27 留下评论

<?php

/**

* PHP-HTTP断点续传实现

* @param string $path: 文件所在路径

* @param string $file: 文件名

* @return void

*/

function download($path,$file) {

$real = $path.’/’.$file;

if(!file_exists($real)) {

return false;

}

$size = filesize($real);

$size2 = $size-1;

$range = 0;

if(isset($_SERVER[‘HTTP_RANGE’])) {

header(‘HTTP /1.1 206 Partial Content’);

$range = str_replace(‘=’,’-‘,$_SERVER[‘HTTP_RANGE’]);

$range = explode(‘-‘,$range);

$range = trim($range[1]);

header(‘Content-Length:’.$size);

header(‘Content-Range: bytes ‘.$range.’-‘.$size2.’/’.$size);

} else {

header(‘Content-Length:’.$size);

header(‘Content-Range: bytes 0-‘.$size2.’/’.$size);

}

header(‘Accenpt-Ranges: bytes’);

header(‘application/octet-stream’);

header(“Cache-control: public”);

header(“Pragma: public”);

//解决在IE中下载时中文乱码问题

$ua = $_SERVER[‘HTTP_USER_AGENT’];

if(preg_match(‘/MSIE/’,$ua)) {

$ie_filename = str_replace(‘+’,’%20′,urlencode($file));

header(‘Content-Dispositon:attachment; filename=’.$ie_filename);

} else {

header(‘Content-Dispositon:attachment; filename=’.$file);

}

$fp = fopen($real,’rb+’);

fseek($fp,$range);

while(!feof($fp)) {
set_time_limit(0);

print(fread($fp,1024));

flush();

ob_flush();

}

fclose($fp);

}

/*End of PHP*/

分类:PHP 标签:, , ,

使用 Firebug 和 FirePHP 调试 PHP

2011-07-26 留下评论

如果你和我一样,你会在开发网页项目时候完全无法离开 FireBug。这个小巧的”臭虫”是一个神奇而有用的HTML/CSS/JavaScript/Ajax调试器。但是你也许不知道这个还可以用来调试PHP,没错,它可以,感谢一款名为FirePHP的FireFox插件。

通过一个小小的服务端库,和这款在Firebug上的插件,你的PHP脚本能够发送调试信息到浏览器,轻易的通过HTTP相应头编码。一旦你设置,你可以在Fiirebug的控制台获得PHP脚本警告和错误,就感觉像直接调试JavaScript一样。

使用这个工具,首先你需要安装FirePHP插件。这个插件需要你已经安装FireBug。装好FirePHP之后,重新打开Firebug面板时候,你会看到新加了一个蓝色的臭虫图标。点击这个图标会出现一个开启或者关闭FirePHP的菜单。

当然,这时候我们还无法做任何事,你还需要安装FirePHP的服务端,点击这里下载。这是一个独立的版本,你可以手动下载或者使用PEAR。装后之后,你可是轻松的将这个库加入你的代码。它被设计了很多版本来整合入多个框架或者管理系统,比如 WP-FirePHP plugin for WordPressJFirePHP plugin for Joomla。暂时不管这些,我们将把精力集中在独立的功能上。

一旦你在你服务器上部署了FirePHP库,你还需要在你的代码中加入以下的代码:

require_once('FirePHPCore/fb.php');

这是因为FirePHP通过HTTP头发送记录的数据,你需要缓存你的代码产生的输出,从而来响应头信息从这里获取代码生成的内容。这个可以通过在代码头部的ob_start来实现。

ob_start();

当这些步骤完成后,你可以开始使用FirePHP了。你需要做的只是调用fb函数在任何你想要记录的地方。同时你也可以使用一个可选的标签和常量去定义预定义信息,一个错误,一个警告,或者一条信息。

1
2
3
4
5
6
$var = array('a'=>'pizza', 'b'=>'cookies', 'c'=>'celery');
fb($var);
fb($var, "An array");
fb($var, FirePHP::WARN);
fb($var, FirePHP::INFO);
fb($var, 'An array with an Error type', FirePHP::ERROR);

这些代码将在Firebug控制台输出如下所示

你也可以使用FirePHP来跟踪你程序的执行情况:通过使用FirePHP::TRACE常量,你可以在 fb被调用的地方查看行数、类名和方法名

1
2
3
4
5
6
7
function hello() {
    fb('Hello World!', FirePHP::TRACE);
}
function greet() {
    hello();
}
greet();

产生的输出如下

这个跟踪功能可以完美的调试更复杂的代码,让你精确的知道你的方法是在哪里被调用的。
当然,别忘了你需要在你代码发布之前移除你的调试语句。
这里还有很多FirePHP的内容没有涉及到。我只是向你简单展示一下FirePHP的API,还有很多高级的面向对象API。

分类:PHP 标签:, ,

优化 PHP 代码的 40 条建议

2011-07-26 留下评论

1.如果一个方法可静态化,就对它做静态声明。速率可提升至 4 倍。

2.echo 比 print 快。

3.使用 echo 的多重参数(译注:指用逗号而不是句点)代替字符串连接。

4.在执行 for 循环之前确定最大循环数,不要每循环一次都计算最大值。

5.注销那些不用的变量尤其是大数组,以便释放内存。

6.尽量避免使用 __get,__set,__autoload。

7.require_once() 代价昂贵。

8.在包含文件时使用完整路径,解析操作系统路径所需的时间会更少。

9.如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用 $_SERVER[‘REQUEST_TIME’] 要好于 time()。

10.函数代替正则表达式完成相同功能。

11.str_replace 函数比 preg_replace 函数快,但 strtr 函数的效率是 str_replace 函数的四倍。

12.如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码,使得每次传递参数是一个字符,而不是只写一行代码接受数组作为查询和替换的参数。

13.使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。

14.用@屏蔽错误消息的做法非常低效。

15.打开 apache的 mod_deflate 模块。

16.数据库连接当使用完毕时应关掉。

17.$row[‘id’] 的效率是 $row[id] 的7倍。

18.错误消息代价昂贵。

19.尽量不要在 for 循环中使用函数,比如 for ($x=0; $x < count($array); $x) 每循环一次都会调用count() 函数。

20.在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。

21.递增一个全局变量要比递增一个局部变量慢2倍。

22.递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢 3 倍。

23.递增一个未预定义的局部变量要比递增一个预定义的局部变量慢 9 至 10 倍。

24.仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。PHP 大概会检查看是否存在全局变量。

25.方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添加了 10 个方法,但性能上没有变化。

26.派生类中的方法运行起来要快于在基类中定义的同样的方法。

27.调用带有一个参数的空函数,其花费的时间相当于执行 7 至 8 次的局部变量递增操作。类似的方法调用所花费的时间接近于 15 次的局部变量递增操作。

28.用单引号代替双引号来包含字符串,这样做会更快一些。因为 PHP 会在双引号包围的字符串中搜寻变量,单引号则不会。当然,只有当你不需要在字符串中包含变量时才可以这么做。

29.输出多个字符串时,用逗号代替句点来分隔字符串,速度更快。注意:只有 echo 能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中说 echo 是语言结构,不是真正的函数,故把函数加上了双引号)。

30.Apache 解析一个 PHP 脚本的时间要比解析一个静态 HTML 页面慢 2 至 10 倍。尽量多用静态 HTML 页面,少用脚本。

31.除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升 25% 至 100% 的性能,以免除编译开销。

32.尽量做缓存,可使用 memcached。memcached 是一款高性能的内存对象缓存系统,可用来加速动态 Web 应用程序,减轻数据库负载。对运算码 (OP code) 的缓存很有用,使得脚本不必为每个请求做重新编译。

33.当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用 strlen() 函数。此函数执行起来相当快,因为它不做任何计算,只返回在 zval 结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度。但是,由于 strlen() 是函数,多多少少会有些慢,因为函数调用会经过诸多步骤,如字母小写化(译注:指函数名小写化,PHP 不区分函数名大小写)、哈希查找,会跟随被调用的函数一起执行。在某些情况下,你可以使用 isset() 技巧加速执行你的代码。

(举例如下)

if (strlen($foo) < 5) { echo "Foo is too short"$$ }

(与下面的技巧做比较)

if (!isset($foo{5})) { echo "Foo is too short"$$ }

调用 isset() 恰巧比 strlen() 快,因为与后者不同的是,isset() 作为一种语言结构,意味着它的执行不需要函数查找和字母小写化。也就是说,实际上在检验字符串长度的顶层代码中你没有花太多开销。

34.当执行变量 $i 的递增或递减时,$i++ 会比 ++$i 慢一些。这种差异是 PHP 特有的,并不适用于其他语言,所以请不要修改你的 C 或 Java 代码并指望它们能立即变快,没用的。++$i 更快是因为它只需要 3 条指令 (opcodes),$i++ 则需要 4 条指令。后置递增实际上会产生一个临时变量,这个临时变量随后被递增。而前置递增直接在原值上递增。这是最优化处理的一种,正如 Zend 的 PHP 优化器所作的那样。牢记这个优化处理不失为一个好主意,因为并不是所有的指令优化器都会做同样的优化处理,并且存在大量没有装配指令优化器的互联网服务提 供商(ISPs)和服务器。

35.并不是事必面向对象 (OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。

36.并非要用类实现所有的数据结构,数组也很有用。

37.不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?

38.当你需要时,你总能把代码分解成方法。

39.尽量采用大量的 PHP 内置函数。

40.如果在代码中存在大量耗时的函数,你可以考虑用 C 扩展的方式实现它们。

41.评估检验 (profile) 你的代码。检验器会告诉你,代码的哪些部分消耗了多少时间。Xdebug 调试器包含了检验程序,评估检验总体上可以显示出代码的瓶颈。

42.mod_zip 可作为 Apache 模块,用来即时压缩你的数据,并可让数据传输量降低 80%。

分类:PHP 标签:,

基于 PHP 的 cURL 快速入门

2011-07-26 留下评论

cURL 是一个利用 URL 语法规定来传输文件和数据的工具,支持很多协议,如 HTTP、FTP、TELNET 等。最爽的是,PHP 也支持 cURL 库。本文将介绍 cURL 的一些高级特性,以及在 PHP 中如何运用它。

为什么要用 cURL?

是的,我们可以通过其他办法获取网页内容。大多数时候,我因为想偷懒,都直接用简单的PHP函数:

1
2
3
4
5
$content = file_get_contents("http://www.nettuts.com");
// or
$lines = file("http://www.nettuts.com");
// or
readfile(http://www.nettuts.com);

不过,这种做法缺乏灵活性和有效的错误处理。而且,你也不能用它完成一些高难度任务——比如处理coockies、验证、表单提交、文件上传等等。

基本结构

在学习更为复杂的功能之前,先来看一下在 PHP 中建立 cURL 请求的基本步骤:

  1. 初始化
  2. 设置变量
  3. 执行并获取结果
  4. 释放 cURL 句柄
1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 初始化
$ch = curl_init();

// 2. 设置选项,包括URL
curl_setopt($ch, CURLOPT_URL, "http://www.nettuts.com");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);

// 3. 执行并获取HTML文档内容
$output = curl_exec($ch);

// 4. 释放curl句柄
curl_close($ch);

第二步(也就是 curl_setopt() )最为重要,一切玄妙均在此。有一长串 cURL 参数可供设置,它们能指定 URL 请求的各个细节。要一次性全部看完并理解可能比较困难,所以今天我们只试一下那些更常用也更有用的选项。

检查错误

你可以加一段检查错误的语句(虽然这并不是必需的):

1
2
3
4
5
6
// ...
$output = curl_exec($ch);
if ($output === FALSE) {
    echo "cURL Error: " . curl_error($ch);
}
// ...

请注意,比较的时候我们用的是“=== FALSE”,而非“== FALSE”。因为我们得区分 空输出 和 布尔值 FALSE,后者才是真正的错误。

获取信息

这是另一个可选的设置项,能够在 cURL 执行后获取这一请求的有关信息:

1
2
3
4
5
// ...
curl_exec($ch);
$info = curl_getinfo($ch);
echo '获取'. $info['url'] . '耗时'. $info['total_time'] . '秒';
// ...

返回的数组中包括了以下信息:

“url” //资源网络地址
“content_type” //内容编码
“http_code” //HTTP状态码
“header_size” //header的大小
“request_size” //请求的大小
“filetime” //文件创建时间
“ssl_verify_result” //SSL验证结果
“redirect_count” //跳转技术
“total_time” //总耗时
“namelookup_time” //DNS查询耗时
“connect_time” //等待连接耗时
“pretransfer_time” //传输前准备耗时
“size_upload” //上传数据的大小
“size_download” //下载数据的大小
“speed_download” //下载速度
“speed_upload” //上传速度
“download_content_length”//下载内容的长度
“upload_content_length” //上传内容的长度
“starttransfer_time” //开始传输的时间
“redirect_time”//重定向耗时

基于浏览器的重定向

在第一个例子中,我们将提供一段用于侦测服务器是否有基于浏览器的重定向的代码。例如,有些网站会根据是否是手机浏览器甚至用户来自哪个国家来重定向网页。

我们利用 CURLOPT_HTTPHEADER 选项来设定我们发送出的 HTTP 请求头信息(http headers),包括 user agent 信息和默认语言。然后我们来看看这些特定网站是否会把我们重定向到不同的URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// test URLs
$urls = array(
	"http://www.cnn.com",
	"http://www.mozilla.com",
	"http://www.facebook.com"
);
// test browsers
$browsers = array(

	"standard" => array (
		"user_agent" => "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)",
		"language" => "en-us,en;q=0.5"
		),

	"iphone" => array (
		"user_agent" => "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A537a Safari/419.3",
		"language" => "en"
		),

	"french" => array (
		"user_agent" => "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; GTB6; .NET CLR 2.0.50727)",
		"language" => "fr,fr-FR;q=0.5"
		)

);

foreach ($urls as $url) {

	echo "URL: $url\n";

	foreach ($browsers as $test_name => $browser) {

		$ch = curl_init();

		// set url
		curl_setopt($ch, CURLOPT_URL, $url);

		// set browser specific headers
		curl_setopt($ch, CURLOPT_HTTPHEADER, array(
				"User-Agent: {$browser['user_agent']}",
				"Accept-Language: {$browser['language']}"
			));

		// we don't want the page contents
		curl_setopt($ch, CURLOPT_NOBODY, 1);

		// we need the HTTP Header returned
		curl_setopt($ch, CURLOPT_HEADER, 1);

		// return the results instead of outputting it
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

		$output = curl_exec($ch);

		curl_close($ch);

		// was there a redirection HTTP header?
		if (preg_match("!Location: (.*)!", $output, $matches)) {

			echo "$test_name: redirects to $matches[1]\n";

		} else {

			echo "$test_name: no redirection\n";

		}

	}
	echo "\n\n";
}

首先,我们建立一组需要测试的URL,接着指定一组需要测试的浏览器信息。最后通过循环测试各种URL和浏览器匹配可能产生的情况。

因为我们指定了cURL选项,所以返回的输出内容则只包括HTTP头信息(被存放于 $output 中)。利用一个简单的正则,我们检查这个头信息中是否包含了“Location:”字样。

运行这段代码应该会返回如下结果:

用POST方法发送数据

当发起 GET 请求时,数据可以通过“查询字串”(query string)传递给一个URL。例如,在google中搜索时,搜索关键即为 URL 的查询字串的一部分:

http://www.google.com/search?q=nettuts

这种情况下你可能并不需要 cURL 来模拟。把这个 URL 丢给“file_get_contents()”就能得到相同结果。

不过有一些 HTML 表单是用 POST 方法提交的。这种表单提交时,数据是通过 HTTP 请求体(request body) 发送,而不是查询字串。例如,当使用 CodeIgniter 论坛的表单,无论你输入什么关键字,总是被 POST 到如下页面:

http://codeigniter.com/forums/do_search/

你可以用 PHP 脚本来模拟这种 URL 请求。首先,新建一个可以接受并显示 POST 数据的文件,我们给它命名为post_output.php:

print_r($_POST);

接下来,写一段 PHP 脚本来执行 cURL 请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
$url = "http://localhost/post_output.php";
$post_data = array ( "foo" => "bar", "query" => "Nettuts", "action" => "Submit");
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// 我们在POST数据哦!
curl_setopt($ch, CURLOPT_POST, 1);
// 把post的变量加上
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
$output = curl_exec($ch);
curl_close($ch);
echo $output;

执行代码后应该会得到以下结果:

这段脚本发送一个 POST 请求给 post_output.php ,这个页面 $_POST 变量并返回,我们利用 cURL 捕捉了这个输出。

文件上传

上传文件和前面的 POST 十分相似。因为所有的文件上传表单都是通过 POST 方法提交的。

首先新建一个接收文件的页面,命名为 upload_output.php:

print_r($_FILES);

以下是真正执行文件上传任务的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$url = "http://localhost/upload_output.php";
$post_data = array (
    "foo" => "bar",
    // 要上传的本地文件地址 
    "upload" => "@C:/wamp/www/test.zip"
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);

$output = curl_exec($ch);
curl_close($ch);
echo $output;

如果你需要上传一个文件,只需要把文件路径像一个 post 变量一样传过去,不过记得在前面加上@符号。执行这段脚本应该会得到如下输出:

cURL批处理(multi cURL)

cURL 还有一个高级特性——批处理句柄(handle)。这一特性允许你同时或异步地打开多个URL连接。

下面是来自来自 php.net 的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 创建两个cURL资源
$ch1 = curl_init();
$ch2 = curl_init();

// 指定URL和适当的参数
curl_setopt($ch1, CURLOPT_URL, "http://lxr.php.net/");
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_URL, "http://www.php.net/");
curl_setopt($ch2, CURLOPT_HEADER, 0);

// 创建cURL批处理句柄
$mh = curl_multi_init();

// 加上前面两个资源句柄
curl_multi_add_handle($mh,$ch1);
curl_multi_add_handle($mh,$ch2);

// 预定义一个状态变量
$active = null;

// 执行批处理
do {
    $mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);

while ($active && $mrc == CURLM_OK) {
    if (curl_multi_select($mh) != -1) {
        do {
            $mrc = curl_multi_exec($mh, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
    }
}

//关闭各个句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

这里要做的就是打开多个 cURL 句柄并指派给一个批处理句柄。然后你就只需在一个 while 循环里等它执行完毕。

这个示例中有两个主要循环。第一个 do-while 循环重复调用 curl_multi_exec() 。这个函数是无隔断(non-blocking)的,但会尽可能少地执行。它返回一个状态值,只要这个值等于常量 CURLM_CALL_MULTI_PERFORM ,就代表还有一些刻不容缓的工作要做(例如,把对应 URL 的 http 头信息发送出去)。也就是说,我们需要不断调用该函数,直到返回值发生改变。

而接下来的 while 循环,只在 $active 变量为 true 时继续。这一变量之前作为第二个参数传给了 curl_multi_exec() ,代表只要批处理句柄中是否还有活动连接。接着,我们调用 curl_multi_select() ,在活动连接(例如接受服务器响应)出现之前,它都是被“屏蔽”的。这个函数成功执行后,我们又会进入另一个 do-while 循环,继续下一条URL。

还是来看一看怎么把这一功能用到实处吧:

WordPress 连接检查器

想象一下你有一个文章数目庞大的博客,这些文章中包含了大量外部网站链接。一段时间之后,因为这样那样的原因,这些链接中相当数量都失效了。要么是被和谐了,要么是整个站点都被功夫网了…

我们下面建立一个脚本,分析所有这些链接,找出打不开或者 404 的网站/网页,并生成一个报告。

请注意,以下并不是一个真正可用的 WordPress 插件,仅仅是一段独立功能的脚本而已,仅供演示,谢谢。

好,开始吧。首先,从数据库中读取所有这些链接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// CONFIG
$db_host = 'localhost';
$db_user = 'root';
$db_pass = '';
$db_name = 'wordpress';
$excluded_domains = array(
	'localhost', 'www.mydomain.com');
$max_connections = 10;

// initialize some variables
$url_list = array();
$working_urls = array();
$dead_urls = array();
$not_found_urls = array();
$active = null;

// connect to MySQL
if (!mysql_connect($db_host, $db_user, $db_pass)) {
	die('Could not connect: ' . mysql_error());
}
if (!mysql_select_db($db_name)) {
	die('Could not select db: ' . mysql_error());
}

// get all published posts that have links
$q = "SELECT post_content FROM wp_posts
	WHERE post_content LIKE '%href=%'
	AND post_status = 'publish'
	AND post_type = 'post'";
$r = mysql_query($q) or die(mysql_error());
while ($d = mysql_fetch_assoc($r)) {

	// get all links via regex
	if (preg_match_all("!href=\"(.*?)\"!", $d['post_content'], $matches)) {

		foreach ($matches[1] as $url) {

			// exclude some domains
			$tmp = parse_url($url);
			if (in_array($tmp['host'], $excluded_domains)) {
				continue;
			}

			// store the url
			$url_list []= $url;
		}
	}
}

// remove duplicates
$url_list = array_values(array_unique($url_list));

if (!$url_list) {
	die('No URL to check');
}

我们首先配置好数据库,一系列要排除的域名($excluded_domains),以及最大并发连接数($max_connections)。然后,连接数据库,获取文章和包含的链接,把它们收集到一个数组中($url_list)。

下面的代码有点复杂了,因此我将一小步一小步地详细解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 1. 批处理器
$mh = curl_multi_init();

// 2. 加入需要批处理的 URL
for ($i = 0; $i < $max_connections; $i++) {
	add_url_to_multi_handle($mh, $url_list);
}

// 3. 初始化处理
do {
	$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);

// 4. 主循环
while ($active && $mrc == CURLM_OK) {

	// 5. 有活动链接
	if (curl_multi_select($mh) != -1) {

		// 6. 干活
		do {
			$mrc = curl_multi_exec($mh, $active);
		} while ($mrc == CURLM_CALL_MULTI_PERFORM);

		// 7. 是否有信息?
		if ($mhinfo = curl_multi_info_read($mh)) {
			// 意味着该链接正常结束

			// 8. 从 curl 句柄获取信息
			$chinfo = curl_getinfo($mhinfo['handle']);

			// 9. 死链?
			if (!$chinfo['http_code']) {
				$dead_urls []= $chinfo['url'];

			// 10. 404?
			} else if ($chinfo['http_code'] == 404) {
				$not_found_urls []= $chinfo['url'];

			// 11. 还能用
			} else {
				$working_urls []= $chinfo['url'];
			}

			// 12. 移除句柄
			curl_multi_remove_handle($mh, $mhinfo['handle']);
			curl_close($mhinfo['handle']);

			// 13. 加入新 URL,干活
			if (add_url_to_multi_handle($mh, $url_list)) {

				do {
					$mrc = curl_multi_exec($mh, $active);
				} while ($mrc == CURLM_CALL_MULTI_PERFORM);
			}
		}
	}
}

// 14. 结束
curl_multi_close($mh);

echo "==Dead URLs==\n";
echo implode("\n",$dead_urls) . "\n\n";

echo "==404 URLs==\n";
echo implode("\n",$not_found_urls) . "\n\n";

echo "==Working URLs==\n";
echo implode("\n",$working_urls);

// 15. 向批处理器添加 URL
function add_url_to_multi_handle($mh, $url_list) {
	static $index = 0;

	// 如果还有 URL 没用
	if ($url_list[$index]) {

		// 新建 curl 句柄
		$ch = curl_init();

		// 配置 curl
		curl_setopt($ch, CURLOPT_URL, $url_list[$index]);
		// 不想输出返回内容
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		// 跟踪重定向
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		// 不需要返回内容,节省带宽和时间
		curl_setopt($ch, CURLOPT_NOBODY, 1);

		// 加入到批处理器中
		curl_multi_add_handle($mh, $ch);

		// 拨一下计数器,下次调用该函数就能添加下一个 url 了
		$index++;

		return true;
	} else {

		// 没有新的URL需要处理了
		return false;
	}
}

下面解释一下以上代码。列表的序号对应着代码注释中的顺序数字。

  1. 新建一个批处理器。Created a multi handle.
  2. 稍后我们将创建一个把URL加入批处理器的函数 add_url_to_multi_handle() 。每当这个函数被调用,就有一个新url被加入批处理器。一开始,我们给批处理器添加了10个URL(这一数字由 $max_connections 所决定)。
  3. 运行 curl_multi_exec() 进行初始化工作是必须的,只要它返回 CURLM_CALL_MULTI_PERFORM 就还有事情要做。这么做主要是为了创建连接,它不会等待完整的URL响应。
  4. 只要批处理中还有活动连接主循环就会一直持续。
  5. curl_multi_select() 会一直等待,直到某个URL查询产生活动连接。
  6. cURL的活儿又来了,主要是获取响应数据。
  7. 检查各种信息。当一个URL请求完成时,会返回一个数组。
  8. 在返回的数组中有一个 cURL 句柄。我们利用其获取单个cURL请求的相应信息。
  9. 如果这是一个死链或者请求超时,不会返回http状态码。
  10. 如果这个页面找不到了,会返回404状态码。
  11. 其他情况我们都认为这个链接是可用的(当然,你也可以再检查一下500错误之类…)。
  12. 从该批次移除这个cURL句柄,因为它已经没有利用价值了,关了它!
  13. 很好,现在可以另外加一个URL进来了。再一次地,初始化工作又开始进行…
  14. 嗯,该干的都干了。关闭批处理器,生成报告。
  15. 回过头来看给批处理器添加新URL的函数。这个函数每调用一次,静态变量 $index 就递增一次,这样我们才能知道还剩多少URL没处理。

我把这个脚本在我的博客上跑了一遍(测试需要,有一些错误链接是故意加上的),结果如下:

另一些有用的cURL 选项

HTTP 认证

如果某个URL请求需要基于 HTTP 的身份验证,你可以使用下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$url = "http://www.somesite.com/members/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// 发送用户名和密码
curl_setopt($ch, CURLOPT_USERPWD, "myusername:mypassword");
// 你可以允许其重定向
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
// 下面的选项让 cURL 在重定向后
// 也能发送用户名和密码
curl_setopt($ch, CURLOPT_UNRESTRICTED_AUTH, 1);
$output = curl_exec($ch);
curl_close($ch);

FTP 上传

PHP 自带有 FTP 类库, 但你也能用 cURL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 开一个文件指针
$file = fopen("/path/to/file", "r");
// url里包含了大部分所需信息
$url = "ftp://username:password@mydomain.com:21/path/to/new/file";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// 上传相关的选项
curl_setopt($ch, CURLOPT_UPLOAD, 1);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize("/path/to/file"));

// 是否开启ASCII模式 (上传文本文件时有用)
curl_setopt($ch, CURLOPT_FTPASCII, 1);
$output = curl_exec($ch);
curl_close($ch);

翻墙术

你可以用代理发起 cURL 请求:

1
2
3
4
5
6
7
8
9
10
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,'http://www.example.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// 指定代理地址
curl_setopt($ch, CURLOPT_PROXY, '11.11.11.11:8080');
// 如果需要的话,提供用户名和密码
curl_setopt($ch, CURLOPT_PROXYUSERPWD,'user:pass');
$output = curl_exec($ch);
curl_close ($ch);

回调函数

可以在一个 URL 请求过程中,让 cURL 调用某指定的回调函数。例如,在内容或者响应下载的过程中立刻开始利用数据,而不用等到完全下载完。

1
2
3
4
5
6
7
8
9
10
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,'http://net.tutsplus.com');
curl_setopt($ch, CURLOPT_WRITEFUNCTION,"progress_function");
curl_exec($ch);
curl_close ($ch);

function progress_function($ch,$str) {
    echo $str;
    return strlen($str);
}

这个回调函数必须返回字串的长度,不然此功能将无法正常使用。

在 URL 响应接收的过程中,只要收到一个数据包,这个函数就会被调用。

小结

今天我们一起学习了 cURL 库的强大功能和灵活的扩展性。希望你喜欢。下一次要发起 URL 请求时,考虑下 cURL 吧!

分类:PHP 标签:,

细说 PHP 模板引擎

2011-07-26 留下评论

PHP模板引擎,除官方的Smarty之外,其它开源的模板引擎,不下于成百上千。这是有历史的原因的。
早期,Smarty早其由于性能上的问题,使得全世界众多的PHP开发者,不得不进一步想弄出更好的模板引擎出来。因为,高并发的高性能要求的大型网站,使用SMARTY,简直就是恶梦。

到目前为止,PHP模板引擎角然处于春秋战国时代,大量的模板引擎,让衩学者不知所措。从我们国内的基本现状来看,普通公司,对项目要求,必须要使 用MVC但其性能要求不高的,基本都是Smarty。因为Smarty作为官方的模板引擎,技术比较普及,找一个程序员也较为方便。国内一些开发框架,也 主动集成Smarty。这也造成了,一些官方的模板引擎,在国内得不到普及。

当然,早期,Smarty虽然那么性能低下,却凭借其提供的Cache争夺了市场。

近其,我对模板引擎对互联网进行一了些搜索,发现,有官方网站的大概有以下一些:

http://dwoo.org/

http://phpsavant.com/

http://www.twig-project.org/

http://www.templateblocks.com/

http://www.invenzzia.org/en/projects/open-power-libraries/open-power-template

http://www.tinybutstrong.com/

http://www.raintpl.com/

http://www.phpte.com/

http://templatelite.sourceforge.net/

http://www.vivvo.net/vte.php

http://www.phpxtemplate.org/HomePage

買取タイガーの口コミ評判を調査!高い買取金額やスピーディーな査定が魅力!

http://www.feverxl.org/template/

http://www.greaterscope.net/projects/Vemplator

http://phptal.org/

http://quickskin.worxware.com/

http://vlib.clausvb.de/vlibtemplate.php

不过,这只是其中的一部分,有些可能名不副实。并不是真正的模板引擎。

从这一现状来看,这是PHP官方一个丰当大的失误。人为地定某一个模板引擎作为官方的东西,作为用来挣钱的工具。但却不是最优秀的,也不是完全利用PHP最强大的优势来解决的方案。

我们可以从以下几点可以看出:第一,PHP最为强大的是其扩展库。模板原本完全可以用扩展库来解决的,但PHP官方没有这么做。也许,PHP官方,用扩展库,实现了数据库链接池,从而失去了一个挣钱的机会,从而后悔,这也只是一种猜测。

官方不做,不代表民间无人做,目前,最快的模板引擎:Blitz 模板引擎是一个 PHP 扩展,号称是当前最快的模板引擎。这就是说,它的低层是用C++写成的PHP扩展,而不是PHP源码。具休大家可以参考其网站:

http://alexeyrybak.com/blitz/blitz_en.html

当然,这是目前唯一发现的一个,其它的均是PHP源码的,不管谁,均说它的速度快,但,有些引擎是不需要测的,一看源码就不想用,100K不到的源 码,一堆正则表达式,没有明确提供的模板语言控制结构,函数,过滤器,还说速度快,再愉也没什么大用,更何况,正则越多,性能越低下。不知开发者是如何测 试的。

再有就是,很多代码没有很好的错误与异常管理,可不知,这会让开发者,因为你的错误,而要花去多少他们的不该浪费的时间?

另外,PHP5现在有DOM,有SPL,国外,有QueryPath实现了对HTML的操作,并由此实现了简单的模板引擎功能,然而,再也没有其它 人考虑过,用此技术作出一个良好的模板引擎。这也看出,靠做软件外包的,做项目的,只是应付工作时间内会什么,没有人想到去创造或创新。

另外,所有模板体系,现在千篇一律师,均是{}模板语言标签。却没有XML命名空间标签。真不知PHP官方是怎样想的,在SF.NET 中,SMARTY被称作:Struts for php,但它却没有象struts那样,使用XML命名空间标签。虽说,这能提供更大的灵活性,但是,这却失去了很多原本做JAVA的用户。这可以说是市 场策略上的一个相当大的败笔。从历史看,微软为了争JAVA的用户,不惜把VB改得象JAVA,还弄出个C#。但反过来看,Zend Framework中的面向对象的模式,以及象SYMFONY的面向对象的模式,均基本与JAVA等同,完全是用接口类扩展模式。(使用这种模式,使得, 框架中不得不多出在堆类文件,并不能够充分发控PHP的特长。)

此外,象CODEIGNITER干脆就不用模板引擎,而告诉用户,使用PHP的另一种语法模式实现与模板引擎相类似的功能。
Smarty与Zend Framework作为PHP官方的商业模式,实际上,很多却是阻碍了PHP的良好的发展。
不过,另外一点则是非常遗憾的,那就是,所有的非官方的模板引擎,至今无一个真正形成气候。其主要原因可能有以下几点:
早期只是满足于速度改良,一些人因类效率提高了,于是就发布了,不在向其中添加页面Cache,而这却是失去市场的一个主要原因。

第二个重要原因,那就是,很多开发者,都比较初级,并没有好好了解解Zend之类的大型框架,他们面向的应用开发需求,因而不了解这些需求,更不能应用这些开发思想。

第三个原因,很多框架,没有很好的文档,使用户无所适从。

第四个原因,很多框架,只是把源码放到网上,没有建立公司,也没有对应的社区,开源,实际根本上,是需要至少一个方面,或是社区,或是公司,否则, 无技术支持,从哪里获得用户?就象现在,我给大家列出的模板引擎,肯定是,有官方网站的,要我选用,我也是选有官方网站的。如果有强大的社区,或公司,那 更是我的首选。

侧如我们去 phpclasses.org, 或 sourceforge.net 上去搜索一下,肯定是相当多的。你会感慨,这么多的模板引擎呀!!现在还多出了一个 googlecode,上面一样也有很多。很多人以为,放上去就可以了。其实,用户希望的仍是服务。

第五个原因,那就是,很多开发者,并没有用心去做。有很多框架避然有两年多未更新的,这使得用户无法信任。

第七个原因,虽然有断方网站,却必须要注册后才下载。我不知这些开发者脑子是不是进水了,如果你的东西确实好,Smarty都不用注册直接下载,而你要注册,不给用户有匿名对你开发的东西拿去与其它进行比较的机会,你如何占领市场?

第八个原因,那就是,开发者,并不能够足够地从用户角度考虑,要使用模板引擎,则要学习一个新的模板语言,并且相当多的模析语言,并不是简单易学。于是,大家都学Smarty,不如就学Smarty,学了你这个不易学的模板语言,到其它地方,并不一定能用上,浪费时间。

这方面做得最好的,好象就是symfony template,有5分钟的向导,详细的文档,并且,很工整的面向对象的代码,当然TWIG也是一个不错的模板引擎。DWOO虽说改写了SMARTY, 但并不比SMARTY高明多少,只能说是两种不同的风格,或体系。当然,不可否认,DWOO比smarty2肯定快,但现在,相对于 smarty3,DWOO又如何了呢?

模板引擎,或许是PHP永远的一个心结!

分类:PHP 标签:,

PHP的异常处理

2011-07-26 留下评论

写健壮应用时经常会提到的异常处理,一般遵循着80/20原则: 80%的代码用于处理异常或者验证,20%的代码没什么实际的用途。原始的代码通常都是在乐观的环境下编写的。这意味着代码可以在数据正常、一切理解的基础环境中工作的很好。但是这种代码在其生命周期内是脆弱的。在极端的情形中,你得花更多的时间来未很可能永远不会发生的状况编写相应代码。

这个习惯就是要你处理全部的出错情况,而且如果要是不这么做,你的代码永远也完不成。

防守型编程

表明处理并抛出异常是一件很有意义的事情。不只是额外的异常处理可以让代码健壮,但是这有助于提高代码的可读性。这种异常处理为原作者查看何时编写提供了一个很好的说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php

/**
 * This is the exception thrown if the day of the week is invalid.
 * @author nagood
 *
 */
class InvalidDayOfWeekException extends Exception { }

class InvalidDayFormatException extends Exception { }

/**
 * Gets the name of the day given the day in the week. Will
 * return an error if the value supplied is out of range.
 *
 * @param $day
 * @return unknown_type
 */
function convertDayOfWeekToName($day)
{
    if (! is_numeric($day)) {
        throw new InvalidDayFormatException('The value '' . $day . '' is an ' .
            'invalid format for a day of week.');
    }

    if (($day > 6) || ($day < 0)) {
        throw new InvalidDayOfWeekException('The day number '' . $day . '' is an ' .
            'invalid day of the week. Expecting 0-6.');
    }

    $dayNames = array(
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday");
    return $dayNames[$day];
}

echo("The name of the 0 day is:  " . convertDayOfWeekToName(0) . "n");

try {
    echo("The name of the 10 day is:  " . convertDayOfWeekToName(10) . "n");
} catch (InvalidDayOfWeekException $e) {
    echo ("Encountered error while trying to convert value:  " . $e->getMessage() . "n");
}

try {
    echo("The name of the 'orange' day is:  " . convertDayOfWeekToName('orange') . "n");
} catch (InvalidDayFormatException $e) {
    echo ("Encountered error while trying to convert value:  " . $e->getMessage() . "n");
}

?>

通过检验参数的全法性——这有助于他人使用你需要正确参数的函数——你应该检验它们并抛出异常的大意:

  • 尽量抛出接近错误的异常.
  • 处理每个特殊的异常.
分类:PHP 标签:,

PHP 文件上传源码分析(RFC1867)

2011-07-26 留下评论

文件上传,一般分为俩种方式 FTP 和 HTTP, 对于我们的互联网应用来说: FTP上传虽然传输稳定, 但是易用性和安全性都是个问题. 你总不至于在用户要上传头像的时候告诉用户”请打开FTP客户端,上传文件到http://www.laruence.com/uploads/中, 并以2dk433423l.jpg命名” 吧?

而基于 HTTP 的上传,相对来说易用性和安全性上就比 FTP 要增强了很多. 可以应用的上传方式有 PUT, WEBDAV, 和 RFC1867 三种, 本文将分析在 PHP 中,是如何基于 RFC1867 实现文件上传的.

RFC1867

RCF1867 是 Form-based File Upload in HTML 标准协议, RFC1867 标准对 HTML 做出了两处修改:

  1. 为input元素的type属性增加了一个file选项。
  2. input标记可以具有accept属性,该属性能够指定可被上传的文件类型或文件格式列表。

另外,本标准还定义了一种新的mime类型:multipart/form-data,以及当处理一个带有enctype=”multipart/form-data” 并且/或含有 <input type=”file”>的标记的表单时所应该采取的行为。

举例来说,当HTML想让用户能够上传一个或更多的文件时,他可以这么写:

1
2
3
4
5
6
7
<form enctype="multipart/form-data" action="upload.php" method=post>
选择文件:
<input name="userfile" type="file">
文件描述:
<input name="description" type="text">
<input type="submit" value="上传">
</form>

这个表单, 大家一定不陌生, 而对于PHP来说, 它自己另外定义了一个默认表单元素MAX_FILE_SIZE, 用户可以通过这个隐藏的表单元素来建议PHP最多只容许上传文件的大小, 比如对于上面的例子, 我们希望用户上传的文件不能大于5000(5k)字节, 那么可以如下写:

1
2
3
4
5
6
7
8
<form enctype="multipart/form-data" action="upload.php" method=post>
<input type="hidden" value="5000" name="MAX_FILE_SIZE"> <!--文件大小-->
选择文件:
<input name="userfile" type="file">
文件描述:
<input name="description" type="text">
<input type="submit" value="上传">
</form>

姑且不说, 这个MAX_FILE_SIZE是多么的不可靠(所以基于浏览器的控制,都是不可靠的), 我们单纯从实现来介绍这个MAX_FILE_SIZE是如何起作用的.

当用户选择了一个文件(laruence.txt), 并填写好文件描述(“laruence的个人介绍”), 点击上传后, 发生了什么呢?

表单提交

在用户确定提交以后, 浏览器会根据用户选择的输入, 读取要上传的文件, 连同表单中的其他元素, 组织成一定格式(如下)的数据发送到form中action属性指定的页面(在本例中是upload.php):

//请求头
POST /upload.php HTTP/1.0\r\n

Host: http://www.laruence.com\r\n

Content-length: xxxxx\r\n

Content-type: multipart/form-data, boundary=7d51863950254\r\n
…\r\n\r\n
//开始POST数据内容
–7d51863950254
content-disposition: form-data; name=”description”\r\n
laruence的个人介绍
–7d51863950254
content-disposition: form-data; name=”userfile”; filename=”laruence.txt”
Content-Type: text/plain\r\n
… laruence.txt 的内容…
–7d51863950254–

接下来, 就是服务器, 是如何处理这些数据了.

接受上传

当Web服务器, 此处假设为Apache(另外假设PHP是以module方式安装在Apache上的), 接受到用户的数据时, 首先它根据HTTP请求头, 通过确定MIME TYPE为PHP类型, 然后经过一些过程以后, 最终会把控制权交给PHP模块.

这个时候, PHP会调用sapi_activate来初始化一个请求, 在这个过程中, 首先判断请求类型, 此时是POST, 从而去调用sapi_read_post_data, 通过Content-type, 找到rfc1867的处理函数rfc1867_post_handler, 从而调用这个handler, 来分析POST来的数据.

关于rfc1867_post_handler这部分的源代码, 可以在mian/rfc1867.c找到,.

然后, PHP通过boundary, 对于每一个分段, 都通过检查, 是否同时定义了:

name和filename属性(有名文件上传)
没有定义name定义了filename(无名上传)
定义了name没有定义filename(普通数据),

从而进行不同的处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
if ((cd = php_mime_get_hdr_value(header, "Content-Disposition"))) {
     char *pair=NULL;
     int end=0;

     while (isspace(*cd)) {
          ++cd;
     }

     while (*cd && (pair = php_ap_getword(&cd, ';')))
     {
          char *key=NULL, *word = pair;

          while (isspace(*cd)) {
               ++cd;
          }

          if (strchr(pair, '=')) {
               key = php_ap_getword(&pair, '=');

               if (!strcasecmp(key, "name")) {
                    //获取name字段
                    if (param) {
                         efree(param);
                    }
                    param = php_ap_getword_conf(&pair TSRMLS_CC);
               } else if (!strcasecmp(key, "filename")) {
                    //获取filename字段
                    if (filename) {
                         efree(filename);
                    }
                    filename = php_ap_getword_conf(&pair TSRMLS_CC);
               }
          }
          if (key) {
               efree(key);
          }
          efree(word);
     }

在这个过程中, PHP会去检查普通数据中,是否有MAX_FILE_SIZE.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 /* Normal form variable, safe to read all data into memory */
if (!filename && param) {
     unsigned int value_len;
     char *value = multipart_buffer_read_body(mbuff, &value_len TSRMLS_CC);
     unsigned int new_val_len; /* Dummy variable */
     ......

     if (!strcasecmp(param, "MAX_FILE_SIZE")) {
                  max_file_size = atol(value);
    }

     efree(param);
     efree(value);
     continue;
}

有的话, 就会按照它的值来检查文件大小是否超出.

1
2
3
4
5
6
7
8
9
10
if (PG(upload_max_filesize) > 0 && total_bytes > PG(upload_max_filesize)) {
     cancel_upload = UPLOAD_ERROR_A;
} else if (max_file_size && (total_bytes > max_file_size)) {
#if DEBUG_FILE_UPLOAD
     sapi_module.sapi_error(E_NOTICE,
          "MAX_FILE_SIZE of %ld bytes exceeded - file [%s=%s] not saved",
           max_file_size, param, filename);
#endif
     cancel_upload = UPLOAD_ERROR_B;
}

通过上面的代码,我们也可以看到, 判断分为俩部, 第一部分是检查PHP默认的上传上限. 第二部分才是检查用户自定义的MAX_FILE_SIZE, 所以表单中定义的MAX_FILE_SIZE并不能超过PHP中设置的最大上传文件大小.

通过对name和filename的判断, 如果是文件上传, 会根据php的设置, 在文件上传目录中创建一个随机名字的临时文件:

1
2
3
4
5
6
7
8
9
10
 if (!skip_upload) {
     /* Handle file */
     fd = php_open_temporary_fd_ex(PG(upload_tmp_dir),
                "php", &temp_filename, 1 TSRMLS_CC);
     if (fd==-1) {
          sapi_module.sapi_error(E_WARNING,
                "File upload error - unable to create a temporary file");
          cancel_upload = UPLOAD_ERROR_E;
     }
}

返回文件句柄, 和临时随机文件名.

之后, 还会有一些验证,比如文件名合法, name合法等.

如果这些验证都通过, 那么就把内容读入, 写入到这个临时文件中.

1
2
3
4
5
6
7
8
9
10
11
12
.....
else if (blen > 0) {
     wlen = write(fd, buff, blen); //写入临时文件.
     if (wlen == -1) {
     /* write failed */
#if DEBUG_FILE_UPLOAD
     sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno));
#endif
     cancel_upload = UPLOAD_ERROR_F;
     }
}
....

当循环读入完成后, 关闭临时文件句柄. 记录临时变量名:

1
2
zend_hash_add(SG(rfc1867_uploaded_files), temp_filename,
     strlen(temp_filename) + 1, &temp_filename, sizeof(char *), NULL);

并且生成FILE变量, 这个时候, 如果是有名上传, 那么就会设置:

1
$_FILES['userfile'] //name="userfile"

如果是无名上传, 则会使用tmp_name来设置:

1
$_FILES['tmp_name'] //无名上传

最终交给用户编写的upload.php处理.

这时在upload.php中, 用户就可以通过move_uploaded_file来操作刚才生成的文件了~

分类:PHP 标签:, ,

通过刷新 PHP 缓冲区来加速你的站点

2011-07-26 留下评论

在当前 PHP 版本的默认配置下,“输出缓冲(Output Buffering)”是被打开的。旧版本则不是这样,在旧版本的 PHP 中,字符串在每次被输出的时候(通过 echo 或 print 函数),都会触发一次发送到客户端浏览器的动作。

“输出缓冲”的引入,使得这一过程更加快速、更加高效。缓冲区实际上是在内存中开辟了一块区域,可以认为是内存中的一个大的字符串。当程序中有字符 要输出的时候,会把要输出的内容附加到该缓冲区中,用来替代旧版本 PHP 中每次都直接输出到浏览器的方式。当缓冲区被“刷新”的时候,再统一输入到用户浏览器。以下几种情况下,会引起缓冲区的“刷新”操作:

  1. PHP 程序执行完毕;
  2. 缓存区的大小超过了 php.ini 配置文件中设置的 output_buffering 值;
  3. flush() 或者 ob_flush() 函数被调用的时候。

在实际的产品环境下,我们可以通过在 head 标签后马上刷新 PHP 的缓冲区,来达到加速你的站点的目的,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Buffer flushing in action</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
<link rel="shortcut icon" href="favicon.ico" />
</head>
<?php
// 这里强制刷新缓冲区
flush();
?>
<body>
...

(如果你的站点在使用 WordPress,你应该把类似的代码放在你的 WordPress 模板的 header.php 文件中。)

接下来来解释上述代码的作用:

当浏览器接收到页面 head 部分的代码的时候,就可以开始下载 head 段中的包括的资源了,类似 CSS 文件、站点收藏图标(Favicon)等。这些内容的下载可以与浏览器接受 body 段的内容时的时间同步进行。

具体能加速多少,需要因地制宜。这取决于很多客观条件,包括服务端的响应速度、你的页面大小、你的 CSS 文件大小和数量、浏览器是否有本地缓存等等。因素固然有很多,不过这样一个小小的优化,就可以显而易见的为你的站点加速,何乐而不为呢?

我很希望各位把这个小技巧运用在自己网站上,并跟踪所产生的实际效果。期待大家的反馈。

分类:PHP 标签:,

通过PHP OAuth客户端访问TX微博API

2011-07-25 留下评论
目前任何用户都可以在TX的微博开放平台上注册使用APP KEY,并且初级授权无需经过官网的人工验证,开通即可使用,缺陷是访问次数有所限制,不过对于个人和一些小站来说,应该足够了,况且,我只是为了测试一下客户端罢了。
详细信息业参考官方API文档:  http://open.t.qq.com/resource.php?i=1,1&j=0
代码如下:
  1. <?php
  2. session_start();
  3. require(“../_inc/libs/OAuth/OAuth.php”);
  4. //基本属性设置
  5. $oauthAttr = array();
  6. $oauthAttr[‘callback’]           = http://www.phpme_blogex.com/_test/test.php&#8217;;
  7. $oauthAttr[‘consumer_key’]       = ‘xxba793a492744af93ba9dfdd34b726e’;
  8. $oauthAttr[‘consumer_secret’]    = ‘xx56da44c894c8aecd33f9785b0e5ce4’;
  9. //如果有访问授权令牌
  10. if($_SESSION[‘oauth’][‘access_token_secret’])
  11. {
  12. $oauthAttr[‘oauth_token’]        = $_SESSION[‘oauth’][‘access_token’];
  13. $oauthAttr[‘oauth_token_secret’] = $_SESSION[‘oauth’][‘access_token_secret’];
  14. }
  15. else if($_GET[‘oauth_verifier’])
  16. {
  17. $oauthAttr[‘oauth_verifier’]     = trim($_GET[‘oauth_verifier’]);
  18. $oauthAttr[‘oauth_token’]        = $_SESSION[‘oauth’][‘oauth_token’];
  19. $oauthAttr[‘oauth_token_secret’] = $_SESSION[‘oauth’][‘oauth_token_secret’];
  20. unset($_SESSION[‘oauth’]);
  21. }
  22. $oauth = new OAuthClient($oauthAttr[‘consumer_key’], $oauthAttr[‘consumer_secret’],
  23. $oauthAttr[‘oauth_token’], $oauthAttr[‘oauth_token_secret’]);
  24. $oauth->host             = http://open.t.qq.com&#8217;;
  25. $oauth->format           = ‘json’;
  26. $oauth->decode_json  = true;
  27. $oauth->requestTokenURL = https://open.t.qq.com/cgi-bin/request_token&#8217;;
  28. $oauth->authenticateURL = https://open.t.qq.com/cgi-bin/authenticate&#8217;;
  29. $oauth->authorizeURL    = https://open.t.qq.com/cgi-bin/authorize&#8217;;
  30. $oauth->accessTokenURL  = https://open.t.qq.com/cgi-bin/access_token&#8217;;
  31. //获得访问授权令牌
  32. if(emptyempty($_SESSION[‘oauth’][‘access_token_secret’]))
  33. {
  34. //获得临时访问token并保存
  35. if(emptyempty($oauthAttr[‘oauth_token_secret’]))
  36. {
  37. $request_token = $oauth->getRequestToken($oauthAttr[‘callback’]);
  38. //如果没有错误发生
  39. if($oauth->http_code != 200)
  40. {
  41. die(“HTTP Error: {$oauth->http_code}”);
  42. }
  43. $_SESSION[‘oauth’][‘oauth_token’]        = $oauthAttr[‘oauth_token’]        = trim($request_token[‘oauth_token’]);
  44. $_SESSION[‘oauth’][‘oauth_token_secret’] = $oauthAttr[‘oauth_token_secret’] = trim($request_token[‘oauth_token_secret’]);
  45. }
  46. //获得用户授权
  47. if(emptyempty($oauthAttr[‘oauth_verifier’]))
  48. {
  49. $url = $oauth->getAuthorizeURL($oauthAttr[‘oauth_token’], false);
  50. header(“location: {$url}”);
  51. }
  52. //获得access token并保存
  53. $access_taken = $oauth->getAccessToken($oauthAttr[‘oauth_verifier’]);
  54. //如果没有错误发生
  55. if($oauth->http_code != 200)
  56. {
  57. die(“HTTP Error: {$oauth->http_code}”);
  58. }
  59. $_SESSION[‘oauth’][‘access_token’]        = $oauthAttr[‘access_token’]        = trim($access_taken[‘oauth_token’]);
  60. $_SESSION[‘oauth’][‘access_token_secret’] = $oauthAttr[‘access_token_secret’] = trim($access_taken[‘oauth_token_secret’]);
  61. }
  62. //获得QQ微博当前用户信息
  63. $result = $oauth->get(‘/api/user/info?f=1’, array(‘format’ => ‘json’));
  64. print_r($result);
  65. ?>
1、$oauth->getRequestToken($oauthAttr[‘callback’]); 获取临时访问token,返回的数据格式是json,包含3个字段:

oauth_token=a63f4a0e1242462fb8c11e53159ba294&oauth_token_secret=40ece707e064128e4fec692e3c09c692&oauth_callback_confirmed=true
将oauth_token 和oauth_token_secret保存起来下一步使用,另外一个字段没有什么用。
2、$oauth->getAuthorizeURL($oauthAttr[‘oauth_token’], false); 需要获得用户授权,这里使用该函数获得当前临时token的授权页面并做跳转。
3、用户授权后,官方链接会跳转回我们的回调地址,并且附上2个GET方式传递的值oauth_token=80484decceb142dfaf5791c9cb9cd256&oauth_verifier=47b9122eb2bb4a3ca642a78b34158193 ,其中 oauth_token值是我们传递过去的oauth_token值,oauth_verifier 保存用于下一步获取 access_taken
4、$oauth->getAccessToken($oauthAttr[‘oauth_verifier’]); 用户获取当前授权用户的最终授权令牌,保存该令牌,用户替换当前的 oauth_token 和 oauth_token_secret 可直接初始化客户端对象,用于下一次直接访问API调用。
5、最后调用官方的API接口,并按照协议传递了相应的参数获得当前的用户信息。
然后,你可以将$access_token这个变量里面的东西保存进数据表,可以做很多各种各样有趣的事情。

但是当用户删除该授权,或者重新授权的时候,该授权将无法使用,可以在用户登录的时候进行判断、操作和更新。

分类:Other, PHP 标签:, ,

腾讯微博PHP OAuth授权

2011-07-25 留下评论

之前研究过新浪微博OAuth授权,刚巧腾讯微博最近也开放API了.

本想使用Ruby OAuth来进行授权,只是每次在获取AcessToken的时候,都出现签名错误。
问题耽搁了很久,且一直未能解决,最后是直接使用了官方的PHP SDK来绕过了,并结合需求,实现了两个功能:
1.授权成功的同时,绑定到自家网站的用户id,没有就生成一个.
2.将PHP的发微博功能封装成了一个API,供自家调用.

authorize.php <!-- 这里是OAuth入口 -->
<?php
  @header('Content-Type:text/html;charset=utf-8');
  session_start();
  require_once('config.php'); //配置AppKey, AppSecret
  require_once('oauth.php');
  require_once('opent.php');
  $o = new MBOpenTOAuth( MB_AKEY , MB_SKEY );
  $keys = $o->getRequestToken('http://api.5yi.com/qq/callback.php');//这里填上你的回调URL
  $aurl = $o->getAuthorizeURL( $keys['oauth_token'] ,false,'');
  $_SESSION['keys'] = $keys;
  header("Location: ".$aurl);
?> callback.php
<!-- OAuth回调地址,绑定我易网用户 -->
<?php
  @header('Content-Type:text/html;charset=utf-8');
  session_start();
  @require_once('config.php');
  @require_once('oauth.php');
  @require_once('opent.php');
  @require_once('api_client.php');
  // 获取Acess Token
  $o = new MBOpenTOAuth( MB_AKEY , MB_SKEY , $_SESSION['keys']['oauth_token'] , $_SESSION['keys']['oauth_token_secret'] );
  $last_key = $o->getAccessToken( $_REQUEST['oauth_verifier'] );
  $_SESSION['last_key'] = $last_key;
  // 获取用户的信息
  $c = new MBApiClient( MB_AKEY , MB_SKEY , $_SESSION['last_key']['oauth_token'] , $_SESSION['last_key']['oauth_token_secret'] );
  $response = $c->getUserInfo();
  // 重定向到我易网登录API,绑定我易网用户id
  $url = "http://api.5yi.com:9080/third_login/t_qq?token=".$last_key['oauth_token']."&secret=".$last_key['oauth_token_secret']."&uid=".$response['data']['name']."&uname=".$response['data']['nick']."&source=tqq";
  header("Location: ".$url);
?>
post.php
<!-- 调用PHP接口,发送微博信息 -->
<?php
@header('Content-Type:text/html;charset=utf-8');
session_start();
@require_once('config.php');
@require_once('oauth.php');
@require_once('opent.php');
@require_once('api_client.php');
//$c = new MBApiClient( MB_AKEY , MB_SKEY , $_SESSION['last_key']['oauth_token'] , $_SESSION['last_key']['oauth_token_secret'] );
$c = new MBApiClient( MB_AKEY , MB_SKEY , $_REQUEST['oauth_token'] , $_REQUEST['oauth_token_secret'] );
//发消息
$p =array(
'c' => ''.$_REQUEST["text"],
'ip' => $_SERVER['REMOTE_ADDR'],
'j' => '',
'w' => ''
);
var_dump($c->postOne($p));
?>
分类:PHP 标签:, ,

Facebook Api 使用(PHP版)

2011-07-24 留下评论

如果想通过Facebook登录到你的网站,Facebook站外API可以实现你想要的,如下介绍实际使用.

(我目前没在网上找到中文的更详细的介绍了,呵呵.)

(提示:Facebook不支持取得朋友的邮件地址,如果需求是这个,别在浪费功夫了)

注册Facebook Key http://www.facebook.com/developers/apps.php 注册后可以得到

App ID,API Key,App Secret (如果你想测试,必须要一个域名,申请的时候填上)

https://github.com/facebook/php-sdk/下载PHP开发包

进入examples/页面,里面有个示例文件,

$facebook = new Facebook(array(
‘appId’  => ”,//你的App ID
‘secret’ => ”,//你的App Secret
‘cookie’ => true,
));

修改如上

如果你本地的服务器无法验证FACEBOOK的SSL验证,请修改facebook class 的方法makeRequest

在curl_init()之后添加:

$opts[CURLOPT_SSL_VERIFYPEER] = false;
$opts[CURLOPT_SSL_VERIFYHOST] = 2;

上面的API支持FACEBOOK新旧两种格式,

通过调用api传不同的参数实现

1.路径型,如:/me

还有具体的参数可以参考:http://developers.facebook.com/docs/reference/api/ 页面

在右边objects下面,每个点击进去之后都有非常多的参数

2.数组型,如array(“method”=>”links.get”)

数组型是为兼容旧的API而保留的,建议用新的.旧的所有功能基本上新的都有,具体参数可以参考:getApiUrl 方法

如果你使用 /me 你会发现你取不到自己的邮箱地址,是因为你没权限,在http://developers.facebook.com/docs/reference/api/user/

你会发现有很多东西是要得到用户的授权才可以访问的,以下是具体的授权方式:

https://graph.facebook.com/oauth/authorize?client_id=171908639493418&redirect_uri=you url address&scope=read_mailbox,email

你也可以自己来往facebook里添加生成上述地址的方法,这里不介绍了

可以得到授权,你就可以往facebook推送消息了

$dat=array(
“picture”=>”image”,
“subject”=>”你好啊~”,
“message”=>”呵呵,测试FACEBOOKAPI~,这个是测试信息.联系Q:97148830″,
);

$f = $facebook->api(‘/me/friends’);
foreach($f[‘data’] as $v){
$f = $facebook->api(‘/’.$v[‘id’].’/feed’,”POST”,$dat);//$f是返回你发送的消息的ID,消息只能发送到公开的版面,比如消息墙
}

通过不同的$dat参数来实现不同的消息,具体你就参考上面的API参数解释的地址吧

还有一个问题,加入一个用户登录了你的网站后立即跑到facebook去把他刚才的授权给取消掉,你去取他的信息的时候就出错误了,

facebook也为这个情况提供了一个解决办法,请参考:http://developers.facebook.com/docs/reference/api/subscription/

就是让你的应用程序就订阅用户的状态,但用户使用了你的应用程序之后对你的在facebook上的应用进行有关操作的时候,facebook会发消息提示你

想使用这个功能,你得先到你的网站建立一个回调验证程序,FACEBOOK也为你提供了,可以在http://github.com/facebook/real-time/blob/master/samples/ 下载得到

把里面的 callback.php 放到你的站点目录,这个文件用于验证应用程序是你的外还用于得到用户的活动消息,

下面是如何添加订阅,查询订阅,删除订阅的代码,(更改就是发送添加订阅,存在的话会覆盖掉以前的)

$acc=file_get_contents(“https://graph.facebook.com/oauth/access_token?client_id=you app id&client_secret=you app secret&grant_type=client_credentials”);
$t=explode(“=”,$acc);
$dat=array(
“object”=>”user”,
“fields”=>”feed,friends,activities,interests,music,books,movies,television,likes,checkins”,//就支持这些
“callback_url”=>””,//刚刚那个callback.php
“verify_token”=>”abc”,//你的callback.php里的那个token,自己看下或自己修改
“access_token”=>$t[1]
);
$f = $facebook->api(‘/appid/subscriptions’,”POST”,$dat);//添加
print_r($f);//啥也没返回
$dat=array(
“access_token”=>$t[1]
);
$f = $facebook->api(‘/appid/subscriptions’,”GET”,$dat);//查询
print_r($f);//很多关于你订阅的东东
$param = array(‘access_token’ => $t[1]);
$subs = $facebook->api(‘/appid/subscriptions’, ‘DELETE’, $param); //干掉了

添加后如果别人移除了他使用你的应用程序,会得到一个消息,大约如下:

Array
(
[fb_sig_uninstall] => 1//YOU APP UNINSTALL
[fb_sig_locale] => en_US
[fb_sig_in_new_facebook] => 1
[fb_sig_time] => 1293444384.8939
[fb_sig_added] => 0
[fb_sig_user] => 100001008942959
[fb_sig_country] => us
[fb_sig_api_key] => appkey
[fb_sig_app_id] => appid
[fb_sig] => 383d629e8eeac39022adbc1d158f6065
)

新版的API把crt证书移除了,但我这里还有保留,需要的下载

这样就可以实现facebook ssl 验证了,而不用设置

$opts[CURLOPT_SSL_VERIFYPEER] = false;
$opts[CURLOPT_SSL_VERIFYHOST] = 2;

不过要修改:

public static $CURL_OPTS = array(
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT        => 60,
CURLOPT_USERAGENT      => ‘facebook-php-2.0’,
);

public static $CURL_OPTS = array(
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT        => 60,
CURLOPT_USERAGENT      => ‘facebook-php-2.0’,
CURLOPT_CAINFO      => ‘/home/lonely/public_html/fb_ca_chain_bundle.crt’,//你的地址
);

搞定~

错误:

200 The user hasn’t authorized the application to perform this action 用户没有权限

210 User not visible 用户无效
开发完毕,记得进入FACEBOOK的应用程序页面,吧沙盒模式关闭,否则用户没法登陆的~
分类:PHP 标签:, ,

PHP源码防泄漏方法

2011-07-24 留下评论

四条预防PHP源代码泄漏的方法:

1)使用mod_security过滤输出严防泄漏 Use mod_security to filter output and prevent leakage (例如)

PHP代码

SecFilterOutput On

SecFilterSelective OUTPUT “<?php” log,deny

2)不要将关键敏感代码放到根目录中 Code should live outside of the web root (例如)

PHP代码

index.php:

<?php

include(‘../realroot/index.php’);

?>

3)更改默认的文件类型 Change the default file type (例如对http.conf做如下修改)

PHP代码

httpd.conf:

DefaultType application/x-httpd-php

4)绝对禁止访问根目录 Deny all outside of the webroot (假设你的根目录是 ‘www’ ,例如)

PHP代码

http.conf: (or .htaccess)

<directory />

Order Deny,Allow

Deny from all

Options None

AllowOverride None

<directory /www>

Order Allow,Deny

Allow from all

</directory>

分类:PHP 标签:, ,

php实现RC4加密算法的函数

2011-07-15 留下评论

/*
* rc4加密算法
* 解密方法:重新加密一次,便可还原。。
* $pwd 密钥
* $data 要加密的数据
*/
function rc4 ($pwd, $data)//$pwd密钥 $data需加密字符串
{
$key[] =””;
$box[] =””;

$pwd_length = strlen($pwd);
$data_length = strlen($data);

for ($i = 0; $i < 256; $i++)
{
$key[$i] = ord($pwd[$i % $pwd_length]);
$box[$i] = $i;
}

for ($j = $i = 0; $i < 256; $i++)
{
$j = ($j + $box[$i] + $key[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}

for ($a = $j = $i = 0; $i < $data_length; $i++)
{
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;

$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;

$k = $box[(($box[$a] + $box[$j]) % 256)];
$cipher .= chr(ord($data[$i]) ^ $k);
}

return $cipher;
}

分类:PHP 标签:, , ,

PHP使用正则替换全角字符的函数

2011-07-15 留下评论

function zo_replace(text) {
return text && text.replace(/[\u3000\uff01-\uff5f]/g, function($0) {
return $0 == “\u3000″ ? ” ” : String.fromCharCode($0.charCodeAt(0) – 0xfee0);
});
}

分类:PHP 标签:, , ,

PHP使用正则替换全角字符的函数

2011-07-15 留下评论

function zo_replace(text) {
return text && text.replace(/[\u3000\uff01-\uff5f]/g, function($0) {
return $0 == “\u3000″ ? ” ” : String.fromCharCode($0.charCodeAt(0) – 0xfee0);
});
}

分类:PHP 标签:, , ,

php文件下载的函数

2011-07-15 留下评论

public function downloads($name){
$name_tmp = explode(“_”,$name);
$type = $name_tmp[0];
$file_time = explode(“.”,$name_tmp[3]);
$file_time = $file_time[0];
$file_date = date(“Y/md”,$file_time);
$file_dir = SITE_PATH.”/data/uploads/$type/$file_date/”;

if (!file_exists($file_dir.$name)){
header(“Content-type: text/html; charset=utf-8”);
echo “File not found!”;
exit;
} else {
$file = fopen($file_dir.$name,”r”);
Header(“Content-type: application/octet-stream”);
Header(“Accept-Ranges: bytes”);
Header(“Accept-Length: “.filesize($file_dir . $name));
Header(“Content-Disposition: attachment; filename=”.$name);
echo fread($file, filesize($file_dir.$name));
fclose($file);
}
}

分类:PHP 标签:, ,

PHP 生成 .htpasswd 文件的用户名和密码

2011-07-15 留下评论

<?php

// – – – – – – – – – – – – – – – – – – –
// Author: Christopher Hair
// Website: http://www.chrishair.co.uk
// – – – – – – – – – – – – – – – – – – –

if (isset($_POST[‘submit’])){
$error = ”;
$username = trim(stripslashes($_POST[‘username’]));
$password = trim(stripslashes($_POST[‘password’]));

if ($username == ” || preg_match(‘/\s/m’, $username)) {
$error .= “<p><strong>Invalid Username.</strong></p>\n”;
}

if ($password == ” || preg_match(‘/\s/m’, $password)) {
$error .= “<p><strong>Invalid Password.</strong></p>\n”;
}

if ($error == ”) {
echo “<p>Success</p>\n”;
echo “<h1>” . htmlspecialchars($username) . “:” . crypt($password) . “</h1>\n”;
} else {
echo $error;
}
}

?>

分类:Apache, PHP 标签:,

Apache中.htaccess重定向某个类型的文件到另外的URL

2011-07-15 留下评论

RedirectMatch 301 (.*)\.asp$ http://www.domain.com/newpage.html

分类:Apache, PHP 标签:, , , ,

PHP定时执行任务的实现

2011-07-13 留下评论

用到的函数  ignore_user_abort(),set_time_limit(0),sleep($interval)
此代码只要运行一次后关闭浏览器即可。
不知道能程序的性能会不会影响很大!

[cc lang=”php”]
ignore_user_abort();//关掉浏览器,PHP脚本也可以继续执行.
set_time_limit(0);// 通过set_time_limit(0)可以让程序无限制的执行下去
$interval=60*30;// 每隔半小时运行
do{
//这里是你要执行的代码
sleep($interval);// 等待5分钟
}while(true);
[/cc]

分类:PHP 标签:,

PHP如何在服务器端调整图片大小

2011-07-13 留下评论

在服务器端完成图片大小的调整,会比在浏览器的处理有很多的好处。
本文介绍了PHP如何在服务器端调整图片大小。
代码包括两部分:

  • imageResizer() is used to process the image
  • loadimage() inserts the image url in a simpler format

[cc lang=”php”]
function imageResizer($url, $width, $height) {

header(‘Content-type: image/jpeg’);

list($width_orig, $height_orig) = getimagesize($url);

$ratio_orig = $width_orig/$height_orig;

if ($width/$height > $ratio_orig) {
$width = $height*$ratio_orig;
} else {
$height = $width/$ratio_orig;
}

// This resamples the image
$image_p = imagecreatetruecolor($width, $height);
$image = imagecreatefromjpeg($url);
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);

// Output the image
imagejpeg($image_p, null, 100);

}

//works with both POST and GET
$method = $_SERVER[‘REQUEST_METHOD’];

if ($method == ‘GET’) {

imageResize($_GET[‘url’], $_GET[‘w’], $_GET[‘h’]);

} elseif ($method == ‘POST’) {

imageResize($_POST[‘url’], $_POST[‘w’], $_POST[‘h’]);
}

// makes the process simpler
function loadImage($url, $width, $height){
echo ‘image.php?url=’, urlencode($url) ,
‘&w=’,$width,
‘&h=’,$height;
}
//调用方法
//Above code would be in a file called image.php.
//Images would be displayed like this:
<img src=”<?php loadImage(‘image.jpg’, 50, 50) ?>” alt=”” />
[/cc]

分类:PHP 标签:, ,

php时间友好格式化

2011-07-12 留下评论

[cc lang=”php”]
class DateFormat
{
private static $_DIFF_FORMAT = array(
‘DAY’             => ‘%s天前’,
‘DAY_HOUR’        => ‘%s天%s小时前’,
‘HOUR’             => ‘%s小时’,
‘HOUR_MINUTE’     => ‘%s小时%s分前’,
‘MINUTE’         => ‘%s分钟前’,
‘MINUTE_SECOND’    => ‘%s分钟%s秒前’,
‘SECOND’        => ‘%s分钟前’,
);

/**
* 友好格式化时间
*
* @param int 时间
* @param array $formats
* @return string
*/
public static function diff($timestamp, $formats = null)
{
if ($formats == null) {
$formats = self::$_DIFF_FORMAT;
}
/* 计算出时间差 */
$seconds = time() – $timestamp;
$minutes = floor($seconds / 60);
$hours      = floor($minutes / 60);
$days      = floor($hours / 24);

if ($days > 0) {
$diffFormat = ‘DAY’;
} else {
$diffFormat = ($hours > 0) ? ‘HOUR’ : ‘MINUTE’;
if ($diffFormat == ‘HOUR’) {
$diffFormat .= ($minutes > 0 && ($minutes – $hours * 60) > 0) ? ‘_MINUTE’ : ”;
} else {
$diffFormat = (($seconds – $minutes * 60) > 0 && $minutes > 0)
? $diffFormat.’_SECOND’ : ‘SECOND’;
}
}

$dateDiff = null;
switch ($diffFormat) {
case ‘DAY’:
$dateDiff = sprintf($formats[$diffFormat], $days);
break;
case ‘DAY_HOUR’:
$dateDiff = sprintf($formats[$diffFormat], $days, $hours – $days * 60);
break;
case ‘HOUR’:
$dateDiff = sprintf($formats[$diffFormat], $hours);
break;
case ‘HOUR_MINUTE’:
$dateDiff = sprintf($formats[$diffFormat], $hours, $minutes – $hours * 60);
break;
case ‘MINUTE’:
$dateDiff = sprintf($formats[$diffFormat], $minutes);
break;
case ‘MINUTE_SECOND’:
$dateDiff = sprintf($formats[$diffFormat], $minutes, $seconds – $minutes * 60);
break;
case ‘SECOND’:
$dateDiff = sprintf($formats[$diffFormat], $seconds);
break;
}
return $dateDiff;
}
}

echo DateFormat::diff(‘1310455823’);
/* 33分钟47秒前  */

[/cc]

分类:PHP 标签:, ,

PHP读取excel(.csv, .xls)文件的方法

2011-07-12 留下评论

常用的用PHP读取EXCEL的方法有以下三种,各自有各自的优缺点。个人推荐用第三种方法,因为它可以跨平台使用。

1. 以.csv格式读取
将.xls转换成.csv的文本格式,然后再用PHP分析这个文件,和PHP分析文本没有什么区别。
优点:跨平台,效率比较高、可以读写。
缺点:只能直接使用.csv的文件,如果经常接受.xls二进制文件的话需要手工转换,不能自动化。一个文件只有一个SHEET。

PHP有自带的分析.csv函数:fgetcsv

array fgetcsv ( int $handle [, int $length [, string $delimiter [, string $enclosure]]] )
handle 一个由 fopen()、popen() 或 fsockopen() 产生的有效文件指针。
length (可选)必须大于 CVS 文件内最长的一行。在 PHP 5 中该参数是可选的。如果忽略(在 PHP 5.0.4 以后的版本中设为 0)该参数的话,那么长度就没有限制,不过可能会影响执行效率。
delimiter (可选)设置字段分界符(只允许一个字符),默认值为逗号。
enclosure (可选)设置字段环绕符(只允许一个字符),默认值为双引号。该参数是在 PHP 4.3.0 中添加的。 和 fgets() 类似,只除了 fgetcsv() 解析读入的行并找出 CSV 格式的字段然后返回一个包含这些字段的数组。
fgetcsv() 出错时返回 FALSE,包括碰到文件结束时。
注意: CSV 文件中的空行将被返回为一个包含有单个 null 字段的数组,不会被当成错误。

当然也可以自己手动分析字符串。
[cc lang=”php”]
$row = 1;
$handle = fopen(“test.csv”,”r”);
while ($data = fgetcsv($handle, 1000, “,”)) {
$num = count($data);
echo ”
$num fields in line $row:\n”;
$row++;
for ($c=0; $c < $num; $c++) {
echo $data[$c] . "\n";
}
}
fclose($handle);
[/cc]
还可以利用fputcsv函数将行格式化为 CSV 并写入文件指针。

2. ODBC链接数据源

优点:支持多种格式,cvs, xls等。支持读写,使用标准SQL语言,和SQLSERVER、MYSQL数据库几乎完全一样。
缺点:值支持windows服务器

3. PHP自定义类

优点:跨平台。某些类支持写操作。支持.xls二进制文件
常用的类有phpExcelReader、PHPExcel。其中后者支持读写,但是需要php5.2以上版本。

phpExcelReader是专门用来读取文件的。返回一个数组,包含表格的所有内容。
该 class 使用的方法可以参考网站下载回来的压缩档中的 example.php。
不过我下载回来的 (版本 2009-03-30),有两点要注意:
reader.php 中的下面这行要修改
将 require_once ‘Spreadsheet/Excel/Reader/OLERead.php’;

改为 require_once ‘oleread.inc’;

example.php 中
修改 $data->setOutputEncoding(‘CP1251′);

为 $data->setOutputEncoding(‘CP936′);

修改 nl2br(htmlentities($data->sheets[$sheet][‘cells’][$row][$col]));

为 $table_output[$sheet] .= nl2br(htmlspecialchars($data->sheets[$sheet][‘cells’][$row][$col]));

不然中文会有问题。
繁体的话可以修改为CP950、日文是CP932,具体可参考codepage说明。

修改 $data->read(‘jxlrwtest.xls’) 为自己的 excel 文件名,zip 档中附的 jxlrwtest.xls 应该是坏了。
这是下载地址:

phpExcelReader:http://sourceforge.net/projects/phpexcelreader/
PHPExcel:http://www.codeplex.com/PHPExcel/Wiki/View.aspx?title=Documents&referringTitle=Home

分类:PHP 标签:, , , ,

php读取csv的函数

2011-07-12 留下评论

[cc lang=”php”]

function read_csv($cvs) {

$shuang = false;

$str = file_get_contents($cvs);

for($i=0;$i<strlen($str);$i++) {

if($str{$i}=='”‘) {

if($shuang) {

if($str{$i+1}=='”‘) {

$str{$i} = ‘*’;

$str{$i+1} = ‘*’;

} else {

$shuang = false;

}

} else {

$shuang = true;

}

}

if($str{$i}==’,’) {

if($shuang) {

} else {

$str{$i} = ‘|’;

}

}

if($str{$i}==”\n”) {

if($shuang) {

$str{$i} = ‘^’;

} else {

}

}

}

$str = str_replace(array(‘”‘,’*’),array(”,'”‘),$str);

$a1 = explode(“\n”,$str);

$array = array();

foreach($a1 as $k=>$value) {

if($value) {

$value = str_replace(“^”,”\n”,$value);

$array[$k] = explode(“|”,$value);

}

}

return $array;

}
[/cc]

百度排名抓取

2011-07-11 留下评论

function s($keyword,$url,$page = 1){
static $px = 0;
$rsState = false;

$enKeyword = urlencode($keyword);
$firstRow = ($page – 1) * 10;

if($page > 10){
die(’10页之内没有该网站排名..end’);
}
$contents = file_get_contents(“http://www.baidu.com/s?wd=$enKeyword&&pn=$firstRow&#8221;);
preg_match_all(‘/<table[^>]*?class=”result”[^>]*>[\s\S]*?<\/table>/i’,$contents,$rs);
foreach($rs[0] as $k=>$v){
$px++;
if(strstr($v,$url)){
$rsState = true;
preg_match_all(‘/<h3[\s\S]*?(<a[\s\S]*?<\/a>)/’,$v,$rs_t);
echo ‘当前 “‘ . $url . ‘” 在百度关键字 “‘ . $keyword . ‘” 中的排名为:’ . $px;
echo ‘<br>’;
echo ‘第’ . $page . ‘页;第’ . ++$k . “个<a target=’_blank’ href=’http://www.baidu.com/s?wd=$enKeyword&&pn=$firstRow’>进入百度</a>&#8221;;
echo ‘<br>’;
echo $rs_t[1][0];
break;
}
}
unset($contents);
if($rsState === false){
s($keyword, $url,++$page);
}
}
if(isset($_POST[‘submit’])){

$time = explode(‘ ‘,microtime());
$start = $time[0] + $time[1];

$url = $_POST[‘url’];
if( count(explode(‘.’,$url)) <= 2){

$url = ltrim($url,’http://&#8217;);
$url = ‘www.’ . $url;
}

s($_POST[‘keyword’],$url);

$endtime = explode(‘ ‘,microtime());

$end = $endtime[0] + $endtime[1];

echo ‘<hr>’;
echo ‘程序运行时间: ‘;
echo $end – $start;
die();
}

?>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=gbk” />
<title>抓取排名</title>

</head>

<body>
<form action=”” method=”post”>
<ul>
<li>
<span>关键字:</span><input type=”text” name=”keyword”>
</li>
<li>
<span>url地址:</span><input type=”text” name=”url”>
</li>
<li>
<input type=”submit” name=”submit” value=”搜索”>
</li>
</ul>

</form>
</body>
</html>

分类:PHP, SEO 标签:, ,

wordpress某段时间内的文章数

2011-07-11 留下评论

function num_posts($days=1) {//$days就是设定时间一天;
global $wpdb;
$today = gmdate(‘Y-m-d H:i:s’, time() + 3600 * 8);//获取当前的时间
$daysago = date( “Y-m-d H:i:s”, strtotime($today) – ($days * 24 * 60 * 60) );  //Today – $days
$result = $wpdb->get_results(“SELECT ID FROM $wpdb->posts WHERE post_date BETWEEN ‘$daysago’ AND ‘$today’ AND post_status=’publish’ AND post_type=’post’ ORDER BY post_date DESC “);
foreach ($result as $Item) {
$post_ID[] = $Item->ID;//已发布的文章ID,写到一个数组里面去
}
$post_num = count($post_ID);//输出数组中元素个数,文章ID的数量,也就是发表的文章数量
$output .= ‘<a>’.$post_num.'</a>’;//输出文章数量
echo $output;
}