读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

1.emtpy、isset、is_null的区别

  isset()检测一个变量是否已声明且值不为NULL,并且声名一个变量没有赋值也算false,但是空字符串或者空格为true
  empty()用来检测一个变量是否为空,当变量为空字符串、false、空数组、null、0、‘’以及被unset删除后的变量
  is_null()判断变量内容是否是null,是isset()函数的反函数,区别是isset()可以应用到未知变量,但是is_null只能针对已声名变量

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

2.超级全局数组

  PHP执行时会自动将当前脚本需要收集的数据分类保存在这些超级全局数组中

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

     $GLOBALS超级全局数组可以让我们在函数里访问全局变量

     例如

 <?php
 $globalName='全局变量';
 function say(){
 echo $GLOBALS['globalName'];
 }


 3.global关键字与global数组的区别
 $GLOBALS['var']是外部的全局变量本身
 global $var是外部$var的同名引用或者指针
 

<?php
$val=1;
function test(){
    //1、unset($GLOBALS['val']);
    global $val;
    unset($val);
}
test();
echo $val;


1.的时候$val变量被删除  
global $val 与&GLOBALS['val']等价,相当于调用外部变量的一个别名,所以上面代码中的$val和$GLOBALS['val']指的是同一个变量
注:php的全局变量和c有点不同,在c的全局变量在函数体内无效,而在php中,在函数中想要调用外部的全局变量可用global什么,php的
'全局'不是指整个网站,而是应用于当前页面,包括include或require的全部文件
结论:$GLOBALS['var']是外部的全局变量本身
      global $var是外部$var的同名引用或者指针

4.静态变量
  函数内的局部变量执行时存在,完毕后会在内存里直接删除,但是如果想在函数调用保存上次变量执行的结果,以便下次执行时使用,这时就用静态变量,当脚本运行完毕退出的时候,静态变量也会销毁,这一点和全局、局部变量特性相同
5.require_once要慢于require,使用autoload速度最快,在实际运营环境中,使用error_reporting(0)禁止所有的错误显示
6.匿名函数
 

<?php
$greet =function($name){
 echo "$name";    
};


array_map('函数',数组)   数组内的每个元素都将使用之前的函数遍历一遍
而在匿名函数中 array_map(function(value){},$names);
PHP在默认情况下,匿名函数不能调用所在代码块的上下文变量,而需要通过使用use关键字。
function getMoney() {
    $rmb = 1;
    $dollar = 6;
    $func = function() use ( $rmb ) {
        echo $rmb;
        echo $dollar;
    };
    $func();
}
//输出:
//1
//报错,找不到dorllar变量
7.is_callable与method_exists函数  都是用来检查一个对象里的方法是否存在
  在method_exists(array(对象,'方法'))函数中判断一个私有或者保护方法,能够得到正确的返回,但是执行的时候会得到一个错误警告
  而is_callable(array(对象,'方法'))函数接受一个回调参数,如果在当前作用域可以执行就返回true
8.文件处理
$handle=fopen("/var/logs/somefile.txt",'r');
如果处理意见存在的文件,也不想删除它原来的内容:
  r : 只读。指针定位在文件的开头,如果文件不会报错。
  r+: 读/写。指针定位在文件的开头,如果文件不存会报错。
如果想新创建一个文件或代替现有文件:
  w : 只写。打开并清空文件的内容,如果文件不存在,则创建新文件。
  w+: 读/写。打开并清空文件的内容,如果文件不存在,则创建新文件。
php允许我们使用两种追加写入文件的模式:
  a-:追加模式,如果不存在,则尝试创建它
  a+:读/追加模式,游标置于文件尾部,将从文件末尾开始追加(写),如果该文件不存在,则创建它
谨慎的文件写操作:
  x : 只写。创建新文件。如果文件以存在,则返回 FALSE。
  x+ : 读/写。创建新文件。如果文件已存在,则返回 FALSE 和一个错误。
  大多数读取文件场景,都是读取文件的每一行后进行操作,这时就可以使用file()函数读取整个文件到一个数组中

  $lines=file('1.txt');
  foreach($lines as $line =>$l){
  echo "Line #{$line_num}:".$line."n";
  }


file()函数可选参数:
include_path    可选。如果也想在 include_path 中搜寻文件的话,可以将该参数设为 "1"。
context可选。规定文件句柄的环境。context 是一套可以修改流的行为的选项。若使用 null,则忽略。

file_get_contents(path,include_path,context,start,max_length)

可以接受这几个参数:start:规定在文件中开始读取的位置 max_length:可选。规定读取的字节数
file_get_contents 获取远程文件时会把结果都存在一个字符串中 fiels函数则会储存成数组形式,这个函数是一次性读取所有文件内容并显示出来,但是如果文件超大会导致php占很大的内存了。

注:下面给大家介绍下fopen()和file_get_contents()打开URL获得网页内容的用法区别
在php里,要想打开网页URL获得网页内容,比较常用的函数是fopen()和file_get_contents()。如果要求不苛刻,此两个函数多数情况下是可以根据个人爱好任意选择的,本文谈下此两函数的用法有什么区别,以及使用时需要注意的问题。
fopen()打开URL

下面是一个使用fopen()打开URL的例子:  

 <?php

    $fh = fopen('http://www.php.cn/', 'r');
    if($fh){
    while(!feof($fh)) {
    echo fgets($fh);

    }
    }
?>


从此例子可以看到,fopen()打开网页后,返回的$fh不是字符串,不能直输出的,还需要用到fgets()这个函数来获取字符串。fgets()函数是从文件指针中读取一行。文件指针必须是有效的,必须指向由 fopen() 或 fsockopen() 成功打开的文件(并还未由 fclose() 关闭)。
可知,fopen()返回的只是一个资源,如果打开失败,本函数返回 FALSE 。
file_get_contents()打开URL
下面是一个使用file_get_contents()打开URL的例子:

<?php

$fh= file_get_contents('http://www.php.cn/');

echo $fh;

?>


从此例子看到,file_get_contents()打开网页后,返回的$fh是一个字符串,可以直接输出的。
通过上面两个例子的对比,可以看出使用file_get_contents()打开URL,也许是更多人的选择,因为其比fopen()更简单便捷。
不过,如果是读取比较大的资源,则是用fopen()比较合适。

readfile()函数不像之前两个函数很方便抓取远端文件,这是直接的方式来获取缓冲区中的文件内容

<?php
$file="1.txt";
$bytesRead=readfile($file);
echo $bytesRead;

fgets()函数可以帮我们读取文件时从文件指针的位置开始读取一行,并作为一个字符串返回,也可以指定想让他读取的字节长度
例如:

$file="1.txt";
$hadle=fopen($file,"rt");
if($handle){
    while(!feof($handle)){ //检查是否到达文件末尾,如果指针到了EOF或者出错返回TRUE,否则返回一个错误(包括socket超时),其他false
          $buffer=$fets($handle,8192)//8kb
          echo $buffer;
    }
    fclose($handle);
}

fread()函数,主要是来安全读取二进制文件,它需要一个文件句柄和文件指针读取字节的长度
例如:安全读取二进制文件

$file='1.txt';
$handle=fopen($file,"r");
$contents=fread($handle,filesize($file));
fclose($handle);

例如:安全读取远端二进制文件

$hadle=fopen("http://www.2.com/1.gif","rt");
$contents='';
if($handle){
    while(!feof($handle)){  
    $contents=$contents.fread($handle,8192);
    }
    fclose($handle);
}

9.局部goto语句(无法跳出一个函数或类方法)

<?php
goto a;
echo 'Foo';
 
a:
echo 'Bar';
?>


输出 :Bar
10手册上的小瑕疵

echo "hollo".getInfo()."还有:".showInfo();
这段代码的执行过程:
 1>创建一个新的临时字符串
 2>把hollo加入字符串
 3>然后调用getInfo()函数返回的内容加入字符串
 4>创建一个新的临时字符串
 5>放入第一次创建的字符串
 6>把还有加入字符串
 7>调用showInfo()返回的内容加入字符串
 8>打印
优化:echo "hollo",getInfo(),"还有:",showInfo();

 

面向对象可实现的目标

                      容易在已有代码的基础上扩展,代码重构和扩展
                      允许类型微调,以在方法中对这些变量进行权限控制
                      结合设计模式,能够解决大多数软件设计的问题,扩展性好,调试更容易

虽然会损耗一些性能,但是面向对象的开发重要性在于封装,它将问题分割成小块,容易理清并易解决

1.构造方法:function_construct()和类名相同的方法 ,但是PHP虚拟机会先执行前一个
2.接口:接口就是一个空类,只包含方法的声明,任何类要实现接口,必须完成里面定义的所有方法
3.抽象类:当声明抽象方法的时候,意味着子类必须重写该方法,一个抽象的方法不包含任何内容
4.魔术方法: 
  1> __get() __set() __call()来存取类中没有定义的成员方法和属性
     当写入一个不存在或不可见的属性时,执行__set($name,$value)方法
     当调用一个不存在或者不可见的属性时,执行__set($name)方法
     当试图调用类中一个不存在或者不可见的方法,执行__call($method_name,$parameters)方法,接受两个参数,用来存放试图调用方法名称及参数
       (参数会被放在一个与该参数同名的数组中)
  2>__sleep()方法在序列化一个实例的时候被调用,必须返回一个数组或者对象(一般是返回的是当前对象$this),返回
        的值将会被用来做序列化的值,如果不返回这个值,则表示序列化失败,也意味着反序列化不会触发__wakeup()事件
    __wakeup()则是在反序列化的时候被调用

    $i=new 类;
    $si=serialize($i);
    unserizlize($i);
  3>__toString() 可以把类的实例转化为字符串,这个成员方法调用并打印当前类中构建器中的值,在这个结构中,调用了公共的字符串操作,这样的字符串连接也就形成了字符串
  4>__autoload() 当访问一个还未定义的类,这个方法开始将这个类名作为文件类参数来调用该类,如果能够成功引入该类,则顺利执行下去,如果没有调用到该类,PHP引擎则抛出一个fatal错误,停止该脚本的执行
5.PHP单继承的问题  traits  
<?php
    trait Drive {
        public $carName = 'trait';
        public function driving() {
            echo "driving {$this->carName}\n";
        }
    }
    class Person {
        public function eat() {
            echo "eat\n";
        }
    }
    class Student extends Person {
        use Drive;
        public function study() {
            echo "study\n";
        }
    }
    $student = new Student();
    $student->study();
    $student->eat();
    $student->driving();
  1> 如果Trait、基类和本类中都存在某个同名的属性或者方法,最终会保留哪一个呢? 
    当方法或属性同名时,当前类中的方法会覆盖 trait的 方法,而 trait 的方法又覆盖了基类中的方法。
  2> 如果要组合多个Trait,通过逗号分隔 Trait名称:
     use Trait1,Trait2
  3> 如果多个Trait中包含同名方法或者属性时,会怎样呢?
    是当组合的多个Trait包含同名属性或者方法时,需要明确声明解决冲突,否则会产生一个致命错误。使用insteadof和as操作符来解决冲突,insteadof是使用某个方法替代另一个,而as是给方法取一个别名,具体用法请看代码:
        class Class1 {
        use Trait1, Trait2 {
            Trait2::hello insteadof Trait1;
            Trait1::hi insteadof Trait2;
        }
    }
   4> as关键词还有另外一个用途,那就是修改方法的访问控制:
          <?php
        trait Hello {
            public function hello() {
                echo "hello,trait\n";
            }
        }
        class Class1 {
            use Hello {
                hello as protected;
            }
        }
        class Class2 {
            use Hello {
                Hello::hello as private hi;
            }
        }
        $Obj1 = new Class1();
        $Obj1->hello(); # 报致命错误,因为hello方法被修改成受保护的
        $Obj2 = new Class2();
        $Obj2->hello(); # 原来的hello方法仍然是公共的
        $Obj2->hi();  # 报致命错误,因为别名hi方法被修改成私有的
    5>Trait 也能组合Trait,Trait中支持抽象方法、静态属性及静态方法

PHP输出缓冲区
   缓冲区:实际上是一个内存地址空间。它用来存储速度不同步的设备或者优先级不同的设备之间传输数据的区域。通过缓冲可
   以使进程之间的交互时间等待变小,从而使从速度慢的设备读取数据时,速度快的设备的操作进程不发生间断
   
   PHP的输出流包含很多内容,通常都是开发者要PHP输出的文本,这些文本大多是用echo或printf()函数来输出的
   

    1>任何输出内容的函数都会用到输出缓冲区
      这是指正常的PHP脚本,如果开发的是PHP扩展,使用的函数(C函数)可能会直接输出写到SAPI缓冲区层,不需要经过输出缓冲层(我们可以在PHP源文件 main/php_output.h了解这些C函数的API文档)
 

    2>输出缓冲区不是唯一用于缓冲输出的层,它实际上只是很多层中的一个,输出缓冲层的行为与使用的SAPI(Web或CLI)有关,不同的SAPI可能有不同的行为

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

                           缓冲逻辑关系图

最上端的两次就是我们通常所认识的‘输出缓冲区’


