Posts Tagged ‘ PHP

巧用zend cache+smarty

最近网站general listing程序执行的速度比较慢,主要原因是在关联表数量太多,数据库的压力大造成的。
Ok 进入正题:

原先页面程序执行时间为2s+;
第一步;我分析了下程序,没看出有什么语句上的问题;
第二步:转而查数据库,发现产品与产品属性的关联表有一个字段没加索引,加好后速度明显提升,大概是1s+
可是还不行啊,程序就要消耗掉1s的时间;
在此我简单介绍下我的网站架构
底层:Zend Framework + 一些类库;
View层用的是Smarty;
思考下,问题主要是在与数据的查询上出的,也就是说在把数据给Smarty之前花掉了大量时间;那我们如果能把这部分数据缓存下来,岂不是完美;

这样理解:
原来是:接收请求->分析请求->查询数据库->处理数据->Smarty->页面
现在呢:接收请求->缓存数据->Smarty->页面

也就是 缓存数据==分析请求->查询数据库->处理数据

做好后程序执行时间由1s变成0.008s
2s/0.008s = ?
对就是200多倍;

虽然smarty由cache的功能,但是对与一个动态的电子商务网站根本就不适合,为什么?有兴趣的可以把smarty的cache设置为true 试试看,呵呵~

还有呢:数据缓存+SMARTY 个人感觉比较理想 因为Smarty编译好后成为php脚本本身时间不要消耗很长。

当然也有缺点,缓存过期后第一个访问网站的人会稍微感觉有些慢 也就是原来优化后的1s 但是一个几百万pv的网站 一个人偶尔某个页面访问慢总比都慢好吧

数据缓存的方法有很多,我用的是Zend Cache,如果你的内存够大,也可用memcached扩展将缓存数据放入内存。更快了~

个人不才,望高手看后可以给予更多的指正,也欢迎一起讨论~

PHP图片水印 (水印支持图片或文字)范例参考程序

