Category Archives: Php

php实现简单的“分享”功能

看到人人和facebook都有一个分享的功能。
就是输入一个url,然后返回对方的title名称和很多该页面上的图片,操作者选择之后就提交到后端:
如我在renren上分享链接 http://www.dyee.org/bbs/thread-182935-1-2.html
会跳转到这样的页面:

其中,红色区域是获得的网页title,而点击绿色部分就可以切换所要使用的图片。
我写的后端代码如下:
<?php
set_time_limit(0);
$url = trim($_REQUEST['url']);
if($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//curl_exec($ch);
$output = curl_exec($ch);
//$c_info = curl_getinfo($ch);
//print_r($c_info);
curl_close($ch);
preg_match(”/charset=([^\"]+)\”/i”,$output, $matches);
//print_r($matches);
$code = $matches[1];
echo ‘<p>编码:’.$code.’</p>’;

preg_match(”/<title>([^<]+)</i”,$output, $matches);
//print_r($matches);
$title = mb_convert_encoding($matches[1],’utf-8′,$code);
echo ‘<p>标题:’.$title.’</p>’;

//$output=str_replace(”\””,”\n”,$output);
//$output=str_replace(”\’”,”\n”,$output);

//preg_match_all(”/src=[\"|']{0,1}(.*\.(gif|jpg|jpeg|png))/i”,$output, $imgs);
//preg_match_all(”/src=[\"|']{0,1}(([^\.]+)\.(gif|jpg|jpeg|png))/i”,$output, $imgs);
//preg_match_all(’/<img.*?(?: |\\t|\\r|\\n)?src=[\'"]?(.+?)[\'"]?(?:(?: |\\t|\\r|\\n)+.*?)/sim’, $output, $imgs, [...]

php效率优化[z]

1.在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情况下,尽量用file_get_contents,因为他的效率高得多!但是要注意file_get_contents在打开一个URL文件时候的PHP版本问题;
file_get_contents与curl:
手册上说file_get_contents是用来将文件的内容读入到一个字符串中的首选方法。针对本地文件读取,尽量用它。
针对网络文件读取,还是得curl:
function curl_file_get_contents($durl){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $durl);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_USERAGENT, _USERAGENT_);
curl_setopt($ch, CURLOPT_REFERER,_REFERER_);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$r = curl_exec($ch);
curl_close($ch);
return $r;
}
2.尽量的少进行文件操作,虽然PHP的文件操作效率也不低的;
文件?数据库?内存?
对一个事物,我们不能一棍子打死,上面的话有点太武断。确实在访问量大的时候直接去硬盘上取数据会使I/O消耗大,mangafox前一版中就有明显感觉。但是,难道放在数据库里面去读就没关系?肯定也是撑不住的。
量小,放哪都一样;量大,就得想尽办法放到内存中,memcached说白了就是来解决这类问题的。
另外,排除安全性考虑,linux下/dev/shm/这个目录就是共享内存,也就是你可以用file_get_contents和file_put_contents来操作/dev/shm/下的文件,而这些文件其实是放到内存里的。
3.优化Select SQL语句,在可能的情况下尽量少的进行Insert、Update操作(在update上,我被恶批过);
这条基本上算不上技巧,能用一条为啥用两条?不过,话说回来,有时候分开多条,不一定效率低。
4.尽可能的使用PHP内部函数(但是我却为了找个PHP里面不存在的函数,浪费了本可以写出一个自定义函数的时间,经验问题啊!);
但是,第一个问题中curl_file_get_contents()这个自定义函数,就是比file_get_contents()效率高,不信你试试。
针对该用哪个函数,实做的时候查查php.net,都会有比较详细的介绍和例程,值得注意的是:少用别名。
5.循环内部不要声明变量,尤其是大变量、对象;
有理,不过只能说尽量吧~
6.多维数组尽量不要循环嵌套赋值;
其实,php的数组太方便,但是也效率太低,比python什么的差远了,但是,如果必要,还真没别的办法~
7.在可以用PHP内部字符串操作函数的情况下,不要用正则表达式;
这要看具体是啥函数,其实很多php内部函数也就是包了个正则~
8.foreach效率更高,尽量用foreach代替while和for循环;
支持~但要注意,以前的for循环用久了,可能会少写一行判断if(is_array($rs))来判断将被foreach的变量是否为数组。
9.用单引号替代双引号引用字符串;
知道为啥么?因为双引号来包裹字符,每次解析的时候都要做一次转义操作,而单引号会直接负值。
10.“用i+=1代替i=i+1。符合c/c++的习惯,效率还高”;
针对这个~ 好像没人做过基准测试吧?不好评价
11.对global变量,应该用完就unset()掉;
这我还真没试过~
静态调用的成员一定要定义成 static  (PHP5 ONLY)
贴士:PHP 5 引入了静态成员的概念,作用和 PHP 4 的函数内部静态变量一致,但前者是作为类的成员来使用。静态变量和 Ruby 的类变量(class variable)差不多,所有类的实例共享同一个静态变量。
<?php
class foo {
function bar() {
echo ‘foobar’;
}
}
$foo = new foo;
// instance way
$foo->bar();
// static way
foo::bar();
?>
静态地调用非 static 成员,效率会比静态地调用 static 成员慢 50-60%。主要是因为前者会产生 E_STRICT 警告,内部也需要做转换。
使用类常量 (PHP5 ONLY)
PHP 5 新功能,类似于 C++ 的 const。
使用类常量的好处是:
- [...]

php与mongoDB的update操作

因为mongoDB的update方法有很多与no-sql不同的地方,所以把它单独拿出来讨论。
php的mongoDB之update方法中
只谈到了php方法和语法,
http://www.php.net/manual/en/mongocollection.update.php
但这是远远不够的。
mongoDB的官方手册中:
只提到了command方式的写法
http://www.mongodb.org/display/DOCS/Updating
没有php的具体实做方法。
在学习和实践中,要把两个手册结合起来用。
$inc
如果记录的该节点存在,让该节点的数值加N;如果该节点不存在,让该节点值等于N
设结构记录结构为 array(’a’=>1,’b’=>’t’),想让a加5,那么:
$coll->update(
array(’b’=>’t’),
array(’$inc’=>array(’a’=>5)),

)
$set
让某节点等于给定值
设结构记录结构为 array(’a’=>1,’b’=>’t’),b为加f,那么:
$coll->update(
array(’a’=>1),
array(’$set’=>array(’b’=>’f’)),

)
$unset
删除某节点
设记录结构为 array(’a’=>1,’b’=>’t’),想删除b节点,那么:
$coll->update(
array(’a’=>1),
array(’$unset’=>’b’),

)
$push
如果对应节点是个数组,就附加一个新的值上去;不存在,就创建这个数组,并附加一个值在这个数组上;如果该节点不是数组,返回错误。
设记录结构为array(’a’=>array(0=>’haha’),’b’=>1),想附加新数据到节点a,那么:
$coll->update(
array(’b’=>1),
array(’$push’=>array(’a’=>’wow’)),

)
这样,该记录就会成为:array(’a’=>array(0=>’haha’,1=>’wow’),’b’=>1)
$pushAll
与$push类似,只是会一次附加多个数值到某节点
$addToSet
如果该阶段的数组中没有某值,就添加之
设记录结构为array(’a’=>array(0=>’haha’),’b’=>1),如果想附加新的数据到该节点a,那么:
$coll->update(
array(’b’=>1),
array(’$addToSet’=>array(’a’=>’wow’)),

)
如果在a节点中已经有了wow,那么就不会再添加新的,如果没有,就会为该节点添加新的item——wow。
$pop
设该记录为 array(’a’=>array(0=>’haha’,1=>’wow’),’b’=>1)
删除某数组节点的最后一个元素:
$coll->update(
array(’b’=>1),
array(’$pop=>array(’a’=>1)),

)
删除某数组阶段的第一个元素
$coll->update(
array(’b’=>1),
array(’$pop=>array(’a’=>-1)),

)
$pull
如果该节点是个数组,那么删除其值为value的子项,如果不是数组,会返回一个错误。
设该记录为 array(’a’=>array(0=>’haha’,1=>’wow’),’b’=>1),想要删除a中value为haha的子项:
$coll->update(
array(’b’=>1),
array(’$pull=>array(’a’=>’haha’)),

)
结果为: array(’a’=>array(0=>’wow’),’b’=>1)
$pullAll
与$pull类似,只是可以删除一组符合条件的记录。

不断变化的mongoDB结果集

在前几天的一次php+mongoDB数据库实做中,遇到了一个很奇怪的问题:
有N张collection,每个collection中有Mn条记录,我先循环N,去每张collection中find()到所有记录,然后在针对每条记录做update()操作,
$mo = new Mongo();
$db =  $mo->dbname;
for($i=0;$i<100;$i++){
$coll = $db->selectCollection(’col’.$i){
$cursor = $coll->find();
while($cursor as $k=>$v){
$uid = $v['uid'];

$rs = $coll->update(array(’uid’=>$uid),array(要更新的内容));
}
}
}
但是实际上,有些记录被update的两次,百思不得其解,update()的$option换了所有方式,都无效。
后来,在Sam的帮助下,详细查了php手册的mongoDB一段,最后发现:
http://www.php.net/manual/en/mongocursor.snapshot.php
MongoCursor MongoCursor::snapshot()
Use snapshot mode for the query. Snapshot mode assures no duplicates are returned, or objects missed, which were present at both the start and end of the query’s execution (if an object is new during the query, or deleted [...]

用php实现mongoDB的基本操作

说到php连mongoDB,不得不先介绍一下php的官方手册,网址在:http://us.php.net/manual/en/book.mongo.php
在php的mongo扩展中,提供了4类接口(对象):
1,针对mongoDB连接的操作:Mongo
http://us.php.net/manual/en/class.mongo.php
2,针对mongoDB中数据库的操作:MongoDB
http://us.php.net/manual/en/class.mongodb.php
3,针对mongoDB中collection的操作:MongoCollection
http://us.php.net/manual/en/class.mongocollection.php
4,针对查询结果集的操作:MongoCursor
http://us.php.net/manual/en/class.mongocursor.php
与mongoDB建立连接:
直接实例化mongo类+创建连接:
$mo = new Mongo();//得到一个Mongo连接对象
实例化了一个Mongo类,并且与默认的localhost:27017端口的mongoDB建立连接。
如果想连接到其他的主机,可以这样写:$mongo = new Mongo(”mongodb://username:password@192.168.1.22:12345″);
另外一种方式,实例化mongo类,再手动建立连接:
$mongo = new Mongo(”mongodb://username:password@192.168.1.22:12345″,array(’connect’=>false));//初始化类
$mongo->connect();//创建连接

Mongo类中有用的一些方法:
Mongo::listDBs()
http://us.php.net/manual/en/mongo.listdbs.php
返回一个包含当前mongo服务上的库(DB)信息的数组。
$mo = new Mongo();
$dbs = $mo->listDBs();//获得一个包含db信息的数组
Mongo::selectCollection($db,$coll)
http://us.php.net/manual/en/mongo.selectcollection.php
返回一个当前连接下的某db中的collection对象。
$mo = new Mongo();
$coll = $mo->selectCollection(’db’,’mycoll’);//得到一个collection对象

选择想要的数据库(Mongo类):
一种方式:
http://us.php.net/manual/en/mongo.get.php
$mongo = new Mongo();

$db = $mongo->foo;//得到一个MongoDB对象

另一种方式:
http://us.php.net/manual/en/mongo.selectdb.php
$mongo = new Mongo();

$db = $mongo->selectDB(’foo’);//得到一个MongoDB对象
MongoDB中有用的函数:
创建一个MongoDB对象
http://us.php.net/manual/en/mongodb.construct.php
$mo = new Mongo();
$db = new MongoDB($mo,’dbname’);//通过创建方式获得一个MongoDB对象
删除当前DB
http://us.php.net/manual/en/mongodb.drop.php
$db = $mo->dbname;
$db->drop();
获得当前数据库名
http://us.php.net/manual/en/mongodb.–tostring.php
$db = $mo->dbname;
$db->_tostring();
选择想要的collection:
A:
$mo = new Mongo();
$coll = $mo->dbname->collname;//获得一个collection对象
B:
$db = $mo->selectDB(’dbname’);
$coll = $db->collname;
C:
$db = $mo->dbname;
$coll = $db->selectCollectoin(’collname’);//获得一个collection对象
插入数据(MongoCollection对象):
http://us.php.net/manual/en/mongocollection.insert.php
MongoCollection::insert(array $a,array [...]

mongoDB的特性和php-mongoDB环境的搭建

mongoDB是目前比较流行的非关系型数据库(no-sql),对于网上吹嘘的性能、吞吐、并发等大家可以自己来摸索和体会,这里不复述。
说说mongoDB一些开发级的特性吧:
1,mongo没有两表连查,相比mysql就没有 left join 或者 select … from a,b这样的操作了;
2,mongo不支持事务、存储过程等,所以用mongo做安全和稳定性高的应用不太现实;
3,mongo没有建表或者维护表结构的概念:
某个库在第一次写入操作时自动创建
某个表(mysql叫做table,mongo叫做collection)在第一次写入时自动创建
同一个collection中的每条记录的结构可以完全不同
mongoDB是有索引概念的,可以建立index和Unique类型的索引
mongoDB中collection的结构:
你不能把collection惯性的想象成一个二维表(table),因为它与sql数据库中的table有很多的不同。
mongo的collection你可以想象成一个笔记本,每一页就是一条记录,而每一页上所记录的东西就是一个节点可以完全不同的数组(array)。mongoDB在系统层面为每张collection的每条记录自动创建唯一索引——_id来检索和定位记录。
以下,我总结了collection的用法,抛砖引玉:
1,名片式:
用来存储不能完全定义出结构和节点的信息。
一张collection是一个笔记本,每一页记录一个人的名片,基本上来讲名片的结构可能是:姓名,公司名,网址,电话,Email。
有些人只会有“姓名”,“公司名”,其他的项目没有,这在mysql中也好实现,没有的项目就是null么,但是还有个可能性是,有些人要有“别 名”、“手机”、“QQ号码”等,这如果在mysql中来说,就要为这张表添加字段才可以,如果这些属性一再多下去,那就得分表连查了。而在mongo 中,只需要在insert或者update的时候为对应记录增加节点就可以了,也就是说可能A的那一页有“姓名”,“公司名”,而B的那一页可能是“姓 名”“公司名”“年龄”“QQ号”“出生地”…
这样一来,将同一类型的数据都放在了一个collection但有保持了每个个体内容的弹性。
2,菜单式:
这种方式比较适合存储配置性的信息。
请抬头看一下你目前浏览器的菜单:我用的是firefox浏览器,现在的菜单是:文件、编辑、查看、历史、书签、工具、帮助。
其中,“查看”的子菜单是:工具栏(含菜单栏、导航工具栏、书签工具栏…),状态栏,侧栏(含历史、书签)…
每个菜单项都还有自己的子项、子子项,但是他们之间又没有一致性。
我们可以用collection的每一条记录存储一个菜单项的全部内容,而我想操作“文件”的内容的时候,可以直接选择type为“文件”的记录来操作。
3,表格式:
这种方式和传统的mysql表很类似,用来存储完整性强,查询和排序条件较多的记录。
mongoDB的介绍
官方网站:http://www.mongodb.org/
官方开发者手册:http://www.mongodb.org/display/DOCS/Developer+Zone
官方管理员手册:http://www.mongodb.org/display/DOCS/Admin+Zone
官方中文手册:http://www.mongodb.org/display/DOCSCN/Home (不够及时)
什么是mongodb:http://baike.baidu.com/view/3385614.htm (百度百科)
mongoDB的安装(win)
下载想要安装的mongoDB的对应win版本:
http://www.mongodb.org/downloads
mongoDB在win下算是个绿色软件,不用一下一下的点Next,下载对应版本后:
1,将内容解压到你想要放置的对应目录,如:d:\mongodb\
2,建立一个目录,用来放数据,如d:\mongodata\
3,将mongoDB安装为windows服务:
cmd下   d:\mongodb\bin\mongod.exe –dbpath=d:\mongodata –install
之后你就可以在“服务”里面找到mongodb,关闭或者启动它。
可用mongod -help查看配置参数
官方说明书:http://www.mongodb.org/display/DOCS/Quickstart+Windows
让php支持mongoDB
php连接mongodb是个很简单的事情,官方文档在:http://www.mongodb.org/display/DOCS/PHP+Language+Center
1,下载对应的库文件
http://github.com/mongodb/mongo-php-driver/downloads 到这里选择适合你系统的dll文件(vc6是apache用户的,vc9是IIS用的)。
2,zip包中有个php_mongo.dll,放到你对应php的扩展目录,例如我的目录在D:\LAMP\php\ext
3,在你的php.ini中加入:extension=php_mongo.dll
4,重启apache,在phpinfo()中查看是否有一项关于mongo的~
如果出现关于mongo的清单,说明当前环境的php已经支持Mongodb了。如果没有,需要检查一下对应的php_mongo.dll是否与你当前的php版本对应,或者查看一下你的php扩展目录配置是否有效。

Gearman:php的负载均衡与分布式应用

什么是Gearman?
http://blog.s135.com/dips/
http://baike.baidu.com/view/2926980.htm
Gearman与php:
http://www.php.net/manual/en/book.gearman.php

一些简单队列知识

最近一直在找一些类似简单队列的解决方案,朋友说用C直接写个本地的IPC来解决,但是目前C对我来说太难了,所以收集了一下资料:
Web应用中为什么会需要消息队列?主要原因是由于在高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达mysql,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。通过使用消息队列,我们可以异步处理请求,从而缓解系统的压力。在Web2.0的时代,高并发的情况越来越常见,从而使消息队列有成为居家必备的趋势,相应的也涌现出了很多实现方案,像Twitter以前就使用RabbitMQ实现消息队列服务,现在又转而使用Kestrel来实现消息队列服务,此外还有很多其他的选择,比如说:ActiveMQ,ZeroMQ等。上述消息队列的软件中,大多为了实现AMQP,STOMP,XMPP之类的协议,变得极其重量级,但在很多Web应用中的实际情况是:我们只是想找到一个缓解高并发请求的解决方案,不需要杂七杂八的功能,一个轻量级的消息队列实现方式才是我们真正需要的。第一感觉是能不能使用memcached来实现消息队列?稍加考虑后就会发现它不合适,因为memcached仅仅支持键值方式的操作,没有排序之类的功能,所以如果要用它来实现消息队列,则必须自己通过某个键来保存数组形式的队列,不过这样的话,在操作队列的时候很容易丢失数据,比如说我们要添加一个消息,则需先取出现有队列,然后把消息保存到队列尾部,最后保存队列,单纯使用memcached的话,由于我们无法保证整个过程的原子性,所以当处理若干个并发请求时,各个请求间可能会互相覆盖,丢失数据就在所难免(新的memcached扩展一定程度上能缓解这个问题)。另外,memcached只是内存键值缓存而已,一旦宕机,数据就消失了。memcacheq的出现解决了上面的问题,它在memcached的基础上实现了消息队列,以php客户端为例:消息从尾部入栈:memcache_set消息从头部出栈:memcache_getmemcacheq依附于memcached之上,所以你可以通过现有的memcached工具来操作它,这无疑是它的一大优势,但它也有一个很大的缺点,那就是memcacheq本身的开发维护似乎并不活跃,如果遇到问题的话,你很可能需要自己动手解决。目前看来,我更推荐下面这种解决方案,那就是redis,如果不了解,可以参考我以前的文章,表面上看,redis和memcached差不多,也是键值操作,但是redis本身实现了list,相关操作也可以保证是原子的,所以可以很自然的通过list来实现消息队列:消息从尾部进队列:RPUSH消息从头部出队列:LPOPredis本身虽然是一个新项目,但很有朝气,开发维护也很活跃,如果你的下一个Web应用里需要使用轻量级的消息队列,不妨使用它,顺便说一句,redis里还有set结构,可以用来实现一个高效能的tag系统。此外,还有不少其他的选择可供尝试,比如说MySQL第三方的Q4M引擎,通过扩展SQL语法来操作消息队列,也是一个不错的选择。

phpdocument 的bug fix

我在windows上用的phpdoc1.4.3的 下载版,生成的文档有报错:Smarty error: unable to read resource: “pkgelementindex.tpl”,而且没有css,png图标也看不到,在这里发现smarty模板文件的后缀名有错误,本来应该是.tpl,可是有部分文件的后缀为tp,所以引发报 错。其实不光模板文件后缀有误,很多css和png后缀名为cs和pn,找来一段改文件名的批处理,把下面的代码保存为bat,
@echo off
for /r %%a in (*.cs) do ren “%%a” “%%~na.css”
for /r %%a in (*.pn) do ren “%%a” “%%~na.png”
for /r %%a in (*.tp) do ren “%%a” “%%~na.tpl”
pause
放在phpdoc目录下,运行既可以把错误的后缀改过来。

PHP与MYSQL的存储过程(很全面,转)

http://blog.csdn.net/binger819623/archive/2010/03/15/5382300.aspx
实例一:无参的存储过程
$conn = mysql_connect(’localhost’,’root’,’root’) or die (”数据连接错误!!!”);
mysql_select_db(’test’,$conn);
$sql = ”
create procedure myproce()
begin
INSERT INTO user (id, username, sex) VALUES (NULL, ’s’, ‘0′);
end;
“;
mysql_query($sql);// 创建一个myproce的存储过程
$sql = “call test.myproce();”;
mysql_query($sql);// 调用myproce的存储过程,则数据库中将增加一条新记录。
实例二:传入参数的存储过程
$sql = ”
create procedure myproce2(in score int)
begin
if score >= 60 then
select ‘pass’;
else
select ‘no’;
end if;
end;
“;
mysql_query($sql);// 创建一个myproce2的存储过程
$sql = “call test.myproce2(70);”;
mysql_query($sql);// 调用myproce2的存储过程,看不到效果,可以在cmd下看到结果。
实例三:传出参数的存储过程
$sql = ”
create procedure myproce3(out score int)
begin
set score=100;
end;
“;
mysql_query($sql);// [...]