3>SAPI中的输出缓冲区,这些都是PHP中的层,当输出的字节离开PHP进入计算机体系结构中的更底层时,缓冲区又会不断出现(终端缓冲区(terminal buffer)、fast-cgi缓冲区、Web服务器缓冲区、操作系统缓冲区、TCP/IP栈缓冲区等)。
   PHP CLI的SAPI有点特殊,CLI也称命令行界面,它会将php.ini配置中output_buffer选项强制设置为0,这表示禁用默认PHP输出缓冲区,所以在CLI中,默认情况下你要输出的内容会直接传递到SAPI层,除非你手动调用ob_()类函数,并且在CLI中,implicit_flush的值也会被设置为1,
   注:我们经常混淆implicit_flush的作用,php的源代码已说明一切:当implicit_flush被设置为打开(值为1)时,一旦有任何输出写到SAPI缓冲区,它都会立刻刷新(flush,意思把这些数据写到更底层,并且缓冲区会被清空)
   也就是说任何数据到CLI SAPI中时,CLI SAPI都会立即将这些数据仍到它的下一层去,一般会是标准输出管到,write()和fflush()这两个函数就是负责做这件事情的

默认PHP输出缓冲区:如果使用不同于CLI的SAPI,比如PHP-FPM,会用到下面3个与缓冲区相关的php.ini配置选项
   output_buffering
   implicit_flush
   output_handler
 首先不能再运行时使用ini_set()函数修改这几个选项的值,因为这些值会在PHP程序启动的时候,还没有运行任何脚本之前解析,所以在运行时可以使用ini_set()改变值,但是并不会生效。我们只能通过编辑php.ini文件或者是在执行PHP程序的时候使用-d选项才能改变它们的值
 默认情况下,PHP发行版会在php.ini中会把output_buffering设置为4096个字节,如果将它的值设置为ON,那么默认的输出缓冲区的大小为16KB
   在Web应用环境中对输出的内容使用缓冲区对性能有好处
      默认的4K的设置是一个合适的值,意味着你可以先写入4096个ASCLL字符,然后在与下面的SAPI层通信,并且在Web应用环境中,通过Socket一个字节一个字节地传输消息的方式对性能并不好,更好的方式是把所有内容一次性传输给服务器,或者至少是一块一块地传输,层与层之间的数据交换次数越少,性能越好,应该总是保持输出缓冲区处于可用状态,PHP会负责在请求结束后把它们中的内容传输给终端用户,开发者不用做任何事

      implicit_flush默认是设置为关闭,这样的话新数据写入就不会刷新SAPI,对于FastCGI协议,刷新操作是每次写入后都发送一个FastCGI数组包,如果发送数据包之前先把FastCGI的缓冲器写满会更好。如果需要手动刷新SAPI的缓冲区,使用flush()函数,如果想写一个就刷新一次可以设置implicit_flush或者调用一次ob_implicit_flush()函数
      推荐使用配置:
      output_buffering=4096
      implicit_flush = Off/no
      要修改输出缓冲区的大小,硬确保使用的值是4/8的倍数,它们分别是32/64位操作系统
      
      output_handler是一个回调函数,它可以在缓冲区刷新之前修改缓冲区中的内容

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

       缓冲区中的内容会传递给你选择的回调函数(只能用一个)来执行内容转换的工作,所以说如果想获取PHP给Web服务器以及用户的内容,可以使用输出缓冲区回调,输出是指:消息头、消息头。HTTP的消息头也是输出缓冲区层的一部分
  消息头和消息体
      实际上,任何与消息头的输出有关的PHP函数(header()、setcookie()、session_start())都使用内部的sapi_header_op函数,这个函数只会把内容写入消息头缓冲区中。 当我们如使用printf()函数的时候,内容会先被写入到输出缓冲区(可能是多个),当这个输出环城区的内容需要被发送的时候,PHP会先发送消息头,然后发送消息体。PHP为了搞定了所有的事情,如果想自己动手,那就只能禁掉输出缓冲区

  用户输出缓冲区:

      如果想使用默认PHP输出缓冲区层,就不能使用CLI,因为它已经禁用了这个层

/*launched via php -d output_buffering=32 -d implicit_flush=1 
 * */
echo str_repeat('a',31);
sleep(3);
echo 'b';
sleep(3);
echo 'c';
?>

   默认输出缓冲区的大小设置32字节,程序运行会先写入31字节,然后休眠,然后在写入1一个字节,这个字节填满缓冲区,它会立即刷新自身,把里面的数据传递给SAPI层的缓冲区,因为把implicit_flush设置为1,所以SAPI层的缓冲区也会立即刷新到下一层,所以输出aa...b,然后休眠,然后在输出一个字节,此时缓冲区有31空字节,但是脚本执行完毕,所以包含这个字节的缓冲区也会立即刷新,从而会在屏幕输出c

  用户输出缓冲区:通过ob_start()创建,我们可以创建多个这种缓冲区(直到内存耗尽为止),这些缓冲区组成一个堆栈结构,每个新建缓冲区都会堆叠到之前的缓冲区上,每次当它被填满或者溢出,都会执行刷新操作,然后把其中的数据传递给下一个缓冲区

  function callback($buffer)
{
  // replace all the apples with oranges
  return ucfirst($buffer);
}
 function callback1($buffer)
{
  // replace all the apples with oranges
  static $a=0;
  return $a++.'-'.$buffer."\n";
}
ob_start('callback1',10);
ob_start("callback",3);
echo "fo";
sleep(2);
echo 'o';
sleep(2);
echo "barbazz";
sleep(2);
echo "hello";

按照栈的先进后出原则,任何输出都会先存放在缓冲区2中。缓冲区2的大小为3个字节,所以当第一个echo语句输出的字符串'fo'会先存放在缓冲区2中,还差一个字符,当第二个echo语句输出'o'后,缓冲区2满了,所以它会刷新(flush)。在刷新之前先调用ob_start()的回调函数,这个函数将缓冲区的首字母转换成大写,所以输出为‘Foo’,然后它会被保存在缓冲区1中,第三个输出为‘barbazz’,它还是会先放在缓冲区2中,这个字符串有7个字节,缓冲区2已经溢出了,所以它立刻刷新,调用回调函数得到的结构是‘Barbazz’,然后传递给缓冲区1中,这个缓冲区1中保存了'FooBarbazz'这十个字符,缓冲区1会刷新,同样的先会调用ob_start()的回调函数,缓冲区1的回调函数会在字符串签名添加型号,所以第一行是'0-FooBarbazz',

    最后一个echo语句输出一个字符串'hello',它大于三个字符,所以会触发缓冲区2,因为此时脚本执行完毕,所以也会立即刷新缓冲区1,得到 ‘1-Hello’。

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

 因此使用echo函数如此简单的事情,如果涉及缓冲区和性能也是复杂的,所以要注意使用echo输出内容的大小,如果缓冲区配置与输出内容相似,那么性能会比较优良,如果缓冲器配置小于输出内容,需要在应用中输出的内容做切分处理。

