作者归档:admin

PHP的生命周期

PHP的生命周期

php本身的生命周期是在命令行执行php test.php程序的生命周期(也就是cli)

整个过程如下:

执行php test.php

调用每个扩展的模块初始化程序

    请求test.php程序

    调用每个扩展的请求初始化程序

        执行test.php程序

    调用每个扩展的请求关闭程序

    释放内存等清除工作

调用每个扩展的模块关闭程序

终止php

如果PHP运行在WEB服务器中,那么它的生命周期就会有些不同了,这里又要根据服务器的不同分为以下三种:

1、单进程

模块初始化

    请求初始化

        执行脚本

    关闭请求

    请求初始化

        执行脚本

    关闭请求
    请求初始化
        执行脚本
    关闭请求
    请求初始化
        执行脚本
    关闭请求
……

……
……
模块关闭

单进程的WEB服务器只对模块初始化一次,所有的页面请求都在其中

2、多进程

模块初始化                         模块初始化                    模块初始化                模块初始化

    请求初始化                         请求初始化                    请求初始化                请求初始化

        执行脚本                            执行脚本                      执行脚本                   执行脚本

    关闭请求                            关闭请求                      关闭请求                   关闭请求

    请求初始化                         请求初始化                    请求初始化                请求初始化

        执行脚本                            执行脚本                      执行脚本                   执行脚本

    关闭请求                            关闭请求                      关闭请求                   关闭请求

    请求初始化                         请求初始化                    请求初始化                请求初始化

        执行脚本                            执行脚本                      执行脚本                   执行脚本

    关闭请求                            关闭请求                      关闭请求                   关闭请求

……                                  ……                            ……                         ……

关闭模块                            关闭模块                       关闭模块                    关闭模块

多进程只是把单进程复制了多份,各个子进程间无法共享数据等。

3、多线程

                        模块初始化

请求初始化                         请求初始化                    请求初始化                请求初始化

    执行脚本                            执行脚本                      执行脚本                   执行脚本

关闭请求                            关闭请求                      关闭请求                   关闭请求

                    关闭模块

全局变化可以在初始化的时候建立,并且只建立一次。

PHP扩展之smart_str分析

php_smart_str_public.h
在php_smart_str_public.h文件中我们找到了如下定义:

1
2
3
4
5
typedef struct {
              char *c;
              size_t len;
              size_t a;
} smart_str;

其中,size_t的定义 为:typedef unsigned int size_t;
char *c 表示整个字符串
size_t len表示整个字符串的实际长度
size_t a 表示这个字符的总长度(smart_str的字符串总长度总是以128字节的倍数增加)

从php_smart_str.h文件所在的源码中我们可以看出它的空间分配具体有如下特点:
1、在初始化时,如果所给字符串的长度小于SMART_STR_START_SIZE(定义为78字节),则初始smart_str的总长度为78字节(即a值),并给它分配空间;如果所给字符串的长度大于SMART_STR_START_SIZE,则将此长度上加上SMART_STR_PREALLOC(定义为128字节)作为smart_str的总长度,并为之分配空间。
2、在已有字符串后添加时,如果添加后的总长度小于当前smart_str->a的值,则无需分配空间,否则,在smart_str新的长度的基础上再加上128字节的空间。
这样有些类似于操作系统中内存以页为单位分配,它的好处是对齐内存地址,提高访问速度。
在php_smart_str.h文件中,我们可以看到一些针对smart_str类型的宏定义的操作方法,方法列表如下:

smart_str_0 如果字符串存在,给字符串的最后添加’\0’;
smart_str_alloc 在初始化和添加字符串时进行空间的分配;
smart_str_appends_ex(dest, src, what) 在一个smart_str变量后面添加一个字符串,它分为两种空间分配方法,当what为真是,它使用__zend_realloc函数,此函数为永久分配内存,在无法分配内存时会报Out of memory的,当what为假时,它使用标准空间分配,即ZendMM所我有的分配函数erealloc(ptr, size)。
smart_str_appends(dest, src) 在一个smart_str变量后面添加一个原生的字符串
smart_str_appendc(dest, c) 在一个smart_str变量后面添加一个字符
smart_str_free(s) 消除smart_str变量,释放所占内存
smart_str_appendl(dest, src, len) 在一个smart_str变量后面添加一个长度为len的字符串
smart_str_append(dest, src) 在一个smart_str变量后面添加一个smart_str字符串
smart_str_append_long(dest, val) 将一个long的数字转化成字符串后添加到smart_str的后面smart_str_append_unsigned(dest, val) 将一个unsigned long的数字转化成字符串后添加到smart_str的后面
smart_str_appendc_ex(dest, ch, what) 与smart_str_appends_ex类似,所不同的是它添加的是一个字符。
smart_str_setl(dest, src, nlen) 将一个长为len的原生的字符串强制转化为一个smart_str
smart_str_sets(dest, src) 将一个原生的字符串强制转化为一个smart_str

