分类目录归档:php

CGI、FastCGI、PHP-FPM

基础

在整个网站架构中,Web Server(如Apache)只是内容的分发者。举个栗子,如果客户端请求的是 index.html,那么Web Server会去文件系统中找到这个文件,发送给浏览器,这里分发的是静态数据。

如果请求的是 index.php,根据配置文件,Web Server知道这个不是静态文件,需要去找 PHP 解析器来处理,那么他会把这个请求简单处理,然后交给PHP解析器。

当Web Server收到 index.php 这个请求后,会启动对应的 CGI 程序,这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件,初始化执行环境,然后处理请求,再以规定CGI规定的格式返回处理后的结果,退出进程,Web server再把结果返回给浏览器。这就是一个完整的动态PHP Web访问流程,接下来再引出这些概念,就好理解多了,

  • CGI:是 Web Server 与 Web Application 之间数据交换的一种协议。
  • FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。同样,SCGI 协议与 FastCGI 类似。
  • PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。
  • PHP-FPM:是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。

WEB 中,

  • Web Server 一般指Apache、Nginx、IIS、Lighttpd、Tomcat等服务器,
  • Web Application 一般指PHP、Java、Asp.net等应用程序。

Module方式

在了解 CGI 之前,我们先了解一下Web server 传递数据的另外一种方法:PHP Module加载方式。以 Apache 为例,在PHP Module方式中,是不是在 Apache 的配置文件 httpd.conf 中加上这样几句:

# 加入以下2句
LoadModule php5_module D:/php/php5apache2_2.dll
AddType application/x-httpd-php .php

# 修改如下内容
<IfModule dir_module>
    DirectoryIndex index.php index.html
</IfModule>

上面是 Windows 下安装php和apache环境后手动配置,在linux下源码安装大致是这样配置的:

# ./configure --with-mysql=/usr/local --with-apache=/usr/local/apache --enable-track-vars

所以,这种方式,他们的共同本质都是用 LoadModule 来加载 php5_module,就是把php作为apache的一个子模块来运行。当通过web访问php文件时,apache就会调用php5_module来解析php代码。

那么php5_module是怎么来将数据传给php解析器来解析php代码的呢?答案是通过sapi。

我们再来看一张图,详细的说说apache 与 php 与 sapi的关系:

从上面图中,我们看出了sapi就是这样的一个中间过程,SAPI提供了一个和外部通信的接口,有点类似于socket,使得PHP可以和其他应用进行交互数据(apache,nginx等)。php默认提供了很多种SAPI,常见的提供给apache和nginx的php5_module、CGI、FastCGI,给IIS的ISAPI,以及Shell的CLI。

所以,以上的apache调用php执行的过程如下:

apache -> httpd -> php5_module -> sapi -> php

好了。apache与php通过php5_module的方式就搞清楚了吧!

这种模式将php模块安装到apache中,所以每一次apache结束请求,都会产生一条进程,这个进程就完整的包括php的各种运算计算等操作。

在上图中,我们很清晰的可以看到,apache每接收一个请求,都会产生一个进程来连接php通过sapi来完成请求,可想而知,如果一旦用户过多,并发数过多,服务器就会承受不住了。

而且,把mod_php编进apache时,出问题时很难定位是php的问题还是apache的问题。

CGI

CGI(Common Gateway Interface)全称是“通用网关接口”,WEB 服务器与PHP应用进行“交谈”的一种工具,其程序须运行在网络服务器上。CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php、perl、tcl等。

WEB服务器会传哪些数据给PHP解析器呢?URL、查询字符串、POST数据、HTTP header都会有。所以,CGI就是规定要传哪些数据,以什么样的格式传递给后方处理这个请求的协议。仔细想想,你在PHP代码中使用的用户从哪里来的。

也就是说,CGI就是专门用来和 web 服务器打交道的。web服务器收到用户请求,就会把请求提交给cgi程序(如php-cgi),cgi程序根据请求提交的参数作应处理(解析php),然后输出标准的html语句,返回给web服服务器,WEB服务器再返回给客户端,这就是普通cgi的工作原理。

CGI的好处就是完全独立于任何服务器,仅仅是做为中间分子。提供接口给apache和php。他们通过cgi搭线来完成数据传递。这样做的好处了尽量减少2个的关联,使他们2变得更独立。