输出缓冲区的机制:主要是在5.4以后整个缓冲层都被重写,我们自己开发PECL扩展时,可以声明属于自己的输出缓冲区回调方法,这样可以于其他PECL扩展做区分,避免产生冲突

输出缓冲区的陷阱

      有些PHP的内部函数也使用了输出缓冲区,它们会叠加到其他的缓冲区上,这些函数会填满自己的缓冲区然后刷新,或者返回里面的内容,比如print_r()、higglight_file()和highlight_file::handle()都是此类,所以不应该在输出缓冲区的回调函数中使用这些函数,这样会导致未定义的错误

     同样的道理,当PHP执行echo、print时,也不会立即通过tcp输出到浏览器,而时将数据先写入PHP的默认缓冲区,我们可以理解PHP有一套自己的输出缓冲机制,在传送给系统缓存之前建立一个新的队列,数据经过该队列,当一个PHP缓冲区写满以及脚本执行逻辑需要输出时,脚本会把里面的数据传输给SAPI浏览器

    echo/print->php输出缓冲区->SAPI缓冲区->TCP缓冲区->浏览器缓冲区->浏览器展示

     ob_flush()和flush()区别

        ob_flush():把数据从php的缓冲区中释放出来

        flush():把不再缓冲区中的或者说是被释放出来的数据发送到浏览器,严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.

      在nginx中ob_flush和flush两个都失效

        解决办法:  发现在nginx的配置中,有如下的设置

              fastcgi_buffer_size 128k;

     fastcgi_buffers 8 128k;

Nginx会缓冲PHP输出的信息,当达到128k时才会将缓冲区的数据发送给客户端,那么我们首先需要将这个缓冲区调小

比如:

     fastcgi_buffer_size 4k;

     fastcgi_buffers 8 4k;

并且,必须禁用gzip

             gzip off;

然后,在php中,在ob_flushflush前,输出一段达到4k的内容,例如:

            echo str_repeat(‘ ‘, 1024*4);

到此,PHP就可以正常通过ob_flushflush逐行输出需要的内容了。

输出缓冲区实践

    1> 通过ob_start()函数手动处理PHP缓冲区机制,这样即便输出内容超过配置参数大小,也不会把数据传输给浏览器,ob_start()将PHP缓冲区空间设置到足够大,只有脚本执行结束后或调用ob_end_flush()函数,才会把数据发送给浏览器 

for($i=0;$i<10;$i++){
echo $i.'<br/>';
sleep($i+1);
}

 执行之后不会每隔几秒就有输出,知道脚本循环结束后,才会一次性输出,这是因为数据量太小,输出缓冲区没有写满

2>当修改output_buffering=0

for($i=0;$i<10;$i++){
echo $i.'<br/>';
flush();
sleep($i+1);
}

 因为缓冲区的容量设置为0,禁用PHP缓冲区机制,这是我们在浏览器看到断断续续输出,而不必等到脚本执行完毕才看到输出,这是因为数据没有在缓存中停留

3>我们把参数修改为output_buffering=4096,输出数据大于一个缓冲区,不调用ob_start()函数

    首先先输出一个4k的内容记下来加本来输出的内容:   

for($i=0;$i<10;$i++){
echo   echo str_repeat(' ', 1024*4*8).$i<br/>;
sleep($i);
}

发现可以HTTP连接未关闭,可以看到间断输出,尽管启用了PHP输出缓冲区机制,但是也不是一次性输出,这还是因为PHP缓冲区空间不够,每写满一个缓冲区,数据就会发送到浏览器。

4>参照上例子,这次我们调用ob_start()

ob_start(); //开启PHP缓冲区
for($i=0;$i<10;$i++){
echo   echo str_repeat(' ', 1024*4*8).$i<br/>;
sleep($i);
}

等到服务端脚本全部处理完,响应结束才会看到完整的输出,在输出前浏览器会一直保持空白,这是因为,PHP一旦调用了ob_start()会将PHP缓冲区扩展到足够大,知道ob_end_flush函数调用或者脚本运行结束才发送PHP缓冲区中的数据到客户端浏览器

  可以通过tcpdump命令监控TCP的报文,来观察一下使用ob_start()和没有使用它的区别

 总结:ob_start激活output_buffering机制,一旦激活,脚本不再直接输出给浏览器,而是先暂时写入PHP缓冲区

            PHP默认开启out_buffering机制,通过调用ob_start函数把output_buffering值扩展到足够大,也可以通过$chunk_size来指定output_buffering的值,$chunk_size默认值是0,表示直到脚本运行结束后,PHP缓冲区中的数据才会发送到浏览器,若设置了$chunk_size的大小,则只要缓冲区达到这个值,就会发送给浏览器你

           可以通过指定output_callback参数来处理PHP缓冲区的数据,比如ob_gzhandler()将缓冲区中的数据压缩后传送给浏览器,ob_get_contents()是获取一份PHP缓冲区中的数据拷贝

ob_start();
echo date('Y-m-d h:i:s');

