{ yeah : 必须哒 } No place to place should record our youth?

11Jul/110

centos下采用yum升级php5.2

Posted by ofeng

# rpm --import http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka

# vi /etc/yum.repos.d/CentOS-Base.repo 增加下面信息

[utterramblings]

name=Jason's Utter Ramblings Repo
baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/
enabled=1
gpgcheck=1
gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
执行命令,自动升级。
yum update php -y
yum install libmcrypt -y

Tagged as: , , No Comments
23Mar/111

ImageMagick 的php扩展 imagick段错误解决

Posted by alacner

imagick 是 PHP 下针对 ImageMagick 这个强大软件包的 API 接口,如果你在编译 ImageMagick 的时候将 IMAGEMAGICK_JPEG2000 编译进去了,你的 PHP 在启用 MagickWand 模块后会发生段错误,无法正常使用 PHP。

解决方法也就显而易见,在通过 源码安装的时候加上配置 --without-jp2,即:
./configure --without-jp2,安装即可。

21Jan/110

【转】php加速 PHP APC 浅析

Posted by alacner

PHP APC提供两种缓存功能,即缓存Opcode(目标文件),我们称之为apc_compiler_cache。同时它还提供一些接口用于PHP开发人员将用户数据驻留在内存中,我们称之为apc_user_cache。我们这里主要控讨php-apc的配置。

安装PHP APC

作为测试环境,我们这里使用的是CentOS5.3(2.6.18-128.el5PAE) + Apache2.0(prefork) + php5.2。我们可以去pecl apc下载APC-3.0.19.tgz

[cc lang='bash']
# tar -xzvf APC-3.0.19.tgz
#cd APC-3.0.19
# /usr/bin/phpize
# ./configure --enable-apc --enable-mmap --enable-apc-spinlocks --disable-apc-pthreadmutex
#make
#make install
[/cc]
注意:我们这里支持mmap,同时采用spinlocks自旋锁。Spinlocks是Facebook推荐使用,同时也是APC开发者推荐使用的锁机制。

PHP APC 配置参数

如果你使用的系统环境跟我的测试环境是一样的话,可以在/etc/php.d目录下创建文件apc.ini,并且相关配置写入/etc/php.d/apc.ini文件。这里,我们挑了一些常用到的配置,并进行探讨。把相关的配置放在一起解释。

apc.enabled=1
apc.enabled默认值是1,你可设成0禁用APC。如果你设置为0的时候,同样把extension=apc.so也注释掉(这样可以节约内存资源)。一旦启用了APC功能,则会缓存Opcodes到共享内存。

APC既然把数据缓存在内存里面,我们就有必要对它进行内存资源限定。通过这二个配置可以限定APC可以使用的内存空间大小。apc.shm_segments指定了使用共享内存块数,而apc.shm_size则指定了一块共享内存空间大小,单位是M。所以,允许APC使用的内存大小应该是 apc.shm_segments * apc.shm_size = 30M。你可以调整一块共享内存的大小空间。当然,一块共享内存最大值是受操作系统限制的,即不能超过/proc/sys/kernel/shmmax大小。否则APC创建共享内存的时候,会失败。在apc.shm_size达到了上限的时候,你可以通过设置apc.shm_segments来允许APC使用更多的内存空间。我们推荐,如果调用APC使用内存空间的话,先考滤apc.shm_size,后考滤apc.shm_segments。具体数值,可以根据apc.php监控情况进行规划与调整。值得注意的是,每一次调整需要重启httpd守护进程,这样可以重新加载apc.so模块。跟随着httpd守护进程启动,apc.so模块就会加载。apc.so加载初始化的时候,通过mmap请求分配内存指定大小的内存,即apc.shm_size * apc.shm_segments。而且,这里使用的是匿名内存映射方式,通过映射一个特殊设备/dev/zero,提供一个"大型"的,填满了零的内存供APC管理。
为了验证以上陈述,我们注释掉apc.ini配置,并且写了以下php脚本观察apc.so模块初始化的分配的内存空间。