但是CGI有个蛋疼的地方,就是每一次web请求都会有启动和退出过程,也就是最为人诟病的fork-and-execute模式,这样一在大规模并发下,就死翘翘了。

FastCGI介绍

FastCGI简单介绍

从根本上来说,FastCGI是用来提高CGI程序性能的。类似于CGI,FastCGI也可以说是一种协议

FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行,并且接受来自其它网站服务器来的请求。

FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中,并因此获得较高的性能。众所周知,CGI解释器的反复加载是CGI性能低下的主要原因,如果CGI解释器保持在内存中,并接受FastCGI进程管理器调度,则可以提供良好的性能、伸缩性、Fail- Over特性等等。

FastCGI的工作原理

FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求,或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

  1. Web Server启动时载入FastCGI进程管理器(Apache Module或IIS ISAPI等)
  2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可建多个php-cgi),并等待来自Web Server的连接。
  3. 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。
  4. FastCGI子进程完成处理后,将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待,并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中,php-cgi在此便退出了。

FastCGI与CGI特点:

  1. 对于CGI来说,每一个Web请求PHP都必须重新解析php.ini、重新载入全部扩展,并重新初始化全部数据结构。而使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。
  2. 由于FastCGI是多进程,所以比CGI多线程消耗更多的服务器内存,php-cgi解释器每进程消耗7至25兆内存,将这个数字乘以50或100就是很大的内存数。

PHP-FPM介绍

要了解PHP-FPM,就得先说说PHP-CGI。

PHP-CGI就是PHP实现的自带的FastCGI管理器。 虽然是php官方出品,但是这丫的却一点也不给力,性能太差,而且也很麻烦不人性化,主要体现在:

  1. php-cgi变更php.ini配置后,需重启php-cgi才能让新的php-ini生效,不可以平滑重启。
  2. 直接杀死php-cgi进程,php就不能运行了。

上面2个问题,一直让很多人病垢了很久,所以很多人一直还是在用 Module 方式。 直到 2004年一个叫 Andrei Nigmatulin的屌丝发明了PHP-FPM ,这神器的出现就彻底打破了这种局面,这是一个PHP专用的 fastcgi 管理器,它很爽的克服了上面2个问题,而且,还表现在其他方面更表现强劲。

也就是说,PHP-FPM 是对于 FastCGI 协议的具体实现,他负责管理一个进程池,来处理来自Web服务器的请求。目前,PHP5.3版本之后,PHP-FPM是内置于PHP的

因为PHP-CGI只是个CGI程序,他自己本身只能解析请求,返回结果,不会进程管理。所以就出现了一些能够调度 php-cgi 进程的程序,比如说由lighthttpd分离出来的spawn-fcgi。同样,PHP-FPM也是用于调度管理PHP解析器php-cgi的管理程序。

PHP-FPM通过生成新的子进程可以实现php.ini修改后的平滑重启。

总结

最后,我们来总结一下,这些技术经过不断的升级,可以解决什么问题(不然也不会升级嘛)。

所以,如果要搭建一个高性能的PHP WEB服务器,目前最佳的方式是Apache/Nginx + FastCGI + PHP-FPM(+PHP-CGI)方式

 

原文:https://www.awaimai.com/371.html

 

.

 

PHP call_user_func & call_user_func_array

对比范围

  • 直接调用
  • 变量函数调用
  • call_user_func 调用
  • call_user_func_array 调用

测试结果

call_user_func_array 所用时间为:1.1608240604401s

测试代码:

<?php error_reporting(E_ALL | E_STRICT); define('ITERATIONS', 2000000); class Bench { private $bench_name; private $start_time; private $end_time; public function start($name) { $this->bench_name = $name;
        $this->start_time = microtime(true);
    }

    public function end()
    {
        $this->end_time = microtime(true);
        echo "Call style: " . $this->bench_name . '; ' . ($this->end_time - $this->start_time) . " seconds". PHP_EOL;
    }
}

class Test
{
    public function test($a, $b, $c)
    {
        return;
    }
}


$bench = new Bench();
$test = new Test();
$arg = [1, 2, 3];
$func_name = 'test';

$bench->start('normal');
for ($i=0; $i < ITERATIONS; ++$i) { $test->test($arg[0], $arg[1], $arg[2]);
}
$bench->end();

$bench->start('var_function');
for ($i=0; $i < ITERATIONS; ++$i) { $test->$func_name($arg[0], $arg[1], $arg[2]);
}
$bench->end();