【经典代码】
数字转字符串:

1
2
3
4
5
6
7
8
9
char __b[32];             
int __num = 28790;
char *__p;
__p = __b + sizeof(__b) - 1;
*__p= '\0';                                                                                                                                                                                     
do{                                                                                                                                                                                        
 *--__p = (char) (__num % 10)+ '0';                                                                                                 
__num /=10;                                                                                                                                                                       
} while (__num > 0);

获取远程大文件部分内容的方法

获取远程大文件部分内容的方法

【需求】
取远程文件的一部分,文件是以换行隔开,并且这个文件可能比较大

【第一次的方案】
这是我的第一个方案,也是很傻很天真却正确的方案,
这个在当时没有考虑到文件会比较大,而且也没有考虑下载时间过长,导致程序出错!
解决方案是使用PHP中的file()函数,这样会得到一个以行为单位的数组,然后是对这个数组的处理
file函数也可以使用file_get_contents代替,只是需要使用explode函数处理一下。

【优化后的方案】
由于我们所要取的数据不是全部,只是其中的一部分,我们可以只取其中的一部分,
开始使用fseek函数定位文件指针,只是它不支持URL地址
后来想到文件下载中的断点续传,在HTTP协议中存在Range字段,
Range是在 HTTP/1.1(http://www.w3.org/Protocols/rfc2616/rfc2616.html)里新增的一个 header field,也是现在众多号称多线程下载工具(如 FlashGet、迅雷等)实现多线程下载的核心所在。
Range 的规范定义如下:
ranges-specifier = byte-ranges-specifier
byte-ranges-specifier = bytes-unit “=” byte-range-set
byte-range-set = 1#( byte-range-spec | suffix-byte-range-spec )
byte-range-spec = first-byte-pos “-” [last-byte-pos]
first-byte-pos = 1*DIGIT
last-byte-pos = 1*DIGIT

一些其它介绍可以移步:http://minms.blogbus.com/logs/39569593.html
或者直接查看RFC

我们使用文件记录上次访问的位置,下次直接从这个位置访问
使用PHP的fsockopen函数实现获取大文件部分内容的代码如下:

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
<?PHP
header("Content-type:text/html;charset=UTF-8");
 
$host = "downloads.php.net";
$port = 80;
$path = "/johannes/php-5.3.1RC1.tar.bz2";
$start = 0;
$length = 100;
 
$content = get_remote_content($host, $port, $path, $start, $length);
$content_length = strlen($content);
 
if (!empty($content)) {
    $file = array();
    if (preg_match("/Content-Length:.?(\d+)/", $content, $matches)) {
        $header_length = $matches[1];
        $content = substr($content, $content_length - $header_length);
 
        if (!empty($header_length)) {   //  更新start,可能需要记录当前位置
            $start = $start + $header_length;
        }
    }
}
echo $content;
 
//echo xdebug_time_index();
die();
 
 
 
/**
 * 获取远程文件内容
 * @param <type> $host 主机
 * @param <type> $port 端口
 * @param <type> $path 路径
 * @param <type> $start 开始位置
 * @return <type> 远程部分内容
 */
function get_remote_content($host, $port = 80, $path = "/", $start = 0, $length = -1) {
    $fp = fsockopen($host, $port, $errno, $errstr);
    if (!$fp) {
        return false;
    }
 
    $range = $start . "-";
    if ($length != -1) {
        $range .= $start + $length;
    }
 
    $out = "GET " . $path . " HTTP/1.1\r\n";
    $out .= "Host: " . $host . "\r\n";
    $out .= "Range:bytes=" .$range . "\r\n";  //  取start之后的内容
    $out .= "Connection: Close\r\n\r\n";
    fwrite($fp, $out);
    $buffer = '';
    while (!feof($fp)) {
        $buffer .= fgets($fp, 4096);
    }
    return $buffer;
}