<?php
/*
* 功能:PHP图片水印 (水印支持图片或文字)
* 参数:
*      $groundImage    背景图片,即需要加水印的图片,暂只支持GIF,JPG,PNG格式;
*      $waterPos        水印位置,有10种状态,0为随机位置;
*                        1为顶端居左,2为顶端居中,3为顶端居右;
*                        4为中部居左,5为中部居中,6为中部居右;
*                        7为底端居左,8为底端居中,9为底端居右;
*      $waterImage        图片水印,即作为水印的图片,暂只支持GIF,JPG,PNG格式;
*      $waterText        文字水印,即把文字作为为水印,支持ASCII码,不支持中文;
*      $textFont        文字大小,值为1、2、3、4或5,默认为5;
*      $textColor        文字颜色,值为十六进制颜色值,默认为#FF0000(红色);
*
* 注意:Support GD 2.0,Support FreeType、GIF Read、GIF Create、JPG 、PNG
*      $waterImage 和 $waterText 最好不要同时使用,选其中之一即可,优先使用 $waterImage。
*      当$waterImage有效时,参数$waterString、$stringFont、$stringColor均不生效。
*      加水印后的图片的文件名和 $groundImage 一样。
*      作者:longware @ 2004-11-3 14:15:13
*/
function imageWaterMark($groundImage,$waterPos=0,$waterImage=”",$waterText=”",$textFont=5,$textColor=”#FF0000″)
{
$isWaterImage = FALSE;
$formatMsg = “暂不支持该文件格式,请用图片处理软件将图片转换为GIF、JPG、PNG格式。”;

//读取水印文件
if(!empty($waterImage) && file_exists($waterImage))
{
$isWaterImage = TRUE;
$water_info = getimagesize($waterImage);
$water_w    = $water_info[0];//取得水印图片的宽
$water_h    = $water_info[1];//取得水印图片的高

switch($water_info[2])//取得水印图片的格式
{
case 1:$water_im = imagecreatefromgif($waterImage);break;
case 2:$water_im = imagecreatefromjpeg($waterImage);break;
case 3:$water_im = imagecreatefrompng($waterImage);break;
default:die($formatMsg);
}
}

//读取背景图片
if(!empty($groundImage) && file_exists($groundImage))
{
$ground_info = getimagesize($groundImage);
$ground_w    = $ground_info[0];//取得背景图片的宽
$ground_h    = $ground_info[1];//取得背景图片的高

switch($ground_info[2])//取得背景图片的格式
{
case 1:$ground_im = imagecreatefromgif($groundImage);break;
case 2:$ground_im = imagecreatefromjpeg($groundImage);break;
case 3:$ground_im = imagecreatefrompng($groundImage);break;
default:die($formatMsg);
}
}
else
{
die(“需要加水印的图片不存在!”);
}

//水印位置
if($isWaterImage)//图片水印
{
$w = $water_w;
$h = $water_h;
$label = “图片的”;
}
else//文字水印
{
$temp = imagettfbbox(ceil($textFont*2.5),0,”./cour.ttf”,$waterText);//取得使用 TrueType 字体的文本的范围
$w = $temp[2] – $temp[6];
$h = $temp[3] – $temp[7];
unset($temp);
$label = “文字区域”;
}
if( ($ground_w<$w) || ($ground_h<$h) )
{
echo “需要加水印的图片的长度或宽度比水印”.$label.”还小,无法生成水印!”;
return;
}
switch($waterPos)
{
case 0://随机
$posX = rand(0,($ground_w – $w));
$posY = rand(0,($ground_h – $h));
break;
case 1://1为顶端居左
$posX = 0;
$posY = 0;
break;
case 2://2为顶端居中
$posX = ($ground_w – $w) / 2;
$posY = 0;
break;
case 3://3为顶端居右
$posX = $ground_w – $w;
$posY = 0;
break;
case 4://4为中部居左
$posX = 0;
$posY = ($ground_h – $h) / 2;
break;
case 5://5为中部居中
$posX = ($ground_w – $w) / 2;
$posY = ($ground_h – $h) / 2;
break;
case 6://6为中部居右
$posX = $ground_w – $w;
$posY = ($ground_h – $h) / 2;
break;
case 7://7为底端居左
$posX = 0;
$posY = $ground_h – $h;
break;
case 8://8为底端居中
$posX = ($ground_w – $w) / 2;
$posY = $ground_h – $h;
break;
case 9://9为底端居右
$posX = $ground_w – $w;
$posY = $ground_h – $h;
break;
default://随机
$posX = rand(0,($ground_w – $w));
$posY = rand(0,($ground_h – $h));
break;
}

//设定图像的混色模式
imagealphablending($ground_im, true);

if($isWaterImage)//图片水印
{
imagecopy($ground_im, $water_im, $posX, $posY, 0, 0, $water_w,$water_h);//拷贝水印到目标文件
}
else//文字水印
{
if( !empty($textColor) && (strlen($textColor)==7) )
{
$R = hexdec(substr($textColor,1,2));
$G = hexdec(substr($textColor,3,2));
$B = hexdec(substr($textColor,5));
}
else
{
die(“水印文字颜色格式不正确!”);
}
imagestring ( $ground_im, $textFont, $posX, $posY, $waterText, imagecolorallocate($ground_im, $R, $G, $B));
}

//生成水印后的图片
@unlink($groundImage);
switch($ground_info[2])//取得背景图片的格式
{
case 1:imagegif($ground_im,$groundImage);break;
case 2:imagejpeg($ground_im,$groundImage);break;
case 3:imagepng($ground_im,$groundImage);break;
default:die($errorMsg);
}

//释放内存
if(isset($water_info)) unset($water_info);
if(isset($water_im)) imagedestroy($water_im);
unset($ground_info);
imagedestroy($ground_im);
}

//—————————————————————————————

if(isset($_FILES) && !empty($_FILES['userfile']) && $_FILES['userfile']['size']>0)
{
$uploadfile = “./”.time().”_”.$_FILES['userfile']['name'];
if (copy($_FILES['userfile']['tmp_name'], $uploadfile))
{
echo “OK<br>”;

//文字水印
imageWaterMark($uploadfile,0,”",”HTTP://BLOG.CSDN.NET/LONGWARE/”,5,”#FF0000″);

//图片水印
//$waterImage=”./Hanweb_shuiyin.gif”;//水印图片路径
//imageWaterMark($uploadfile,0,$waterImage);

echo “<img src=\”".$uploadfile.”\” border=\”0\”>”;
}
else
{
echo “Fail<br>”;
}
}
?>
<form enctype=”multipart/form-data” method=”POST”>
文件: <input name=”userfile” type=”file”>
<input type=”submit” value=”上传”>
</form>

pdo的使用

PHP 5.1 发布时附带一个全新的数据库连接层PDO(PHP Data Objects)。它与ADODB和Pear DB等数据库抽象层不同,它提供的是如何存取数据库和处理查询结果,效率也更高,还可以通过预处理语句来防止sql注入。
目前支持的数据库:
• DBLIB: FreeTDS / Microsoft SQL Server / Sybase
• Firebird (http://firebird.sourceforge.net/): Firebird/Interbase 6
• MYSQL (http://www.mysql.com/): MySQL 3.x/4.x
• OCI (http://www.oracle.com): Oracle Call Interface
• ODBC: ODBC v3 (IBM DB2 and unixODBC)
• PGSQL (http://www.postgresql.org/): PostgreSQL
• SQLITE (http://www.postgresql.org/): SQLite 3 and SQLite 2
1. 连接数据库
PDO提供了统一的接口:PDO对象。
$db=new PDO(
“driver_name:dbname=db_name;host=hostname/IP;[charset=char_type]“,   //(1)连接字符串
“db_username”,  //(2)db用户名
“db_password”   //(3)db密码
);
说明,PDO有三个参数
(1)连接字符串:
driver_name是使用的PDO驱动,可以为:mysql, mssql, sybase, dblib, firebird, oci, odbc, pgsql, sqlite, sqlite2;
db_name是数据库名称;
hostname/IP是指要连接到哪里,如果是本地则为localhost。
[charset=char_type]是可选的,用来设置字符类型。
(2)db用户名
(3)db密码
example:
$db = new PDO(
“pgsql:dbname=pdo;host=localhost;charset=utf-8″,
“postgres8″,
“postgres8″
);
echo “Successfully created a PDO object”;
?>
连接之前,应先确认已经加载了PDO模块。如果试图处理一个无效的连接字符串:
$db = new PDO(
“this_is_not_a_pdo_module:dbname=pdo;host=localhost”,
“foo”,
“bar”
);
echo “Successfully created a PDO object”;
?>
PHP将会返回以下错误:
Fatal error: Uncaught exception ‘PDOException’ with message ‘could not find driver’
所以,我们可以用一种优雅的方式来处理,即抛出PDO异常来处理错误(但并不是所有情况都是)。
try
{
$db = new PDO(
“this_is_not_a_pdo_modul:dbname=pdo;host=localhost”,
“postgres8″,
“postgres8″
);
}
catch( PDOException $e )
{
die( $e->getMessage() );
}
echo “Successfully created a PDO object”;
?>
我们会得到
could not find driver

SQLSTATE[HY000] [7] FATAL: database “pdo2″ does not exist
如果数据库不存在(不同的错误会返回不同的提示信息)。
2. 设置属性
1) PDO有三种错误处理方式:
• PDO::ERRMODE_SILENT不显示错误信息,只设置错误码
• PDO::ERRMODE_WARNING显示警告错
• PDO::ERRMODE_EXCEPTION抛出异常
可通过以下语句来设置错误处理方式为抛出异常
$db->setAttribute(PDO::ATTR_ERRMODE, );
example:
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
当设置为PDO::ERRMODE_SILENT时可以通过调用errorCode() 或errorInfo()来获得错误信息,当然其他情
况下也可以。
2) 因为不同数据库对返回的字段名称大小写处理不同,所以PDO提供了PDO::ATTR_CASE设置项(包括
PDO::CASE_LOWER,PDO::CASE_NATURAL,PDO::CASE_UPPER),来确定返回的字段名称的大小写。
3) 通过设置PDO::ATTR_ORACLE_NULLS类型(包括PDO::NULL_NATURAL,PDO::NULL_EMPTY_STRING,
PDO::NULL_TO_STRING)来指定数据库返回的NULL值在php中对应的数值。
3. 查询
为了说明清楚,我们这里定义了一个RSS feeds表
id integer
name character varying(100)
url varharacter varying(255)
feed character varying(255)
不使用预处理语句的方式:
$stmt=$db->query(“select * from feeds”);
while($f=$stmt->fetch()){
echo $f["id"].” “;
echo $f["name"].” “;
echo $f["url"].” “;
echo $f["feed"].”
“;
};
/*使用foreach 获取数据
foreach( $db->query( “SELECT * FROM feeds” ) as $row )
{
print_r( $row );
}
*/
?>
得到以下结果:
Array
(
[id] => 1
[0] => 1
[name] => Planet-PHP
[1] => Planet-PHP
[url] => http://www.planet-php.net
[2] => http://www.planet-php.net
[feed] => http://www.planet-php.net/rdf/
[3] => http://www.planet-php.net/rdf/
)
(只显示已行为了节省空间)
使用预处理语句的方式:
$stmt = $db->prepare( “SELECT * FROM feeds” );
$stmt->execute();
print_r( $stmt->fetch() );
?>
这里,$stmt是一个PDOStatement对象,预处理之后会得到这样一个对象,必须execute后才起作用。
fetch函数只提取了一行数据,如果需要读取全部数据,可换成fetchAll函数。
绑定数据
$stmt = $db->prepare( “SELECT * FROM feeds WHERE url = :url” );
$url = “http://www.planet-php.net”;
$stmt->bindParam( “:url”, $url );
$stmt->execute();
while( $row = $stmt->fetch() )
{
print_r( $row );
}
?>
这里,bindParam将$url变量绑定到了:url域,执行时会自动将改变量载入。
插入数据
$stmt = $db->prepare(
“INSERT INTO feeds
( name, url, feed )
VALUES
( :name, :url, :feed )”
);
$stmt->execute(
array(
“:name” => “Planeti Apache”,
“:url” => “http://www.planetapache.org”,
“:feed” => “http://www.planetapache.org/rss10.xml”
)
);
?>
实现插入数据也可以像绑定数据一样来quote数据,这里给出通过在execute中给定输入参数来自动quote
的方法。
事务处理
$dbh->beginTransaction();
try {
$dbh->query(“UPDATE …”);
$dbh->query(“UPDATE …”);
$dbh->commit();
} catch (Exception $e) {
$dbh->rollBack();
}
如果数据库支持事务处理,调用beginTransaction的同时将数据库设置为非自动提交,commit或rollBack
返回自动提交状态。
4. 存储过程
$stmt = $dbh->prepare(“CALL sp_set_string(?)”);
$stmt->bindParam(1, $str);
$str = ‘foo’;
$stmt->execute();
于先前的例子差不多,只是这里使用了“?”数据绑定方法,sp_set_string是存储过程名称。
带有输出参数的存储过程
$stmt = $dbh->prepare(“CALL sp_get_string(?)”);
$stmt->bindParam(1, $ret,PDO:ARAM_STR, 4000);
if ($stmt->execute()) {
echo “Got $ret\n”;
}
绑定列输出
$stmt = $dbh->prepare(“SELECT extension, name from CREDITS”);
if ($stmt->execute()) {
$stmt->bindColumn(‘extension’, $extension);
$stmt->bindColumn(‘name’, $name);
while ($stmt->fetch(PDO::FETCH_BOUND)) {
echo “Extension: $extension\n”;
echo “Author: $name\n”;
}
}