$bench->start('call_user_func');
for ($i=0; $i < ITERATIONS; ++$i) { call_user_func([$test, $func_name], $arg[0], $arg[1], $arg[2]); } $bench->end();

$bench->start('call_user_func_array');
for ($i=0; $i < ITERATIONS; ++$i) { call_user_func_array([$test, $func_name], $arg); } $bench->end();

 


Iterations: 100 000
Averaged over: 10
PHP 5.6.30 (cli) (built: Jan 18 2017 19:47:28)

Overall Average
Invocation Time (s) Delta (s) %
directFunction 0.0089 -0.0211 -70.19
directStatic 0.0098 -0.0202 -67.39
directLambda 0.0109 -0.0191 -63.52
directInstance 0.0116 -0.0184 -61.31
directClosure 0.0150 -0.0150 -50.15
Invoke 0.0282 -0.0018 -6.13
call_user_func 0.0300
ClosureFactory 0.0316 +0.0016 +5.20
assignedClosureFactory 0.0328 +0.0028 +9.28
call_user_func_array 0.0399 +0.0099 +33.02
InvokeCallUserFunc 0.0418 +0.0118 +39.17
directImplementation 0.0475 +0.0175 +58.28

 


Iterations: 100 000
Averaged over: 10
PHP 7.1.2 (cli) (built: Feb 14 2017 21:24:45)

Overall Average
Invocation Time (s) Delta (s) %
directFunction 0.0043 -0.0096 -68.92
directStatic 0.0050 -0.0089 -64.04
directInstance 0.0058 -0.0081 -58.22
directLambda 0.0063 -0.0075 -54.44
directClosure 0.0081 -0.0058 -41.57
call_user_func 0.0139
call_user_func_array 0.0147 +0.0008 +5.84
Invoke 0.0187 +0.0048 +34.61
ClosureFactory 0.0207 +0.0069 +49.43
assignedClosureFactory 0.0219 +0.0080 +57.75
directImplementation 0.0232 +0.0094 +67.53
InvokeCallUserFunc 0.0264 +0.0126 +90.67

.

资料来源:

https://segmentfault.com/q/1010000012081290

 

 

php date

a - "am" 或是 "pm" 
A - "AM" 或是 "PM" 
d - 几日,二位数字,若不足二位则前面补零; 如: "01" 至 "31" 
D - 星期几,三个英文字母; 如: "Fri" 
F - 月份,英文全名; 如: "January" 
h - 12 小时制的小时; 如: "01" 至 "12" 
H - 24 小时制的小时; 如: "00" 至 "23" 
g - 12 小时制的小时,不足二位不补零; 如: "1" 至 12" 
G - 24 小时制的小时,不足二位不补零; 如: "0" 至 "23" 
i - 分钟; 如: "00" 至 "59" 
j - 几日,二位数字,若不足二位不补零; 如: "1" 至 "31" 
l - 星期几,英文全名; 如: "Friday" 
m - 月份,二位数字,若不足二位则在前面补零; 如: "01" 至 "12" 
n - 月份,二位数字,若不足二位则不补零; 如: "1" 至 "12" 
M - 月份,三个英文字母; 如: "Jan" 
s - 秒; 如: "00" 至 "59" 
S - 字尾加英文序数,二个英文字母; 如: "th","nd" 
t - 指定月份的天数; 如: "28" 至 "31" 
U - 总秒数 
w - 数字型的星期几,如: "0" (星期日) 至 "6" (星期六) 
Y - 年,四位数字; 如: "1999" 
y - 年,二位数字; 如: "99" 

z - 一年中的第几天; 如: "0" 至 "365"

 

http://www.php.net/manual/zh/function.date.php

 

php timezone