$output=ob_get_contents();
ob_end_flush();
echo '<!output>'.$output;

 后者是从ob_get_contents取的缓冲区的内容

 ob_end_flush()与ob_end_clean(0这两个函数都会关闭输出缓冲,区别是前者只是把PHP缓冲中的数据发生给客户端浏览器,而后者将PHP缓冲区中的数据删掉,但不发送给客户端,前者调用之后数据依然存在,ob_get_contents()依然可以获取PHP缓冲区中的数据拷贝

输出缓冲与静态页面

     大家都知道静态页面的加载速度快,不用请求数据库,以下就是生成静态页面的脚本代码

echo str_pad('',1024);//使缓冲区溢出
ob_start();//打开缓冲区
$content=ob_get_contents();//获取缓冲区内容
$f=fopen('./index.html','w');
fwrite($f,$content);//写入到文件
fclose($f);
ob_end_clean()清空并关闭缓冲区

  在一些模板引擎和页面文件缓冲中ob_start()函数被使用,如 WP、Drupal、Smarty,例如:

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

在Wp经常在主题目录header.php看到类似的代码:

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

只有一个flush(),它的所在的位置就是告诉浏览器那一部分的缓存需要更新,即页面头部以上部分需缓存

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

以及Wp的部分代码,可以看到,在缓冲区开启时,加入自己的回调方法
 内容压缩输出:就是把输出到客户端浏览器的内容进行压缩

好处:降低客户端对服务器出口带宽的占用,提升带宽的利用率。降低Web服务器(如Nginx、Apache、Tomcat等)处理文本时引入的开销,用户端可以减少网络传输延时对用户体验的影响,降低浏览器加载页面内容时占用的内存,有利于改善浏览器稳定性 

ob_start('ob_gzhandler');//使用gz格式压缩输出
print'my contents';
ob_end_flush();

   之压缩当前脚本与缓冲区,对其他脚本没有影响,PHP还提供另外一种压缩方式,在php.ini修改

  zlib_output_compression=On

  这样输出时所有页面都以zlib的压缩方式输出,但是两者混用是毫无意义的,只会额外消耗CPU性能,让它压缩已经压缩好的内容,但是基于实践,使用PHP压缩效果并不是十分理想,通常做法是放在Web服务器,比如Apache启用defate、Nginx使用gzip的方式都比PHP端压缩效果好得多

   输出缓冲允许第三方库和应用框架(Laravel、TP等)开发者完全控制它们自己输出的内容。比如把他们放在一个全局缓冲区中处理,对于任何输出流的内容(如数据压缩)和任何HTTP消息头、PHP都以正确的书序发送,使用输出缓冲区能够有效地节省带宽,比如图片、字体、CSS、JS等前端内容,特别是限制前端框架也越来越来,让它使用户反应速度更快,从而有效提高系统性能

PHP缓存技术
 Session:信息保存在服务器端,用键值访问
 Cookie:通常为保存在客户端浏览器中的Session的键值
    PHP创始人在设计PHP的时候,也同时发明了‘完全不共享的架构’。也就是服务器端的Session可以保存在文件、数据库或内存缓存里,客户端Cookie保存当前SessionID或浏览器URL中,带一个加密字符串用来查询Session状态就可以了解当前会话状态,由此就可保证网站中用户的持续会话

    缓存实现的基本原理是将数据库查询结果以字符串序列化形式保存在磁盘文件中,打开时再反序列化,这样的效率会高于MySql数据库查询,特别是夺表连接查询时会特别明显    静态HTML与动态页面的结合:内容保存在MySql中,但想在前端访问的静态HTML页面,而且让它在一定时间自动更新
    代码实例:

  <?php 
  ob_start();//开启缓冲区
  $fileName='/cache/static.html';
  $filename=30;  //3秒缓存
  if(file_exists($fileName)&&time()-filename<filemname($fileName){
     include($fileName);
       echo "<!--published at ".date("H:i".filemtime($cachefile))."by Think Creative CMS --> \n";
         exit();
  }
   echo '<--此处是要生成静态的PHP脚本文件-->';
 echo filemtime($cachefile);
  $fp=fopen($fileName,'w');

  fwrite($fp,ob_get_contents());
   //将输出缓冲区的内容写到文件
  
  fcolse($fp);
  //关闭文件

  ob_end_flush();
    //将缓冲区内容立即输出到浏览器,并关闭缓冲区

   1>执行静态文件生成时,需要手工干预或者置于页面以<script src='1.php'></script>或<img src='build_static.php' height=1 widht=1>这种类似标签,在访问所在页面时触发
   2>定时运行的话可以放在linux下的cron或者windows的计划任务中运行,如果想24小时内生成页面我们去掉filename的代码判断
   代码实例:

  ob_start();//开启缓冲输出
   $fileName='/cache/static.html';

   $fp=fopen($fileName,'w');
   fwrite($fp,ob_get_contents());
   //将输出缓冲区的内容写到文件

  fcolse($fp);
  //关闭文件

  ob_end_flush();

随着cron的时间来定期生成一个static.html

数据级别缓存
  全页面静态化缺点:需要占用更多的存储空间,有时候无法预计某一页面的更新频率过高,会使这部分的硬盘IO开销更大
    对于一些交互并不是特别高,但是负载又很大的应用,就需要考虑使用缓存这一机制:
      1>数据缓存是在全动态的低效与全静态占用大量存储空间寻找一个平衡点。
      2>数据缓存可在最新的或是比较活跃的数据上使用,在节约存储空间的前提下,尽量提高数据缓存的使用效果
    代码实例:比如用户管理类叫my_auth,并且所有用户都使用id作为标识,比如保存的缓存内容就是'my_auth:users:123456',最后的数字是用户id

 

opcode缓存:在系统编译PHP时加入的缓存机制
    由于PHP是解释性语言,当解释器执行PHP脚本时会解析脚本代码,将它们生成可以直接运行的中间代码,称为ZendOpcode
    1>PHP脚本执行顺序与zend opcode
      zend引擎是PHP的编译引擎和执行引擎,当它在执行一段PHP脚本时,会做出以下4个步骤:
        scanning:扫描,将PHP代码转换为语言片段
        Parsing:解析,将Tokens转换成简单而有意义的表达式
        Compilation:编译,将表达式编译成Opcode
        Execution:顺次执行opcode,每次一条,执行完刷新内存后销毁

              读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

 Nginx或其他Web服务器把HTTP请求转发给PHP-FPM,PHP-FPM再把请求交给某个PHP子进程处理,PHP进程找到PHP脚本后执行,把脚本编译为opcode后生成响应,每次请求一个PHP脚本都要编译一次zend opcode,然后执行字节码。
   字节码缓存:能缓存预告编译好的字节码,减少应用的响应时间
     zend opcache:它将在PHP zend引擎的编译器和执行器之间运行,并负责对已编译的脚本进行代码优化,以便交给执行引擎,使执行速度更快,在PHP5.5版本以后被内置,但是默认没有启用

 如果是自己的PHP运行环境,需要在configure命令时包含如下:
  --enable -opcache
  编译好,须在php.ini文件制定Zend OpCache的扩展库的所在路径
  

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

opecache.memory_consumption=64  OpCode缓存分配的内存数据,单位为M
opcache.interned_strings_buffer=16 存储驻留字符串的内存量。PHP解释器会使用指针处理,保存在内存中,如果再次使用相同的字符串,PHP解释器会使用指针处理,以保证节省内存。在默认情况下,PHP驻留的字符串会在各个PHP进程中隔离,该选项能够让PHP-FPM进程池中的所有进程共享字符串存储,可以让PHP-FPM进程之间引用驻留字符串,从而节省更多内存
opcache.max_accelerated_files=4000 设置opcode缓存中最多保存多少个PHP脚本
opcache.validate_timestamps=1 设置为1,PHP会检查是否有更新。检查的时间间隔由opcache.revalidate_rreq选项指定,设置为0,PHP就不会检查PHP脚本的内容是否发生了变化,需要我们手工清除缓存操作码(建议在开发环境设置1,生产设置为0)
opcache.fast_shutdown=1 这个设置能够让opcode使用更快的退出步骤。把对象析构与内存释放交给Zend Engine的内存管理器来完成

Ioncube:ioncube是一个保护使用PHP编程语言编写的软件不被查看的工具。这些工具使用在编码之前编译为字节码的技术,从而消除了源代码,减少了运行时开销(加密)。还有一个名为ionCubeLoader的PHP扩展在运行时处理编码文件的读取和执行(解密)。
 与Zend Guard不同:不仅支持期限、注册码等加密方式,还支持对IP、MAC地址等复杂的加密算法,还可以加密除了PHP文件外的XML、JS等,如果要巡行ioncube Encode 加密的PHP软件 就要安装ioncube PHP loader,必须在Zend Optimizer之前运行

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

                     使用ionCube Encode 加密的PHP编译与执行过程

使用APC
  PHP APC提供两种缓存功能,编译缓存和用户数据缓存,即opcode,同是它还提供一些接口将用户数据驻留在内存中,称为apc_user_cache(apc_store和apc_fetch)函数操作读取、写入的
APC与Zend opcode:
  1>APC有数据缓存API,而Zend opcache没有
  2>APC能够回收旧的无效的脚本占用的内存,APC有内存管理器,可以将那些不再使用的脚本管理的内存进行回收,而Zend opcache不同,它将这一的内存标记为脏数据,不会回收他们,一旦内存占用到达配置的阈值,zend opcache就将自己重新启动
  鸟哥在这里有说明:http://www.laruence.com/2013/03/18/2846.html

使用deflate压缩页面: HTTP协议允许压缩传输数据,提升并节约带宽

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

  在Apache中的标准压缩扩展没款,以前称为gzip,这表示会对html、js等压缩后在输出到浏览器,在nignx配置文件中添加这个配置就可以了

 

内存数据库


  基于文件的缓存和基于内存的数据库有两个本质区别:首先基于内存的缓存需要一个中间件,即要访问系统内存,其次,实现内存的缓存容易丢失和有限的空间,此外,基于内存的缓存通常不会出现锁定,而基于文件的缓存有此问题或者有权限属性等
memcached特性
  是基于libevent库 一个非阻塞式的网络程序库开发的,libevent是一个事件触发的程序库,它将Linux的epoll、BSD类操作系统的kqueue等时间处理功能封装成统一的接口,即服务器的并发数量非常大的时候也能保持快速响应的能力
内存存储处理
    1>memcached 首先按照预先规定大小,将内存分割成各种尺寸的块(chunk),并把尺寸相同的块分成组(chunkgroup),从而解决内存碎片问题
    2>memcached根据收到数据大小,选择合适的slab,memcached中保存着slab内空闲的chunk列表,根据该列表选择chunk,然后缓存与其中,但是由于分配都是固定长度的内存块,因此无法有效利用分配的内存空间比如100字节放入128chunk中
memcached的数据处理算法
     当内存容量达到指定值,就会基于LRU(删除最近最少使用)算法自动删除不适用的缓存
    数据过期方式包括两种:lazy Expiration 和 LRU 。 内部不会监视记录是否已经过期,而是进行get时查看记录的时间错判断是否过去,这种技术成为(惰性)Lazy Expiration,所以memcached不会在过期监视上耗费CPU
     memcached会优先使用已经过期记录的内存空间,但也会发生追加新记录时内存不足的情况,此时LRU机制开始使用,当内存空间不足时(无法从slab class获取新的空间时),就从最近未被使用的记录中搜索,并将其空间分配给新的记录,从缓存的使用角度,该模型是十分理想的
memcached分布式结构与算法
    尽管memcached是分布式缓存服务器,但是服务端并没有分布式功能,所以说客户端通过一个分布算法和维护一个服务器列表来实现分布式,目前的分布式有两种:
     1>取模算法
     2>一致性算法
memcached解决Session共享问题
    修改php.ini
读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

或是在php脚本写:

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

如果是多个可以用逗号隔开:

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

PHP网络编程


 1.Socket原理 是由操作系统提供的通信层的一组API
     Socket位于TCP/IP协议的传输控制层,提供客户服务器模式的异步通信,即客户向服务器发出服务请求,服务器收到请求后,提供相应的反馈或服务,也就是两台主机之间通信的通道
       应用程序通过它来发送和接受数据,就像应用程序打开一个文件句柄,将数据读写到稳定的存储器上一样
        客户/服务器模式在操作系统的是主动请求方式:
        服务器处理步骤如下:
          1>打开一个通信通道并告知本地主机,它愿意在某一IP地址和端口上接收客户端请求
          2>等待客户请求达到该端口
          3>接收重复服务请求,处理该请求并发送应答信号
            接收到并发服务请求,要激活一个新进程来处理这个客户请求(如Linux中fork和exec)新进程处理此次客户请求,并不需要对其他请求做出应答,服务完成后,关闭此新进程和客户的通信链路,并终止服务
          4>返回第(2)步,等待另一个客户请求
          5>关闭服务器
        客户端的处理步骤:
          1>打开一通信通道,并连接到服务器所在主机的特定端口
          2>向服务器发送服务请求报文,等待并接收应答,继续提出请求
          3>请求结束,关闭通信通道并终止
        Socket是连接其他网络主机的一种方法,该主机可以使互联网或局域网上的任何一台计算机,创建Socket服务器,监听外部请求并制定端口提供连续的服务,通常作为一种服务或者一个系统守护进程持续运行

     在TCP/IP协议族中的主要Socket类型为:
                    1>流套接字(stream socket),传输层使用TCP协议,提供了一个可信赖的字节流服务
                    2>数据报套接字:传输层使用UDP 协议,最长以发送65500字节数据,
          TCP适合发送图片、文件或者其他信息必须全部结束和反馈(电子邮件)等服务
          UDP适合发送流数据、如音乐、视频数据流,UDP是无连接协议,但是TCP它可以遵守IP协议
     Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象
  2.Socket函数
    PHP提供了开发者 Socket API有两种: 内核中,只能做主动连接而无法实现端口监听相关功能  PECL扩展卡,支持监听和交互模式
    读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

该函数功能是初始化一个Socket套接字并连接到目标主机
 psockopen()函数实现的持久化连接,也被称为长连接

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

  它和socketopen函数功能相同,唯一区别就是建立的是长连接
  这两个函数执行后都会返回一个资源编号,这个资源几乎可以使用其他文件操作函数对其进行操作,如fgets()、fwrite()、fclose()等。比如fread()从函数指针读取指定长度自己

<?php
 $fp=fsockopen("www.baidu.com",80,$errno,$errstr,30);
 //$fp=fsockopen("udp://127.0.0.1",13,$errn,$errstr);  udp
 if(!$fp){
 	echo "$errstr($errno)<br/>n";
	 
 }else{
 	$out="GET/HTTP/1.1\n\n";
	//发送数据
	fwrite($fp,$out);
	while(!feof($fp)){
		echo fgets($fp,128);
	}
	
	fclose($fp);
 }
echo $fp;
?>

cURL核心技术

    主要是获取远程文件或传输文件,支持FTP\FTPS、HTTP\HTTPS等协议


PHP cURL参数:
  
  1>CURL_OPT_FOLLOWLOCATION和 CURLOP_MAXREDIRS
   第一个参数用来跟踪目标页面是否有重定向,目前只支持header重定向处理,对于后面的mata和js跳转则无法识别
   第二个参数用于定义最大的重定向跟踪次数,以防止过程总是进入同一个网站,死循环
 curl_setopt($ch,CURLOPT_FOLLOWCATION,TRUE); //跟踪header头重定向
 curl_setopt($ch,CURLOPT_MAMREDIRS,5);//设置重定向跟随为5次
   
   2>CURLOPT_USERAGENT
    用来定义用户代理的名称,比如我们模拟微信的内置浏览器访问某个网站:
    $agent='Mozilla/5.0 (iphon;CUP iphone).......';
    curl_setopt($ch,CURLOPT_USERAGENT,$agent);
    现在有一些网站会检测用户代理,然后决定是否提供服务,或代理的不同,显示不同的页面
    可以用自己的设备访问:
     echo $_SERVER['HTTP_USER_AGENT'];
   
   3>CURLOPT_NOBODY与CURLOPT_HEADER
     前者返回内容的主体内容,后者返回内容的头内容
     curl_setopt($ch,CURLOPT_HEADER,TRUE);
     curl_setopt($ch,CURLOPT_NOBODY,TRUE);
   
   4>CURLOPT_TIMEOUT与CURLOPT_CONNECTTIMEOUT
     设置cURL操作时等待目标服务器的响应时间比如目标服务器宕机这样会造成我们爬虫无线等待,其实常用在抓取一些访问量大的站点,返回较慢
     链接404等

     curl_setopt($ch,CURLOPT_TIMEOUT,30);

    5>CURLOPT_COOKIEFILE与CURLOPT_COOKIEJAR
      它能够帮我们在传输抓取过程中向服务器传递Cookie信息,可以使用前者定义之前存储Cookie的文件位置,会话完成时,cURL把新的Cookie写入到第二个指定文件中:
       curl_setopt($ch,CURLOPT_COOKIEFILE,'/usr/tmp/cookies.txt');//读取cookie文件
       curl_setopt($ch,CURLOPT_COOKIEFILE,'/usr/tmp/cookies.txt');//写入到cookie文件
    6>CURLOPT_HTTPHEADER
      它可让我们自定义在让目标服务器接受的header头内容,如告知目标服务器能接受的MIME、内容类型、用户代理及压缩类型等内容
       $header_arr[]='Mime-version:1.0';
       $header_arr[]='Content-type:text/html;charset=utf-8';
       $header_arr[]='Accept-encoding:commperss,gzip';

    7>CURLOPT_SSL_VERYFYPEER
      适用于服务器使用了SSL加密,及HTTPS时用到
       curl_setopt($ch,CURLOPR_SSL_VERYFYPEER,FALSE); //没有使用正式

    8>CURLOPT_SSL_VERSION
      用来定义目标服务器SSL版本相对于,使用的SSL版本2或3,但是过去SSL漏洞,腾讯已经废弃了2和3版本,改成了TLS
       curl_setopt($ch,CURLOPT_SSLVERSION,CURL_SSLVERSION_TLSv1);
    
    9>CURLOPT_USERPWD和CURLOPT_UNRESTRICTED_AUTH
       此选项适合于对方服务器有基本认证的情况,这两个选项分别加入基本认知用户密码
       curl_setopt($ch,CURL_USERPWD,"name:pwd");
    
    10>CURLOPT_POST与CURLOPT_POSTFILELDS
       模拟提交时重要参数
       curl_setopt($ch,CURLOPT_POST,TRUE);
       $data='name=post数据&';
       curl_stopt($ch,CURLOPT_POSTFIELD,$data);
      如果使用get方法的话,只需要在目标URL后加入查询的字符串可以实现
    
    11>CURLOPT_PORT
       设置连接的端口号

实例一:
        

   <?php
    $ch=curl_init() or die(curl_error());
    curl_setopt($ch, CURLOPT_URL,"http://www.baidu.com/");
    curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);//设置exec获取信息以字符流的形式返回
    curl_setopt($ch,CURLOPT_REFERER, "http://weibo.com/");
    curl_setopt($ch,CURLOPT_HEADER,1);//启用的时候会将头信息作为字符流输出
    $output=curl_exec($ch) or die(curl_error());
    if($output==FALSE){ //区分空格输出和布尔值FALSE
        echo curl_error($ch);
    }else{
        echo $output;
    }
    curl_close($ch);


    其中 $info=curl_getinfo($ch,CURLINFO_HTTP_CODE); 返回curl执行后这一请求的相关信息


实例二:使用cURL上传图片
 

    $url='';
     $file_path='1.jpg';
     $post=array('info'=>'123','file_contents'=>new \CURLFile(realpath($file_pat)));
    //接下来用post上传
     curl_setopt($ch,CURLOPT_POSTFIELDS,$post);

实例三:cURL批量处理

   $ch1=curl_init('http//aa.com');
      $ch2=curl_init('http//bb.com');
      curl_stopt($ch1,CURLOPT_RETURNTRANSFER,true);
       curl_stopt($ch2,CURLOPT_RETURNTRANSFER,true);
//两个cur资源加入$mh句柄中
       $mh=curl_multi_init();
       curl_multi_add_handle($mh,$ch1);
       curl_multi_add_handle($mh,$ch2);
//执行批处理等待全部完成
       $running=null;
       do{
       curl_multi_exec($mh,$running);
       }while($running);
//完成后,返回获取内容
       
       $r1=curl_multi_getcontent($ch1);
       $r2=curl_multi_getcontent($ch2);
//关闭句柄
       curl_muilti_remove_handle($mh,ch1);
       curl_muilti_remove_handle($mh,ch2);
  //  这个循环重复调用curl_muilti_exec() 这个函数是有阻塞的,但是它会尽可能减少执行


 实例四:模拟cookie登陆
     

 $ch=curl_init();
       curl_setopt($ch,CURLOPT_URL,'http://mis.example.com/index.php');
       curl_setopt($ch,CURLOPT_POST,1);
       curl_setopt($ch,CURLOPT_POSTFIELDS,'name=admin&pwd=123&action=login');
       //模仿浏览器行为,并保存cookie内容
       curl_setopt($ch,CURLOPT_COOKIEJAR,'cookie.txt');
       curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
       //执行请求,表单登陆
       curl_exec($ch);

      //设置文件下载的参数
       curl_setopt($ch,URLOPT_URL,'http://mis.example.com/index.php?file_id=2');
       //执行第二次请求 文件下载
       $content=curl_exec($ch);//为取得的文件内容
       //关闭
       curl_close($ch);

cURL的底层也是Socket
      

 

 

PHP测试与调优


 1.使用魔术常量:就是这个常量保存着当前PHP脚本运行时的状态

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

读书笔记:PHP和MySQL高性能应用开发 (2019.2.20-2019.3.4)

2.建立堆栈跟踪
   1>debug_zval_dump()  
     输出结果跟var_dump类似,唯一增加的一个值是refcount,就是记录一个变量被引用多少次,这是PHP的copy on write(写时复制)的机制的一个重要特点。
 3.活用日志
         为了节约磁盘IO操作,可以放在一个对象里,等对象析构的时候再执行物理写入操作
        简写代码:
       function logtext($text){
        $time =date('y-m-d h:i:s',time());
        file_put_contents('log.txt',$time."  ".$text."\n",FILE_APPEND);        
};

用户验证策略


   作为开发者,要充分了解PHP安全的必要性,特别是带有用户信息等敏感数据时。
   纯PHP验证
    1.自定义session:PHP的SESSION会话是将用户信息存储在服务器上,一般情况下,一个sessionID会存储在客户端的Cookie中,然后通过SessionID的时间维护Session生命周期的建立和保存
      注意:Session也可不需要使用cookie,sessionID可以通过GET和POST变量传递,该验证设置在配置文件修改
      好处

     1>.提高系统的安全性,并且在共享主机服务器上,如果没有进行特别的设定,所有网站站点都会使用同一个临时目录,意味着数十个程序都在同一个位置对文件进行读写操作。我们就很容易地编写一个脚本从这个目录读取会话文件夹中所有文件的内容

     2>.另外把会话数据放在数据库里还可以更方便地搜索Web站点会话更多的信息,我们可以查询会话的数量,还可以对会话数据进行备份 

     3>.如果站点运行与多个服务器上,在这种情况下,同一个用户在一个会话过程中可能会对不同的服务器上的多个页面发送请求,但是会话数据如果保存在某一个服务器上的文件里,就不能被这台服务器外的其他服务器上的页面所使用,所以除了数据库宝UC你,就没有其他解决的办法了

注:在共享主机上,设计安全问题的另一个技巧就是改变会话的目录,我们可以每次调用seeion_start()之前调用session_save_path(),当然要确保新目录存在并且具有合适的权限
    
    2.构造安全的Cookie:
         当用户登陆后,会将登陆的相关信息以纯文本的形式放在Cookie中,这样容易地被攻击者看并复制。
       1> 需要以下的cookie的内容进行加密处理
          UserID、用户最后登录的IP地址、一个时间戳
        第一个是用来了解是哪些用户登录,第二个是用来记录最后登录的IP地址,可以用它与服务端IP地址比较,如果IP地址不匹配,有可能发生Cookie被劫持,或者是用户在同一个地点使用笔记本里用其他无线接入点上网
        时间戳:是由来记录Cookie的创建日期和时间,这条记录有两个目的,第一个是可以检测用户多长时间没有登录,第二个是比较用户登录间隔时间,当发现用户很短时间在不同IP地址登录,对登陆加以限制
        
        2>三点信任度可以表示使用这个Cookie设计
          最信任:IP地址与最近的时间戳匹配
          较信任:IP地址匹配,但时间戳太旧,这表示用户已经离开计算机,属于无人值守
          最不信任:IP地址不匹配

          以下为自己总结的一个方法
            固定的数字不可靠,用户的密码字符串作为salt是更好的办法
            签名字符串:
              $sign = md5('$user_id+$user+_password[+浏览器UA[+IP地址[...]]]');
            存储到cookie里面的字符串为:
              $token = $user_id.','.$sign;
            然后你需要对用户记录做一个服务器端的缓存,缓存通过用户编号查询,里面至少要包含用户的密码
            这样的好处有:
                不存在私钥泄漏的问题,即使出问题也不会影响所有用户
                slat字符串泄漏就等同于用户的密码泄漏,逻辑上是严密的
                即使有人持有$token字符串,只要用户一修改密码,老的$token就马上失效了
            在此基础上还可以有其它的发挥,比如用户记录设计专门的一个字段:salt,每次用户登录成功时就生成一个随机字符串更新到salt内,数字签名计算用
                 记得把$user_salt也放到缓存里
                 $sign = md5('$user_id+$user_password+$user_salt+...');
            这样一来,每次用户重新登录之后,之前的$token一定会失效,不但安全性更高,而且还实现了每次只允许一个人登录使用 

          当出现不信任就要提示用户再次输入密码,输入正确后重置时间戳和IP地址
          作为安全的最后级别,加密有益于数据验证以及用户数据的安全,我们将UserId也同时添加到Session中,当Cookie和Session两端的UserId不匹配时候,可以证明有人伪装Cookie或者数据被破坏,这时,登陆的Session和Cookie被销毁

深度理解MySql驱动与存储引擎


 mysql采用基于组件的模块化设计,使用C/C++开发。架构是一个类似于子系统组成的架构,子系统紧密和高效配合。
  主要讲一下PHP与mysql关系
    在PHP5.3以后用C重写了连接MySQL的驱动库,称为mysqlnd,它是由一个完全用PHP许可证编写的,准守MYSQL通信协议的新驱动,不像之前的驱动编译过程中还需要连接MYSQL官网,然后分发当前主机。 并且支持mysqli客户端API库的持久连接。由于mysqlnd是PHP扩展,意味着它可以使用管理PHP内存和其他枯燥和扭矩,与PHP Zend引擎高度集成,充分使用PHP的流API(stream API)以及客户端缓存机制,执行速度更快、内存消耗更小。
    查看当前连接驱动:
 

    <php
              $mysqlnd=function_exists('mysqli_fetch_all');
        if($mysqlnd){
            echo 'mysqlnd enabled';
        }


    测试PDO中使用的驱动是否正常:

    if(strpos($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), 'mysqlnd')!==FALSE){
    echo 'PDO mysqlnd enabled';
}


 

 

上一篇:Python源码剖析 - 对象初探


下一篇:观察者模式