[cc lang='php']
//@file: apc_load.php
if (!extension_loaded('apc')) {
dl('apc.so'); #加载apc.so模块
echo posix_getpid(); #//输出当前进程的pid,我这里这里输出的是14735
ob_flush();
flush();
sleep(3600); #让进程进入休眠状态.这样,我们可以观察内存分配情况
}
?>
[/cc]
[cc lang='bash']
#strace -p `cat /var/run/httpd.pid`
open("/var/www/html/apc_load.php", O_RDONLY) = 13
...
mmap2(NULL, 31457280, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) = 0xb5ce7000
...
nanosleep({3600, 0},
[/cc]

红色部分,我们可以看出。通过mmap系统内核调用分配了30M(31457280/1024/1024)内存空间。PROT_READ|PROT_WRITE表示该内存空间可供读取与写入。MAP_SHARED表示该内存空间与其它进程是共享的,即其它进程也可以进行读取与写入,我们可以通过apc.php进行管理该块内存空间亦是受益于此设定。MAP_ANONYMOUS则表示匿名映射。其中fd=-1表示忽略,因为这里映射的特殊设备/dev/zero。最后的0表示无偏移量。我们还可以通过进程映像文件查看该块内存的具体情况
[cc lang='bash']
#cat /proc/14735/smaps

b5ce7000-b7ae7000 rw-s 00000000 00:08 633695 /dev/zero (deleted)
Size: 30720 kB
Rss: 44 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 44 kB
[/cc]
可以很容易地发现起始地址0xb5ce7000与上面mmap系统内核调用返回的地址一样。该块内存是可读写rw,并与其它进程共享s。而/dev/zero则是映射文件,该文件节点是633695。其中,size表示进程可以使用的内存空间,而rss则表示实际分配的内存空间,且由Private_Dirty可以看出,实际分配的44kb内存是由当前进程自己分配的。

apc.num_files_hint = 1000
apc.user_entries_hint = 4096

这二配置指定apc可以有多少个缓存条目。apc.num_files_hint说明你估计可能会有多少个文件相应的opcodes需要被缓成,即大约可以有多少个apc_compiler_cache条目。另外apc.user_entries_hint则说明你估计可能会有多少个apc_userdata_cache条目需要被缓存。如果项目中不使用apc_store()缓存用户数据的话,该值可以设定得更小。也就是说apc.num_files_hint与apc.user_entries_hint之和决定了APC允许最大缓存对象条目的数量。准确地设置这二个值可以得到最佳查询性能。当然,如果你不清楚要进行多少缓存(缓存对象实例)的情况下,你可以不必修改这二项配置。
其中apc.user_entries_hint要根据项目实际开发使用了apc_store()条目估计其值大小。相较而言,apc.num_files_hint可以通过find命令,更容易地估计其大小。比如我们的web根目是/var/vhosts,则使用下面的find命令可以大致地统计当前apc.num_files_hint数目.

[cc lang='bash']
#find /var/vhosts \( -name “*.php” -or -name “*.inc” \) -type f -print |wc -l
1442
[/cc]

apc.stat = 1
apc.stat_ctime = 0

这二个参数,只跟apc_compiler_cache缓存相关,并不影响apc_user_cache。我们前面提到过apc_complier_cache,它缓存的对象是php源文件一一对应的opcodes(目标文件)。PHP源文件存放在磁盘设备上,与之相对应的Opcodes目标文件位置内存空间(共享内存),那么当php源文件被修改以后,怎么通知更新内存空间的opcodes呢?每次接收到请求后,APC都会去检查打开的php源文件的最后修改时间,如果文件的最后修改时间与相应的内存空间缓存对象记录的最后修改时间不一致的话,APC则会认为存放在内存空间的Opcode目标文件(缓存对象)已经过期了,acp会将缓存对象清除并且保存新解析得到的Opcode。我们关心的是,即便没有更新任何php源文件,每次接受到http请求后,APC都会请求系统内核调用stat()来获取php源文件最后修改时。我们可以通过将apc.stat设置为0,要求APC不去检查Opcodes相对应的php源文件是否更新了。这样可以获得最佳的性能,我们也推荐这么做。不过,这样做有一点不好的就是,一旦有PHP源文件更新了之后,需要重启httpd守护进程或者调用apc_cache_clear()函数清空APC缓存来保证php源文件与缓存在内存空间的Opcodes相一致。

[cc lang='php']
define('ROOTP', dirname(__FILE__) . '/');
include(ROOTP . 'i1.php');
require(ROOTP . 'i2.php');
include_once(ROOTP . 'i3.php');
require_once(ROOTP . 'i4.php');
require(ROOTP . 'i5.php');
include(ROOTP . 'i6.php');
?>
[/cc]
[cc lang='bash']
# strace -e trace=file -p `cat /var/run/httpd.pid`
getcwd("/var/www/html", 4096) = 14
stat64("/var/www/html/i1.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
stat64("/var/www/html/i2.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/var/www/html/i3.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
open("/var/www/html/i3.php", O_RDONLY) = 12
stat64("/var/www/html/i3.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
lstat64("/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/var/www", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/var/www/html/i4.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
open("/var/www/html/i4.php", O_RDONLY) = 12
stat64("/var/www/html/i4.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
stat64("/var/www/html/i5.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
stat64("/var/www/html/i6.php", {st_mode=S_IFREG|0644, st_size=39, ...}) = 0
chdir("/tmp") = 0

# strace -e trace=file -p `cat /var/run/httpd.pid`
getcwd("/var/www/html", 4096) = 14
open("/var/www/html/i3.php", O_RDONLY) = 12
open("/var/www/html/i4.php", O_RDONLY) = 12
chdir("/tmp") = 0
[/cc]
对比可见,当apc.stat=0时,省了很多系统内核调用,我们没有看到系统内核调用stat64了。其中,i3.php和i4.php分别是php的include_once和require_once函数调用,它要交给fstat()系统内核调用来检查文件是否打开过。单从性能角度出发的话,require比require_once性能更佳。

设置apc.stat_ctime的意义并是很大。如果apc.stat_ctime值为1时,仅当php源文件的创建时间(ctime)大于php源文件的最后修改时间(mtime)时,缓存对象的mtime时间会被php源文件的ctime所代替,否则缓存对象的mtime依然记录为php源文件的mtime。这样做是防止通过cvs, svn或者rsync等工具刷新php源文件的mtime,这样会导致APC通过比对php源文件的创建时间ctime来决定缓存对象有没有过期。我们推荐该保持默认值,即apc.stat_ctime = 0

apc.ttl=0
apc.user_ttl=0

缓存对象的生命周期。其中ttl表示Time To Live,意味着指定时间后缓存对象会被清除。其中0表示永不过期。我们前面提过,APC能缓存的条目是受限定的,如果你把ttl设置永不过期的话,当缓存条目已满或者缓存空间不够,之后的缓存都将失败。
其中apc.ttl作用于apc_compiler_cache。当apc.ttl大于0时,每次请求都会对比这次的请求时间与上一次请求时间之差是不是大于apc.ttl,如果大于apc.ttl,则会被认缓存条目过期了,会被清理。
比较有意思的是apc.user_ttl,它主要作用于apc_user_cache缓存。我们知道,这种类型的缓存是通过apc_store($key, $var, $ttl = 0)创建的缓存对象。函数apc_store()中指定的$ttl与php.ini中设定的apc.user_ttl有什么异同,是我们比较关心的。因为它们同样作用于apc_userdata_cache缓存。经过分析,我们知道:判断apc_user_cache缓存过期的依据是,当apc.user_ttl大于0,且这次http请求时间与上一次http请求时间之差大于apc.user_ttl,则认为相应的缓存条目已过期;或者,user.data.ttl(php函数apc_store()中指定的$ttl)大于0,且这次http请求时间与缓存对象创建时间ctime之差大于user.data.ttl,则同样认为缓存条目已过期,会被清除。
我们推荐,如果你的项目较为稳定,并且apc.stat设置为0。同时apc.shm_size、apc.num_files_hint设置合理的话,apc.ttl建议设置为0。即apc_compiler_cache永不回收,直到重启httpd守护进程或者调用函数apc_cache_clear()清缓存。至于apc.user_ttl,建议设置为0,由开发人员调用apc_store()函数的时候,设置$ttl来指定该缓存对象的生命周期。

apc.slam_defense=0
apc.write_lock=1
apc.file_update_protection=2

之所以把这三个配置放在一起解释,是因为他们的意义很相近。其中apc.file_update_protection最好理解,它的单位是时间单位秒。如果当前http请求时间与php源文件最好修改时间mtime之差小于apc.file_update_protection时间,APC则不会缓存该php源文件与之对应的Opcodes,直到接下来的某次访问,并且访问时间与php源文件的最后修改时间大于apc.file_update_protection时间,相之相应的Opcodes才会被缓存到共享内存空间。这样做的好处是,不容易被用户访问到你正在修改的源文件。我们推荐在开发环境,该值可以设置得更大一点,但在运营环境,我们推荐保留默认值即可。
当你的网站并发量很大的时候,可能出现由http守护进程fork的多个子进程同时缓存同一份Opcodes的情况。通过apc.slam_defense则可以减少这种事情的发生机率。比如,apc.slam_defense值设置为60的时候,当遇到未缓存的Opcodes,每100次有60次是不缓存的。对于并发量不大的网站,我们推荐该值设定为0,对于并发量高的网站我们可以根据统计适当地调整该值。而apc.write_lock是一个布尔值,当该值设置为1的时候,当多个进程同时缓存同一份Opcodes时,仅当最先那个进程缓存有效,其它的无效。通过apc.write_lock设置,有效地避免了缓存写竞争的出现。

apc.max_file_size=1M
apc.filters = NULL
apc.cache_by_default=1

这三个配置放在一起,是因为他们都用于限制缓存。其中apc.max_file_size表示如果php源文件超过了1M,则与之对应的opcodes不被缓存。而apc.filters指定一个文件过滤列表,以逗号(,)隔开。当apc.cache_by_default等于1时,与apc.filters列表中指定的文件名相匹配的文件不会被缓存。相反,apc.cache_by_default等于0时,仅缓存与acp.filters列表中指定的文件相匹配的文件。

总结

1,使用Spinlocks锁机制,能够达到最佳性能。
2,APC提供了apc.php,用于监控与管理APC缓存。不要忘记修改管理员名和密码
3,APC默认通过mmap匿名映射创建共享内存,缓存对象都存放在这块"大型"的内存空间。由APC自行管理该共享内存
4,我们需要通过统计调整apc.shm_size、apc.num_files_hints、apc.user_entries_hint的值。直到最佳
5,好吧,我承认apc.stat = 0 可以获得更佳的性能。要我做什么都可以接受.
6,PHP预定义常量,可以使用apc_define_constants()函数。不过据APC开发者介绍说pecl hidef性能更佳,抛异define吧,它是低效的。
7,函数apc_store(),对于系统设置等PHP变量,生命周期是整个应用(从httpd守护进程直到httpd守护进程关闭),使用APC比Memcached会更好。必竟不要经过网络传输协议tcp。
8,APC不适于通过函数apc_store()缓存频繁变更的用户数据,会出现一些奇异现象。

【转自:http://www.perfgeeks.com/?p=298】

Tagged as: , , No Comments
22Oct/100

php高并发状态下文件的读写

Posted by ofeng

对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!!用一般的文件操作方法完全没有问题。但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失。
例如:一个在线聊天室(这里假定把聊天内容写入文件),在同一时刻,用户A和用户B都要操作数据保存文件,首先是A打开了文件,然后更新里面的数据,但这里B也正好也打开了同一个文件,也准备更新里面的数据。当A把写好的文件保存时,这里其实B已经打开了文件。但当B再把文件保存回去时,这里已经造成了数据的丢失,因为这里B用户完全不知道它所打开的文件在它对其进行更改时,A用户也更改了这个文件,所以最后B用户保存更改时,用户A的更新就被会丢失。
对于这样的问题,一般的解决方案时当一进程对文件进行操作时,首先对其它进行加锁,意味着这里只有该进程有权对文件进行读取,其它进程如果现在读,是完全没有问题,但如果这时有进程试图想对其进行更新,会遭到操作拒绝,先前对文件进行加锁的进程这时如果对文件的更新操作完毕,这就释放独占的标识,这时文件又恢复到了可更改的状态。接下来同理,如果那个进程在操作文件时,文件没有加锁,这时,它就可以放心大胆的对文件进行锁定,独自享用。
所以一般的方案会是:

Php代码

[cc lang='php' ]

$fp = fopen ( "/tmp/lock.txt", "w+" );

if (flock ( $fp, LOCK_EX )) {

fwrite ( $fp, "Write something here\n" );

flock ( $fp, LOCK_UN );

} else {

echo "Couldn't lock the file !";

}

fclose ( $fp );

[/cc]

但在PHP中,flock似乎工作的不是那么好!在多并发情况下,似乎是经常独占资源,不即时释放,或者是根本不释放,造成死锁,从而使服务器的cpu占用很高,甚至有时候会让服务器彻底死掉。好像在很多linux/unix系统中,都会有这样的情况发生。
所以使用flock之前,一定要慎重考虑。
那么就没有解决方案了吗?其实也不是这样的。如果flock()我们使用得当,完全可能解决死锁的问题。当然如果不考虑使用flock()函数,也同样会有很好的解决方案来解决我们的问题。
经过我个人的搜集和总结,大致归纳了解决方案有如下几种。
方案一:对文件进行加锁时,设置一个超时时间.
大致实现如下:

Php代码

[cc lang='php' ]

if ($fp = fopen ( $fileName, 'a' )) {

$startTime = microtime ();

do {

$canWrite = flock ( $fp, LOCK_EX );

if (! $canWrite)

usleep ( round ( rand ( 0, 100 ) * 1000 ) );

} while ( (! $canWrite) && ((microtime () - $startTime) < 1000) );

if ($canWrite) {

fwrite ( $fp, $dataToSave );

}

fclose ( $fp );

}

[/cc]

超时设置为1ms,如果这里时间内没有获得锁,就反复获得,直接获得到对文件操作权为止,当然。如果超时限制已到,就必需马上退出,让出锁让其它进程来进行操作。
方案二:不使用flock函数,借用临时文件来解决读写冲突的问题。
大致原理如下:
1。将需要更新的文件考虑一份到我们的临时文件目录,将文件最后修改时间保存到一个变量,并为这个临时文件取一个随机的,不容易重复的文件名。
2。当对这个临时文件进行更新后,再检测原文件的最后更新时间和先前所保存的时间是否一致。
3。如果最后一次修改时间一致,就将所修改的临时文件重命名到原文件,为了确保文件状态同步更新,所以需要清除一下文件状态。
4。但是,如果最后一次修改时间和先前所保存的一致,这说明在这期间,原文件已经被修改过,这时,需要把临时文件删除,然后返回false,说明文件这时有其它进程在进行操作。
大致实现代码如下:

Php代码

[cc lang='php' ]

<?php

$dir_fileopen = "tmp";

function randomid() {

return time () . substr ( md5 ( microtime () ), 0, rand ( 5, 12 ) );

}

function cfopen($filename, $mode) {

global $dir_fileopen;

clearstatcache ();

do {

$id = md5 ( randomid ( rand (), TRUE ) );

$tempfilename = $dir_fileopen . "/" . $id . md5 ( $filename );

} while ( file_exists ( $tempfilename ) );

if (file_exists ( $filename )) {

$newfile = false;

copy ( $filename, $tempfilename );

} else {

$newfile = true;

}

$fp = fopen ( $tempfilename, $mode );

return $fp ? array ($fp, $filename, $id, @filemtime ( $filename ) ) : false;

}

function cfwrite($fp, $string) {

return fwrite ( $fp [0], $string );

}

function cfclose($fp, $debug = "off") {

global $dir_fileopen;

$success = fclose ( $fp [0] );

clearstatcache ();

$tempfilename = $dir_fileopen . "/" . $fp [2] . md5 ( $fp [1] );

if ((@filemtime ( $fp [1] ) == $fp [3]) || ($fp [4] == true && ! file_exists ( $fp [1] )) || $fp [5] == true) {

rename ( $tempfilename, $fp [1] );

} else {

unlink ( $tempfilename );

//说明有其它进程 在操作目标文件,当前进程被拒绝

$success = false;

}

return $success;

}

$fp = cfopen ( 'lock.txt', 'a+' );

cfwrite ( $fp, "welcome to beijing.\n" );

fclose ( $fp, 'on' );

[/cc]

对于上面的代码所使用的函数,需要说明一下:
1.rename();重命名一个文件或一个目录,该函数其实更像linux里的mv。更新文件或者目录的路径或名字很方便。
但当我在window测试上面代码时,如果新文件名已经存在,会给出一个notice,说当前文件已经存在。但在linux下工作的很好。
2.clearstatcache();清除文件的状态.php将缓存所有文件属性信息,以提供更高的性能,但有时,多进程在对文件进行删除或者更新操作时,php没来得及更新缓存里的文件属性,容易导致访问到最后更新时间不是真实的数据。所以这里需要使用该函数对已保存的缓存进行清除。

方案三:对操作的文件进行随机读写,以降低并发的可能性。
在对用户访问日志进行记录时,这种方案似乎被采用的比较多。
先前需要定义一个随机空间,空间越大,并发的的可能性就越小,这里假设随机读写空间为[1-500],那么我们的日志文件的分布就为log1~到log500不等。每一次用户访问,都将数据随机写到log1~log500之间的任一文件。
在同一时刻,有2个进程进行记录日志,A进程可能是更新的log32文件,而B进程呢?则此时更新的可能就为log399.要知道,如果要让B进程也操作log32,概率基本上为1/500,差不多约等于零。
在需要对访问日志进行分析时,这里我们只需要先将这些日志合并,再进行分析即可。
使用这种方案来记录日志的一个好处时,进程操作排队的可能性比较小,可以使进程很迅速的完成每一次操作。

方案四:将所有要操作的进程放入一个队列中。然后专门放一个服务完成文件操作。
队列中的每一个排除的进程相当于第一个具体的操作,所以第一次我们的服务只需要从队列中取得相当于具体操作事项就可以了,如果这里还有大量的文件操作进程,没关系,排到我们的队列后面即可,只要愿意排,队列的多长都没关系。

对于以前几种方案,各有各的好处!大致可能归纳为两类:
1。需要排队(影响慢)比如方案一、二、四
2。不需要排队。(影响快)方案三
在设计缓存系统时,一般我们不会采用方案三。因为方案三的分析程序和写入程序是不同步的,在写的时间,完全不考虑到时候分析的难度,只管写的行了。试想一下,如我们在更新一个缓存时,如果也采用随机文件读写法,那么在读缓存时似乎会增加很多流程。但采取方案一、二就完全不一样,虽然写的时间需要等待(当获取锁不成功时,会反复获取),但读文件是很方便的。添加缓存的目的就是要减少数据读取瓶颈,从而提高系统性能。

从上为个人经验和一些资料的总结,有什么不对的地方,或者没有谈到的地方,欢迎各位同学指正。

转至:http://num7.javaeye.com/blog/710268

Tagged as: , , No Comments
11Sep/100

PHP5中面向对象部分的特性——abstract抽象、final最终和static静态【转】

Posted by alacner

1.abstract

一. 面向对象程序通过类的分层结构构建起来. 在单重继承语言如PHP中, 类的继承是树状的. 一个根类有一个或更多的子类,再从每个子类继承出一个或更多下一级子类. 当然,可能存在多个根类,用来实现不同的功能. 在一个良好设计的体系中,每个根类都应该有一个有用的接口, 可以被应用代码所使用. 而抽象类就可以提供多个实用的接口.如果你建立了一个只有抽象方法的类,那么你就定义了一个接口(interface).

在抽象类中定义的方法,也都是抽象方法,当然,抽象方法与普通的方法不一样,它只是子类中普通方法的一个占位符(只是占个地主不启作用),没有任何代码,也没有"{}"包含,而是以";"结束的.

  1. abstract class   aaa{
  2. abstract function bbb();
  3. }

当然,如果一个类中有一个或多个抽象的方法,那么这个类就成了抽象类.抽象类不能实例化,只能继承,然后,实例化子类.值得注意的是,它的子类必须覆写所有方法(all method),这样,子类就成为普通的类,才可以实例化.否则,子类中只要有一个抽象方法,那么子类必须声明是抽象类,在class 关键字前加上 abstract.

二.抽象的类的声明方法:

1.使用abstact 限定语.

  1. //定义抽象类aaa为基类
  2. abstract class aaa {
  3. abstract function bbb();
  4. abstract function   ccc();
  5. }
  6. //定义普通类childfromaaa
  7. //继承aaa抽象类
  8. class childfromaaa extends aaa {
  9. //重写所有抽象类中的方法
  10. public function bbb(){
  11. //.....
  12. }
  13. public function ccc(){
  14. //...
  15. ....
  16. }
  17. }
  18. //实例化子类
  19. $child = new childfromaaa;  //right

2. PHP中有interface 和implements关键字. 你可以用interface来代替抽象类, 用implements来代替extends来说明你的类定义或使用一个接口.

  1. //定义抽象基类
  2. interface Foo {
  3. function a(Foo $foo);
  4. }
  5. //定义另一个抽象类
  6. interface Bar {
  7. function b(Bar $bar);
  8. }
  9. //定义子类childfromaaa
  10. //继承aaa抽象类(接口)
  11. //实现多类继承
  12. class FooBar implements Foo, B ar {
  13. function a(Foo $foo) {
  14. // ...
  15. }
  16. function b(Bar $bar) {
  17. // ...
  18. }
  19. }
  20. $a = new FooBar;
  21. $b = new FooBar;
  22. $a->a($b);
  23. $a->b($b);

2.final

final / final关键字
PHP5中新增加了final关键字,它可以加在类或类方法前。标识为final的类方法,在子类中不能被覆写。标识为final的类,不能被继承,而且其中的方法都默认为final类型。
Final方法:
<?php
class Foo {
final function bar() {
// …
}
}
?>
Final类:
<?php
final class Foo {
// class definition
}
// 下面这一行是错误的
// class Bork extends Foo {}
?>

3.static

) static
static关键字在类中是,描述一个成员是静态的,static能够限制外部的访问,因为static后的成员是属于类的,是不属于任何对象实例,其他 类是无法访问的,只对类的实例共享,能一定程序对该成员尽心保护。类的静态变量,非常类似全局变量,能够被所有类的实例共享,类的静态方法也是一样的,类 似于全局函数。类的静态方法能访问类的静态的属性。另外说明的是,static的成员,必须使用self来访问,使用this会出错。
(关于this和self的异同,请参考: http://blog.csdn.net/heiyeshuwu/archive/2004/11/03/165828.aspx )

7Sep/100

PHP PEAR 编码规范中文版

Posted by ofeng

内容列表
缩进
控制结构
函数调用
函数定义
注释
包含代码
PHP 代码标记
头注释块
使用 CVS
例子 URLs
命名约定
--------------------------------------------------------------------------------

缩进
缩进使用4个空格,而不是 tab。如果你使用 Emacs 编辑 PEAR 代码,你应该设置 indent-tabs-mode 为 nil。This helps to avoid problems with diffs, patches, SVN history and annotations.

下面是一个 mode hook 的示例,用于设置 Emacs 符合缩进标准(你必须确保在编辑 PHP 文件时,这些设置发生作用):
(defun php-mode-hook ()
(setq tab-width 4
       c-basic-offset 4
       c-hanging-comment-ender-p nil
       indent-tabs-mode
       (not
        (and (string-match "/\\(PEAR\\|pear\\)/" (buffer-file-name))
             (string-match "\.php$" (buffer-file-name))))))
这里是同等效果的 vim 规则:
set expandtab
set shiftwidth=4
set softtabstop=4
set tabstop=4

It is recommended to keep lines at approximately 75-85 characters long for better code readability. Paul M. Jones has some thoughts about that limit.(居然还有典故)
--------------------------------------------------------------------------------

控制结构
控制结构包含 if、for、while、switch 等。这里有一个 if 语句的示例和一个
switch 语句的示例:
if 语句的示例:
if ((condition1) || (condition2)) { 
    action1;
} elseif ((condition3) && (condition4)) {
    action2;
} else { 
    defaultaction;
}
?>
switch 语句的示例:
switch (condition) {
case 1:
   action1;
   break;
case 2:
   action2;
   break;
default:
   defaultaction;
   break;
}
?>

(switch加个缩进会不会更readable呢)

控制语句应该在控制关键词和开始的圆括号之间应该有一个空格,以此和函数调用进行区别。 强烈建议你总是使用花括号将控制结构各部分标识出来。即使是在技术上可以不使用花括号的地方。这可以增加代码的可读性,同时避免在结构部分增加新行后引入逻辑上的错误。
原始代码:
if (condition)
   return true;
else
   return false;
修改后的代码:
if (condition)
   do something; // 出现逻辑错误
   return true;
else
   return false;
正确的做法:
if (condition) {
   do something;
   return true;
} else {
   return false;
}

如果判断条件比较长

<?php
if (   $condition1
|| $condition2
|| $condition3
) {
//code here
}
?>

<?php
$a = $condition1 && $condition2
? $foo : $bar;
$b = $condition3 && $condition4
? $foo_man_this_is_too_long_what_should_i_do
: $bar;
?>
--------------------------------------------------------------------------------

函数调用
调用函数时,函数名和开始的括号之间不应该有空白字符。参数和开始及结束的括号之间不应有空格。而除第一个参数外,其他参数都应该用一个空格分隔。这里有一个示例:
$var = foo($bar, $baz, $quux);
?>
像上面的示例代码,赋值运算等号两边都应该使用一个空格。如果是相关的赋值运算,应该采用下面的形式以提供更好的可读性:
$short = foo($bar);
$long_variable = foo($baz);
?>
--------------------------------------------------------------------------------
函数定义
按照“one true brace”约定声明函数:
function fooFunction($arg1, $arg2 = ‘’)
{
   if (condition) {
       statement;
   }
   return $val;
}
?>
“one true brace”约定就是开始的花括号单独占一行,而不是跟在其他语句后面。
具有默认值的参数应该位于参数列表的后面(事实上 PHP 语言定义也要求如此)。如果适合,函数应该总是返回一个有意义的值。这里有一个稍微长一点的示例:
function connect(&$dsn, $persistent = false)
{
   if (is_array($dsn)) {
       $dsninfo = &$dsn;
   } else {
       $dsninfo = DB::parseDSN($dsn);
   }
   if (!$dsninfo ||
!$dsninfo[’phptype’]) {
       return $this->raiseError();
   }
   return true;
}
?>
--------------------------------------------------------------------------------

注释
类型(class)的联机文档应该符合 PHPDoc(类似于 JavaDoc)的约定。更多关于 PHPDoc 的信息可以访问 http://www.phpdoc.de/ 获得。
此外,强烈鼓励使用非文档注释。一般性规则是对于那些容易忘记作用的代码添加简短的介绍性注释。
推荐使用 C 样式的注释(/* */)和标准 C++ 注释(//),而不应该使用 Perl/shell 样式的注释(#)。
--------------------------------------------------------------------------------

包含代码
无论在什么地方无条件包含一个类型文件,应该使用 require_once()。如果有条件的包含一个类型文件(例如使用工厂方法),应该使用 include_once()。使用两者中的任何一个都能够确保类型文件只包含一次。它们共享一个文件列表,因此你不需要担心混淆他们
—— 一个文件使用 require_once() 包含后不会在 include_once() 中再一次被包含。
备注:include_once() 和 require_once() 是一个声明,而不是函数。你不需要使用圆括号将文件名扩起来(不过使用括号也不会出现错误)。
--------------------------------------------------------------------------------

PHP 代码标记
总是使用 来界定 PHP 代码,而不要使用 速记方式。这是为了符合
PEAR 一致性所必须的,同时也是在不同操作系统和不同安装设置环境下移植 PHP 代码所要求的。
--------------------------------------------------------------------------------

头注释块
PEAR 发布的所有源代码文件头部都应该包含下面的注释块:
/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */
// +———————————————————————-+
// | PHP version 4                                                        |
// +———————————————————————-+
// | Copyright (c) 1997-2002 The PHP Group                                |
// +———————————————————————-+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +———————————————————————-+
// | Authors: Original Author                        |
// | Your Name                                          |
// +———————————————————————-+
//
// $ Id $
?>
这里没有硬性规定要将一个代码贡献者的名字添加到文件注释的作者列表中。一般情况下,他们的更改属于“substantial”目录(意味大约10%到20%的代码被改写)。有一个例外就是代码贡献者重写了函数或者贡献了新的程序逻辑。
简单的代码重组和 bug 修复不应该增加新作者,这是不恰当的。
不在核心 PEAR 仓库中的文件应该有一个类似的注释块来说明版权、许可协议和作者。所有文件应该包含一个模式行(modeline,用于
vim 和 emacs)以确保一致性。
--------------------------------------------------------------------------------

使用 CVS
这个小节的内容仅仅适用于 cvs.php.net 上使用 CVS 的包。
在每一个文件中包含 $Id $(两个 $ 符号之间的关键字不能够有空格,但由于此文档也是用 CVS 管理,因此只好加个空格,避免被
CVS 替换掉) CVS 关键字,以便查看文件当前状态和最后修改时间等信息。如果已经存在“Last
Modified:”这样的信息,则替换为 $Id $ 标记。
这个小节的其他内容假定你有关于 CVS 标记和分支(branches)的基本知识。
CVS 标记用于标识你包中的文件释放之前所作的修订。下面的列表是必需使用和建议使用的 CVS 标记:
RELEASE_n_n
(必需的)用于标记一个释放版本。如果你没有使用这个标记,别人就没有办法在你发布一个释放版本时从
CVS 服务器获取你的包。
QA_n_n
(分支,可选)如果你觉得在发布释放版本之前要提供一个候选释放版本,那么一个好主意就是增加一个分支。这个分支可以让你将释放版本隔离出来,并在正式发布释放版本之前可以为这些分支单独应用更新。期间,正常的开发工作可以在主干上继续进行。
MAINT_n_n
(分支,可选)如果你需要创建一个较小修改的释放版本(例如在
1.2 之后发布 1.2.1)。那么你可以创建一个分支来达到目的。
仅有 RELEASE 标记是必须的,其他标记为了方便推荐你使用。
下面是一个如何为 "Money_Fast" 包 1.2 释放版本增加标记的示例:
$ cd pear/Money_Fast
$ cvs tag RELEASE_1_2
T Fast.php
T README
T package.xml
经过上面的操作,就可以在 PEAR Web 网站上获取你的一系列释放版本了。
这里是一个如何建立 QA 分支的示例:
$ cvs tag QA_2_0_BP

$ cvs rtag -b -r QA_2_0_BP QA_2_0
$ cvs update -r QA_2_0
$ cvs tag RELEASE_2_0RC1
…and then the actual release, from the same branch:
$ cvs tag RELEASE_2_0
"QA_2_0_BP" 标记是一个 "branch point" 标记,用于标记分支的开始。总是用这样的标记来标明分支的开始是一个好主意。MAINT
分支可以使用 RELEASE 标记作为它的分支起点。
--------------------------------------------------------------------------------

示例中的 URL
所有示例中用到的 URL 地址都应该是 "example.com"、"example.org"、"example.net"。
--------------------------------------------------------------------------------

命名约定
一般而言,class、函数和变量的名字应该总是能够描述让代码阅读者能够容易的知道这些代码的作用。
Classes
class 应该具有一个描述性的名字。可能时应该避免使用缩写。class 名字应该总是用一个大写字母开始。从 class
名字中也能够反映出 PEAR class 的层次。层次中的每个级别都用下划线进行分隔。好的 class 名字示例如下:
Log
Net_Finger
HTML_Upload_Error
函数和方法
函数和方法应该使用 "studly caps" 样式命名。函数应该将所在包的名字作为前缀,以避免与其他包的函数发生名字冲突。名字的受字母(前缀之后)应该是小写,每一个新单词应该以大写字母开头。下面是一些示例:
connect()
getData()
buildSomeWidget()
XML_RPC_serializeData()
私有 class 成员和属性(意味着 class 成员和属性只应该由同一个 class 中声明的成员使用。不过 PHP 并不支持强制性的私有命名空间)应该用一个下划线开头。示例:
_sort()
_initTree()
$this->_status
常数
常数应该总是全部使用大写字母命名,用下划线来分隔单词。常数名字的前缀应该使用大写的 class/包 名字。例如:DB::
包使用的所有常数都已 DB_ 开头。
全局变量
如果你的包需要定义全局变量,那么应该用下划线跟上包的名字和另一个下划线作为开头。例如,PEAR 包使用一个全局变量名为
$_PEAR_destructor_object_list。
预定义的值 true、false 和 null
PHP 的内建值 true、false 和 null 必须全部用小写字母书写。</YOU@EXAMPLE.COM></AUTHOR@EXAMPLE.COM>

7Sep/100

20个非常有用的PHP类库

Posted by ofeng

    本文提供了20个非常有用的PHP类库的名称和下载地址。这20个PHP类库包含了图标库,RSS解析,缩略图生成,支付,OpenID,数据库抽象,PDF生成器等一系列功能。

    下面是一些非常有用的PHP类库,相信一定可以为你的WEB开发提供更好和更为快速的方法。

    图表库

    下面的类库可以让你很简的创建复杂的图表和图片。当然,它们需要GD库的支持。

    pChart - 一个可以创建统计图的库。

    Libchart - 这也是一个简单的统计图库。

    JpGraph - 一个面向对象的图片创建类。

    Open Flash Chart - 这是一个基于Flash的统计图。

    RSS 解析

    解释RSS并是一件很单调的事情,不过幸好你有下面的类库可以帮助你方便地读取RSS的Feed。

    MagpieRSS - 开源的PHP版RSS解析器,据说功能强大,未验证。

    SimplePie - 这是一个非常快速,而且易用的RSS和Atom 解析库。

    缩略图生成

    phpThumb - 功能很强大,如何强大还是自己去体会吧。

    支付

    你的网站需要处理支付方面的事情?需要一个和支付网关的程序?下面这个程序可以帮到你。

    PHP Payment Library - 支持Paypal, Authorize.net 和2Checkout (2CO)

    OpenID

    PHP-OpenID - 支持OpenID的一个PHP库。OpenID是帮助你使用相同的用户名和口令登录不同的网站的一种解决方案。如果你对OpenID不熟悉的话,你可以到这里看看:http://openid.net.cn/

    数据为抽象/对象关系映射ORM

    ADOdb - 数据库抽象

    Doctrine - 对象关系映射Object relational mapper (ORM) ,需要 PHP 5.2.3+ 版本,一个非常强大的database abstraction layer (DBAL).

    Propel - 对象关系映射框架- PHP5

    Outlet - 也是关于对象关系映射的一个工具。

    注:对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 这也同时暗示者额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

    PDF 生成器

    FPDF - 这量一个可以让你生成PDF的纯PHP类库。

    Excel 相关

    你的站点需要生成Excel?没有问题,下面这两个类库可以让你轻松做到这一点。

    php-excel - 这是一个非常简单的Excel文件生成类。

    PHP Excel Reader - 可以解析并读取XLS文件中的数据。

    E-Mail 相关

    不喜欢PHP的mail函数?觉得不够强大?下面的PHP邮件相关的库绝对不会让你失望。

    Swift Mailer - 免费的超多功能的PHP邮件库。

    PHPMailer - 超强大的邮件发送类。

    单元测试

    如果你在使用测试驱动的方法开发你的程序,下面的类库和框架绝你能帮助你的开发。

    SimpleTest - 一个PHP的单元测试和网页测试的框架。

    PHPUnit - 来自xUnit 家族,提供一个框架可以让你方便地进行单元测试的案例开发。并可非常容易地分析其测试结

Tagged as: , , No Comments
30Aug/100

Nginx+php下用Content-Length替换chunk编码模式方法

Posted by ofeng

1.修改nginx配置文件,gzip:off

2.php程序文件中,输出时加一个头参数

Header(“Content-lenth: $output_length\r\n”);

3.搞定!

没看过Nginx的源码,猜想了下Nginx的处理流程

   1: if( gzip on ){

   2:     chunk;

   3: }

   4: else {

   5:     if( has content-lenth ) {

   6:         content-lenth;

   7:     }

   8:     else {

   9:         chunk

  10:     }

  11: }

27Aug/101

Ubuntu 10下安装php-fpm

Posted by ofeng

习惯了在Centos下搭LNPP环境(Ningx+Php+Postgres),突然换了Ubuntu,还不太适应了,以前写的安装脚本完全报废……

基础lib库,Centos下基本用yum的,还好Ubuntu有apt-get,但是lib库名称稍有区别

记录下几个明显的,yum install cyrus-sasl-devel => apt-get install libsasl2-dev

没了,靠,不会数数了……

安装其他包的过程中也碰到不少错误

1./usr/bin/ld: cannot find –lperl

解决办法:apt-get install libperl-dev

2.libtool: unrecognized option `-DHAVE_CONFIG_H'

解决办法:apt-get install build-essential

5Aug/102

php加字符串的测试

Posted by alacner

最近有个bug,改了以后,加了一个字符串,造成了这个字符串的值为0,记录下,提醒下,要严格判断类型!

Tagged as: 2 Comments