$timezones =
array (
 '(GMT-12:00) International Date Line West' => 'Pacific/Wake',
 '(GMT-11:00) Midway Island' => 'Pacific/Apia',
 '(GMT-11:00) Samoa' => 'Pacific/Apia',
 '(GMT-10:00) Hawaii' => 'Pacific/Honolulu',
 '(GMT-09:00) Alaska' => 'America/Anchorage',
 '(GMT-08:00) Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles',
 '(GMT-07:00) Arizona' => 'America/Phoenix',
 '(GMT-07:00) Chihuahua' => 'America/Chihuahua',
 '(GMT-07:00) La Paz' => 'America/Chihuahua',
 '(GMT-07:00) Mazatlan' => 'America/Chihuahua',
 '(GMT-07:00) Mountain Time (US & Canada)' => 'America/Denver',
 '(GMT-06:00) Central America' => 'America/Managua',
 '(GMT-06:00) Central Time (US & Canada)' => 'America/Chicago',
 '(GMT-06:00) Guadalajara' => 'America/Mexico_City',
 '(GMT-06:00) Mexico City' => 'America/Mexico_City',
 '(GMT-06:00) Monterrey' => 'America/Mexico_City',
 '(GMT-06:00) Saskatchewan' => 'America/Regina',
 '(GMT-05:00) Bogota' => 'America/Bogota',
 '(GMT-05:00) Eastern Time (US & Canada)' => 'America/New_York',
 '(GMT-05:00) Indiana (East)' => 'America/Indiana/Indianapolis',
 '(GMT-05:00) Lima' => 'America/Bogota',
 '(GMT-05:00) Quito' => 'America/Bogota',
 '(GMT-04:00) Atlantic Time (Canada)' => 'America/Halifax',
 '(GMT-04:00) Caracas' => 'America/Caracas',
 '(GMT-04:00) La Paz' => 'America/Caracas',
 '(GMT-04:00) Santiago' => 'America/Santiago',
 '(GMT-03:30) Newfoundland' => 'America/St_Johns',
 '(GMT-03:00) Brasilia' => 'America/Sao_Paulo',
 '(GMT-03:00) Buenos Aires' => 'America/Argentina/Buenos_Aires',
 '(GMT-03:00) Georgetown' => 'America/Argentina/Buenos_Aires',
 '(GMT-03:00) Greenland' => 'America/Godthab',
 '(GMT-02:00) Mid-Atlantic' => 'America/Noronha',
 '(GMT-01:00) Azores' => 'Atlantic/Azores',
 '(GMT-01:00) Cape Verde Is.' => 'Atlantic/Cape_Verde',
 '(GMT) Casablanca' => 'Africa/Casablanca',
 '(GMT) Edinburgh' => 'Europe/London',
 '(GMT) Greenwich Mean Time : Dublin' => 'Europe/London',
 '(GMT) Lisbon' => 'Europe/London',
 '(GMT) London' => 'Europe/London',
 '(GMT) Monrovia' => 'Africa/Casablanca',
 '(GMT+01:00) Amsterdam' => 'Europe/Berlin',
 '(GMT+01:00) Belgrade' => 'Europe/Belgrade',
 '(GMT+01:00) Berlin' => 'Europe/Berlin',
 '(GMT+01:00) Bern' => 'Europe/Berlin',
 '(GMT+01:00) Bratislava' => 'Europe/Belgrade',
 '(GMT+01:00) Brussels' => 'Europe/Paris',
 '(GMT+01:00) Budapest' => 'Europe/Belgrade',
 '(GMT+01:00) Copenhagen' => 'Europe/Paris',
 '(GMT+01:00) Ljubljana' => 'Europe/Belgrade',
 '(GMT+01:00) Madrid' => 'Europe/Paris',
 '(GMT+01:00) Paris' => 'Europe/Paris',
 '(GMT+01:00) Prague' => 'Europe/Belgrade',
 '(GMT+01:00) Rome' => 'Europe/Berlin',
 '(GMT+01:00) Sarajevo' => 'Europe/Sarajevo',
 '(GMT+01:00) Skopje' => 'Europe/Sarajevo',
 '(GMT+01:00) Stockholm' => 'Europe/Berlin',
 '(GMT+01:00) Vienna' => 'Europe/Berlin',
 '(GMT+01:00) Warsaw' => 'Europe/Sarajevo',
 '(GMT+01:00) West Central Africa' => 'Africa/Lagos',
 '(GMT+01:00) Zagreb' => 'Europe/Sarajevo',
 '(GMT+02:00) Athens' => 'Europe/Istanbul',
 '(GMT+02:00) Bucharest' => 'Europe/Bucharest',
 '(GMT+02:00) Cairo' => 'Africa/Cairo',
 '(GMT+02:00) Harare' => 'Africa/Johannesburg',
 '(GMT+02:00) Helsinki' => 'Europe/Helsinki',
 '(GMT+02:00) Istanbul' => 'Europe/Istanbul',
 '(GMT+02:00) Jerusalem' => 'Asia/Jerusalem',
 '(GMT+02:00) Kyiv' => 'Europe/Helsinki',
 '(GMT+02:00) Minsk' => 'Europe/Istanbul',
 '(GMT+02:00) Pretoria' => 'Africa/Johannesburg',
 '(GMT+02:00) Riga' => 'Europe/Helsinki',
 '(GMT+02:00) Sofia' => 'Europe/Helsinki',
 '(GMT+02:00) Tallinn' => 'Europe/Helsinki',
 '(GMT+02:00) Vilnius' => 'Europe/Helsinki',
 '(GMT+03:00) Baghdad' => 'Asia/Baghdad',
 '(GMT+03:00) Kuwait' => 'Asia/Riyadh',
 '(GMT+03:00) Moscow' => 'Europe/Moscow',
 '(GMT+03:00) Nairobi' => 'Africa/Nairobi',
 '(GMT+03:00) Riyadh' => 'Asia/Riyadh',
 '(GMT+03:00) St. Petersburg' => 'Europe/Moscow',
 '(GMT+03:00) Volgograd' => 'Europe/Moscow',
 '(GMT+03:30) Tehran' => 'Asia/Tehran',
 '(GMT+04:00) Abu Dhabi' => 'Asia/Muscat',
 '(GMT+04:00) Baku' => 'Asia/Tbilisi',
 '(GMT+04:00) Muscat' => 'Asia/Muscat',
 '(GMT+04:00) Tbilisi' => 'Asia/Tbilisi',
 '(GMT+04:00) Yerevan' => 'Asia/Tbilisi',
 '(GMT+04:30) Kabul' => 'Asia/Kabul',
 '(GMT+05:00) Ekaterinburg' => 'Asia/Yekaterinburg',
 '(GMT+05:00) Islamabad' => 'Asia/Karachi',
 '(GMT+05:00) Karachi' => 'Asia/Karachi',
 '(GMT+05:00) Tashkent' => 'Asia/Karachi',
 '(GMT+05:30) Chennai' => 'Asia/Calcutta',
 '(GMT+05:30) Kolkata' => 'Asia/Calcutta',
 '(GMT+05:30) Mumbai' => 'Asia/Calcutta',
 '(GMT+05:30) New Delhi' => 'Asia/Calcutta',
 '(GMT+05:45) Kathmandu' => 'Asia/Katmandu',
 '(GMT+06:00) Almaty' => 'Asia/Novosibirsk',
 '(GMT+06:00) Astana' => 'Asia/Dhaka',
 '(GMT+06:00) Dhaka' => 'Asia/Dhaka',
 '(GMT+06:00) Novosibirsk' => 'Asia/Novosibirsk',
 '(GMT+06:00) Sri Jayawardenepura' => 'Asia/Colombo',
 '(GMT+06:30) Rangoon' => 'Asia/Rangoon',
 '(GMT+07:00) Bangkok' => 'Asia/Bangkok',
 '(GMT+07:00) Hanoi' => 'Asia/Bangkok',
 '(GMT+07:00) Jakarta' => 'Asia/Bangkok',
 '(GMT+07:00) Krasnoyarsk' => 'Asia/Krasnoyarsk',
 '(GMT+08:00) Beijing' => 'Asia/Hong_Kong',
 '(GMT+08:00) Chongqing' => 'Asia/Hong_Kong',
 '(GMT+08:00) Hong Kong' => 'Asia/Hong_Kong',
 '(GMT+08:00) Irkutsk' => 'Asia/Irkutsk',
 '(GMT+08:00) Kuala Lumpur' => 'Asia/Singapore',
 '(GMT+08:00) Perth' => 'Australia/Perth',
 '(GMT+08:00) Singapore' => 'Asia/Singapore',
 '(GMT+08:00) Taipei' => 'Asia/Taipei',
 '(GMT+08:00) Ulaan Bataar' => 'Asia/Irkutsk',
 '(GMT+08:00) Urumqi' => 'Asia/Hong_Kong',
 '(GMT+09:00) Osaka' => 'Asia/Tokyo',
 '(GMT+09:00) Sapporo' => 'Asia/Tokyo',
 '(GMT+09:00) Seoul' => 'Asia/Seoul',
 '(GMT+09:00) Tokyo' => 'Asia/Tokyo',
 '(GMT+09:00) Yakutsk' => 'Asia/Yakutsk',
 '(GMT+09:30) Adelaide' => 'Australia/Adelaide',
 '(GMT+09:30) Darwin' => 'Australia/Darwin',
 '(GMT+10:00) Brisbane' => 'Australia/Brisbane',
 '(GMT+10:00) Canberra' => 'Australia/Sydney',
 '(GMT+10:00) Guam' => 'Pacific/Guam',
 '(GMT+10:00) Hobart' => 'Australia/Hobart',
 '(GMT+10:00) Melbourne' => 'Australia/Sydney',
 '(GMT+10:00) Port Moresby' => 'Pacific/Guam',
 '(GMT+10:00) Sydney' => 'Australia/Sydney',
 '(GMT+10:00) Vladivostok' => 'Asia/Vladivostok',
 '(GMT+11:00) Magadan' => 'Asia/Magadan',
 '(GMT+11:00) New Caledonia' => 'Asia/Magadan',
 '(GMT+11:00) Solomon Is.' => 'Asia/Magadan',
 '(GMT+12:00) Auckland' => 'Pacific/Auckland',
 '(GMT+12:00) Fiji' => 'Pacific/Fiji',
 '(GMT+12:00) Kamchatka' => 'Pacific/Fiji',
 '(GMT+12:00) Marshall Is.' => 'Pacific/Fiji',
 '(GMT+12:00) Wellington' => 'Pacific/Auckland',
 '(GMT+13:00) Nuku\'alofa' => 'Pacific/Tongatapu',

);

 

windows-time-zone.zip

byte to integer

/**  
      
* 转换一个int为byte数组  
      
* @param $byt 目标byte数组  
      
* @param $val 需要转换的字符串  
      
*  
      
*/ 
   
    public static function integerToBytes($val) {  
        $byt = array();  
        $byt[0] = ($val & 0xff);  
        $byt[1] = ($val >> 8 & 0xff);  
        $byt[2] = ($val >> 16 & 0xff);  
        $byt[3] = ($val >> 24 & 0xff);  
        return $byt;  
    }  
   
     
/**  
      
* 从字节数组中指定的位置读取一个Integer类型的数据  
      
* @param $bytes 字节数组  
      
* @param $position 指定的开始位置  
      
* @return 一个Integer类型的数据  
      
*/ 
   
    public static function bytesToInteger($bytes, $position) {  
        $val = 0;  
        $val = $bytes[$position + 3] & 0xff;  
        $val <<= 8;  
        $val |= $bytes[$position + 2] & 0xff;  
        $val <<= 8;  
        $val |= $bytes[$position + 1] & 0xff;  
        $val <<= 8;  
        $val |= $bytes[$position] & 0xff;  
        return $val;  
    }  
   
     
/**  
      
* 转换一个shor字符串为byte数组  
      
* @param $byt 目标byte数组  
      
* @param $val 需要转换的字符串  
      
*  
      
*/ 
   
    public static function shortToBytes($val) {  
        $byt = array();  
        $byt[0] = ($val & 0xff);  
        $byt[1] = ($val >> 8 & 0xff);  
        return $byt;  
    }  
   
     
/**  
      
* 从字节数组中指定的位置读取一个Short类型的数据。  
      
* @param $bytes 字节数组  
      
* @param $position 指定的开始位置  
      
* @return 一个Short类型的数据  
      
*/ 
   
    public static function bytesToShort($bytes, $position) {  
        $val = 0;  
        $val = $bytes[$position + 1] & 0xFF;  
        $val = $val << 8;  
        $val |= $bytes[$position] & 0xFF;  
        return $val;  
    }  
   
} 

.

strace ltrace 调试php

以分析 in_array 为例

shell$ time /usr/local/php/bin/php test.php

real 0m1.132s
user 0m1.118s
sys 0m0.015s

 

shell$ strace -ttt -o xxx /usr/local/php/bin/php test.php

shell$ less xxx

点击查看原图

看到这两次系统调用之间的延时非常大,却并不知道干了什么?一筹莫展了,幸好,Linux下的调试利器除了strace还有ltrace(当然还有dtrace,ptrace,不在本文讨论范围了,略去)。

引用:strace用来 跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来 跟踪进程调用库函数的情况(via IBM developerworks)。

为了排除干扰因素,将$x直接赋值为array(“0″,”1″,”2″,……)的形式,避免过多的malloc调用影响结果。执行

shell$ ltrace -c /usr/local/php/bin/php test.php

点击查看原图

看到库函数__strtol_internal的调用非常之频繁,达到了94%,太夸张了,然后我又查了一下这个库函数__strtol_internal是干嘛的,原来是strtol的别名,简单的说就是把字符串转换成长整形,可以猜测PHP引擎已经检测到这是一个字符串型的数字,所以期望将他们转换成长整型来比较,这个转换过程中消耗了太多时间,我们再次执行:


shell$ ltrace -e "__strtol_internal" /usr/local/php/bin/php test.php


可以轻松抓到大量下图这样的调用,到此,问题找到了,in_array这种松比较,会将两个字符型数字串先转换为长整型再进行比较,却不知性能就耗在这上面了。

点击查看原图

知道了症结所在,我们解决的办法就很多了,最简单的就是为in_array加第三个参数为true,即变为严格比较,同时还要比较类型,这样避免了PHP自作聪明的转换类型,跑起来果然快多了,代码如下:

<?php
$y="1800";
$x = array();
for($j=0;$j<2000;$j++){
        $x[]= "{$j}";
}
 
for($i=0;$i<3000;$i++){
        if(in_array($y,$x,true)){
                continue;
        }
}
?>

 

shell$ time /usr/local/php/bin/php test.php

real 0m0.267s
user 0m0.247s
sys 0m0.020s

快了好多倍啊!!!可以看到sys耗时几乎没有太大变化。我们再次ltrace一把,还是要把$x直接赋值,排除malloc调用的干扰,因为我们实际应用中是从缓存里一次拉出来的,所以也不存在示例代码中这样的循环来申请内存的情况。
再次执行

shell$ ltrace -c /usr/local/php/bin/php test.php

如下图:

 点击查看原图

 

彻底理解PHP的SESSION机制

1.session.save_handler = files



    
* 1. session_start()

         
1. session_start()是session机制的开始,它有一定概率开启垃圾回收,因为session是存放在文件中,

PHP自身的垃圾回收是无效的,SESSION的回收是要删文件的,这个概率是根据php
.ini的配置决定的,

但是有的系统是 session
.gc_probability = 0,这也就是说概率是0,而是通过cron脚本来实现垃圾回收。



            session
.gc_probability = 1

            session
.gc_divisor = 1000

            session
.gc_maxlifetime = 1440//过期时间 默认24分钟

            //概率是 session.gc_probability/session.gc_divisor 结果 1/1000, 

            //不建议设置过小,因为session的垃圾回收,是需要检查每个文件是否过期的。


            session.save_path = //好像不同的系统默认不一样,有一种设置是 "N;/path"

            //这是随机分级存储,这个样的话,垃圾回收将不起作用,需要自己写脚本




         
2. session会判断当前是否有$_COOKIE[session_name()];session_name()返回保存session_id的COOKIE键值,

这个值可以从php
.ini找到



            session
.name = PHPSESSID //默认值PHPSESSID

             



         
3. 如果不存在会生成一个session_id,然后把生成的session_id作为COOKIE的值传递到客户端.

相当于执行了下面COOKIE 操作,注意的是,这一步执行了setcookie()操作,COOKIE是在header头中发送的,

这之前是不能有输出的,PHP有另外一个函数 
session_regenerate_id() 如果使用这个函数,这之前也是不能有输出的。



                
setcookie(session_name(),

                          
session_id(),

                          session
.cookie_lifetime,//默认0

                          session.cookie_path,//默认'/'当前程序跟目录下都有效

                          session.cookie_domain,//默认为空

                          )



         
4. 如果存在那么session_id = $_COOKIE[session_name];

            然后去session
.save_path指定的文件夹里去找名字为'SESS_' . session_id()的文件.

            读取文件的内容反序列化,然后放到
$_SESSION中

    
* 2. 为$_SESSION赋值

      比如新添加一个值
$_SESSION['test'= 'blah'; 那么这个$_SESSION只会维护在内存中,当脚本执行结束的时候,

用把
$_SESSION的值写入到session_id指定的文件夹中,然后关闭相关资源.      这个阶段有可能执行更改session_id的操作,

比如销毁一个旧的的session_id,生成一个全新的session_id
.一半用在自定义 session操作,角色的转换上,

比如Drupal
.Drupal的匿名用户有一个SESSION的,当它登录后需要换用新的session_id



        
if (isset($_COOKIE[session_name()])) {

          
setcookie(session_name(), '', time() - 42000, '/');//旧session cookie过期

        }

        
session_regenerate_id();//这一步会生成新的session_id

       //session_id()返回的是新的值




      
3.写入SESSION操作

      在脚本结束的时候会执行SESSION写入操作,把
$_SESSION中值写入到session_id命名的文件中,可能已经存在,

可能需要创建新的文件。

    
* 4. 销毁SESSION

      SESSION发出去的COOKIE一般属于即时COOKIE,保存在内存中,当浏览器关闭后,才会过期,假如需要人为强制过期,

比如 退出登录,而不是关闭浏览器,那么就需要在代码里销毁SESSION,方法有很多,

          o 
1. setcookie(session_name(), session_id(), time() - 8000000, ..);//退出登录前执行

          o 2. usset($_SESSION);//这会删除所有的$_SESSION数据,刷新后,有COOKIE传过来,但是没有数据。

          o 3. session_destroy();//这个作用更彻底,删除$_SESSION 删除session文件,和session_id



      当不关闭浏览器的情况下,再次刷新,2和3都会有COOKIE传过来,但是找不到数据



2.session.save_handler = user



      用户自定义session处理机制,更加直观

    
* session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc'); 

1.session_start(),

      执行open(
$save_path, $session_name)打开session操作句柄

      
$save_path 在session.save_handler = files的情况下它就是session.save_path,

但是如果用户自定的话,这个两个参数都用不上,直接返回TRUE



      执行read(
$id)从中读取数据.//这个参数是自动传递的就是session_id(),可以通过这个值进行操作。

    * 2.脚本执行结束

      执行write(
$id, $sess_data//两个参数,很简单

    * 3.假如用户需要session_destroy()

      先执行destroy
.在执行第2步



      一个实际例子:



      
//SESSION初始化的时候调用

      function open($save_path, $session_name)

      {

        
global $sess_save_path;

        
$sess_save_path = $save_path;

        
return(true);

      }



      
//关闭的时候调用

      function close()

      {

        
return(true);

      }



      
function read($id)

      {

        
global $sess_save_path;

        
$sess_file = "$sess_save_path/sess_$id";

        
return (string) @file_get_contents($sess_file);

      }

      
//脚本执行结束之前,执行写入操作

      function write($id, $sess_data)

      {

        
echo "sdfsf";

        
global $sess_save_path;



        
$sess_file = "$sess_save_path/sess_$id";

        
if ($fp = @fopen($sess_file, "w")) {

          
$return = fwrite($fp, $sess_data);

          
fclose($fp);

          
return $return;

        } 
else {

          
return(false);

        }



      }



      
function destroy($id)

      {

        
global $sess_save_path;



        
$sess_file = "$sess_save_path/sess_$id";

        
return(@unlink($sess_file));

      }



      
function gc($maxlifetime)

      {

        
global $sess_save_path;



        
foreach (glob("$sess_save_path/sess_*"as $filename) {

          
if (filemtime($filename+ $maxlifetime < time()) {

            @
unlink($filename);

          }

        }

        
return true;

      }

php aes

/**
 * aes
 *
 */
class aes{
    private $key = "wt@36";
    private $iv         = "8401948501850295"; //只能是16字节
 
    /**
     * 
     * @param unknown_type $key
     * @param string $iv   16 length
     */
    function __construct($key,$iv=null){
        $this->key = $key;
        if (!empty($iv)){
         $this->iv = $iv;
        }
    }
 
    /**
     * 加密
     * @param string $value
     * @return string
     */
    function encrypt($value){
     return bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key, $value, MCRYPT_MODE_CBC,$this->iv));
    }
    
    /*
     * AES解密
    */
    function decrypt($value){
     return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, pack("H*",$value),MCRYPT_MODE_CBC, $this->iv);
    }
}

关闭 Powered-By: PHP

X-Powered-By: PHP/5.x.x
可以在php.ini文件中设置:
; Decides whether PHP may expose the fact that it is installed on the server
; (e.g. by adding its signature to the Web server header).  It is no security
; threat in any way, but it makes it possible to determine whether you use PHP
; on your server or not.
; http://php.net/expose-php
expose_php = On #这里改成Off

这样就避免了输出类似 X-Powered-By: PHP/5.x.x 这样的信息

注: 有一些php版本包会把这样的信息写进源码里面,所以改源码后重新编译即可。

 

session.name  = phpsession 可修改其它