排行榜的的数据库设计

排行榜在游戏中非常常见的功能之一,在游戏中有各种排行榜,如工会活跃度,玩家的英雄战斗力排行等。当数据上亿时,如果使用数据库直排是致命的慢,远远超出用户接受的响应时间。也对数据库造成非常大的压力。本文将会讲述千万用户级别的用户排行系统的一些设计理念并讲述数据库直排以及使用桶排和内存数据优化排行榜。

在讲述设计前,有必要先了解一些基础理论,文章将会先讲述什么排行榜的类别,排行规则和排名分布,然后进一步结合以往写的一个简单的排行系统Nagi,讲述数据库直排和使用桶排技术,以及内存缓存技术等。

排行榜的类别

刷新频率

如果以排行榜的刷新频率来分类可分为及时排行榜,和周期排行榜。

及时排行榜
~~~~~~~~~~~

排行榜的排名能及时反映用户的排变名化,但可能是近似的排名。

周期性排行榜
~~~~~~~~~~~~~

排行榜将会在一定周期内刷新排名,如日排行,周排行,月排行等

准确性分类

精准排名
~~~~~~~~~~

能够准确的反应当前玩家的某段时间,或者当前的排名。

近似排名
~~~~~~~~

近似排名能够反映用户的排名变化和接近真实排名也许会稍稍低于真实排名,或者高于真实排名。总之可能与真实的排名有一定差别。

排行规则

排名规则,这里并不是如竞技场,使用交换排名的方式,一个新用户进入竞技场时只要简单的统计下当前竞技场用户数量就可以初始化其排名,随着玩家挑战高名次的玩家,如果胜利就交换名次这类规则。而是诸如工会活跃度可能是当前工会所有工会成员的活跃度总和作为工会活跃度、或工会所有玩家战斗力总和作为工会战斗力。这类因为最后由唯一属性(如工会活跃度,工会战斗力)决定排名的归为简单排名(唯一属性排名)。

你可能会为担忧如何计算工会的战斗力。那么考虑一个简单的游戏功能如签到排名,规则是用户每天签到将会记录用户最近连续签到的天数,如果某天用户忘记签到,那么用户签到天数将会从零开始重新计算,除非用户补签。如果用户签到天数越多,那么用户排名越高这类就是简单的排名,仅有单一属性决定玩家的排名。但是由于这个排名可能因为大多数用户都在游戏开始就持续的签名,这样就会有很多玩家排名一致,但为了保证每个用户都有不同的排名,于是将由用户id来区分排名,id越小排名越靠前,这类排名签到天数结合用户id就有多个属性决定排名就是复合属性排名。

用户排名的分布

在设计排名系统时一定要注意到用户排名的分布,正如上面讲到签到系统,是非常符合‘二八法则’的,大多数用户的排名将会非常接近或者相同。这类分布也可能会相近于正太分布。两端的用户越来越少,中间用户越来多。这样造成大量用户的排名相同。所以如果有可能应该制定比较好的游戏规则,使用户的排行分散均匀。

- 阅读剩余部分 -

KMP算法与朴素匹配

查找算法中朴素匹配之于KMP就类似冒泡排序之于快速排序

 function KMPMatch($src, $par) {
    $K = array(0);
    $M = 0;
    for($i=1; $i<strlen($str); $i++) {
        if ($str[$i] == $str[$M]) {
            $K[$i] = $K[$i-1] + 1;
            $M ++;
        } else {
            $M = 0;
            $K[$i] = $K[$M];
        }
    }
    for($i=0,$j=0; $i<strlen($src); ) {
        if ($j == strlen($par)) return $i-$j;

        if ($par[$j] === $src[$i]) {
            $j++;
            $i++;
        } else {
            if ($j === 0 && $par[$j] != $src[$i]) {
                $i++;
            }
            $j = $K[$j-1 >= 0 ? $j -1 : 0];
        }
     }
     return false;
 }

 function normalSearch($src, $par) {
     for($i=0; $i< strlen($src); $i++) {
         $k = $i;
         $j = 0;
         while($i<strlen($src) && $j<strlen($par) && $par[$j] == $src[$k]) {
             $k++;$j++;
         }
         if ($j == strlen($par)) return $k - $j;
     }
     return false;
 }

匹配内容包含较多重复字符时KMP算法明显比朴素匹配要快.

PHP图的邻接矩阵表示以及几种简单遍历算法

在web开发中图这种数据结构的应用比树要少很多,但在一些业务中也常有出现,下面介绍几种图的寻径算法,并用PHP加以实现.

佛洛依德算法,主要是在顶点集内,按点与点相邻边的权重做遍历,如果两点不相连则权重无穷大,这样通过多次遍历可以得到点到点的最短路径,逻辑上最好理解,实现也较为简单,时间复杂度为O(n^3);

迪杰斯特拉算法,OSPF中实现最短路由所用到的经典算法,djisktra算法的本质是贪心算法,不断的遍历扩充顶点路径集合S,一旦发现更短的点到点路径就替换S中原有的最短路径,完成所有遍历后S便是所有顶点的最短路径集合了.迪杰斯特拉算法的时间复杂度为O(n^2);

克鲁斯卡尔算法,在图内构造最小生成树,达到图中所有顶点联通.从而得到最短路径.时间复杂度为O(N*logN);

<?php  
/** 
 * PHP 实现图邻接矩阵 
 */  

class MGraph{  
        private $vexs; //顶点数组  
        private $arc; //边邻接矩阵,即二维数组         
        private $arcData; //边的数组信息  
        private $direct; //图的类型(无向或有向)  
        private $hasList; //尝试遍历时存储遍历过的结点  
        private $queue; //广度优先遍历时存储孩子结点的队列,用数组模仿  
        private $infinity = 65535;//代表无穷,即两点无连接,建带权值的图时用,本示例不带权值   
        private $primVexs; //prim算法时保存顶点  
        private $primArc; //prim算法时保存边  
        private $krus;//kruscal算法时保存边的信息   

        public function MGraph($vexs, $arc, $direct = 0){  
                $this->vexs = $vexs;  
                $this->arcData = $arc;  
                $this->direct = $direct;  
                $this->initalizeArc();  
                $this->createArc();   
        }  

        private function initalizeArc(){  
                foreach($this->vexs as $value){  
                        foreach($this->vexs as $cValue){  
                                $this->arc[$value][$cValue] = ($value == $cValue ? 0 : $this->infinity);  
                        }  
                }  
        }  

        //创建图 $direct:0表示无向图,1表示有向图  
        private function createArc(){  
                foreach($this->arcData as $key=>$value){  
                        $strArr = str_split($key);  
                        $first = $strArr[0];  
                        $last = $strArr[1];  

                        $this->arc[$first][$last] = $value;  
                        if(!$this->direct){  
                                $this->arc[$last][$first] = $value;   
                        }  
                }  
        }  

        //floyd算法  
        public function floyd(){  
                $path = array();//路径数组  
                $distance = array();//距离数组  

                foreach($this->arc as $key=>$value){  
                        foreach($value as $k=>$v){  
                                $path[$key][$k] = $k;  
                                $distance[$key][$k] = $v;  
                        }  
                }  

                for($j = 0; $j < count($this->vexs); $j ++){  
                        for($i = 0; $i < count($this->vexs); $i ++){  
                                for($k = 0; $k < count($this->vexs); $k ++){  
                                        if($distance[$this->vexs[$i]][$this->vexs[$k]] > $distance[$this->vexs[$i]][$this->vexs[$j]] + $distance[$this->vexs[$j]][$this->vexs[$k]]){  
                                                $path[$this->vexs[$i]][$this->vexs[$k]] = $path[$this->vexs[$i]][$this->vexs[$j]];  
                                                $distance[$this->vexs[$i]][$this->vexs[$k]] = $distance[$this->vexs[$i]][$this->vexs[$j]] + $distance[$this->vexs[$j]][$this->vexs[$k]];  
                                        }  
                                }  
                        }  
                }  

                return array($path, $distance);  
        }  

        //djikstra算法  
        public function dijkstra(){  
                $final = array();  
                $pre = array();//要查找的结点的前一个结点数组  
                $weight = array();//权值和数组  
                foreach($this->arc[$this->vexs[0]] as $k=>$v){  
                        $final[$k] = 0;  
                        $pre[$k] = $this->vexs[0];  
                        $weight[$k] = $v;  
                }  

                $final[$this->vexs[0]] = 1;  

                for($i = 0; $i < count($this->vexs); $i ++){  
                        $key = 0;  
                        $min = $this->infinity;  

                        for($j = 1; $j < count($this->vexs); $j ++){  
                                $temp = $this->vexs[$j];  
                                if($final[$temp] != 1 && $weight[$temp] < $min){  
                                        $key = $temp;  
                                        $min = $weight[$temp];   
                                }  
                        }  

                        $final[$key] = 1;  

                        for($j = 0; $j < count($this->vexs); $j ++){  
                                $temp = $this->vexs[$j];  
                                if($final[$temp] != 1 && ($min + $this->arc[$key][$temp]) < $weight[$temp]){  
                                        $pre[$temp] = $key;  
                                        $weight[$temp] = $min + $this->arc[$key][$temp];  
                                }  
                        }  
                }  

                return $pre;  
        }  

        //kruscal算法  
        private function kruscal(){  
                $this->krus = array();  

                foreach($this->vexs as $value){  
                        $krus[$value] = 0;     
                }  

                foreach($this->arc as $key=>$value){  
                        $begin = $this->findRoot($key);  

                        foreach($value as $k=>$v){  
                                $end = $this->findRoot($k);  
                                if($begin != $end){  
                                        $this->krus[$begin] = $end;   
                                }  
                        }  
                }   
        }  

        //查找子树的尾结点  
        private function findRoot($node){  
                while($this->krus[$node] > 0){  
                        $node = $this->krus[$node];  
                }  

                return $node;  
        }  



        //prim算法,生成最小生成树  
        public function prim(){  
                $this->primVexs = array();  
                $this->primArc = array($this->vexs[0]=>0);  

                for($i = 1; $i < count($this->vexs); $i ++){  
                        $this->primArc[$this->vexs[$i]] = $this->arc[$this->vexs[0]][$this->vexs[$i]];  
                        $this->primVexs[$this->vexs[$i]] = $this->vexs[0];  
                }  

                for($i = 0; $i < count($this->vexs); $i ++){  
                        $min = $this->infinity;  
                        $key;  

                        foreach($this->vexs as $k=>$v){  
                                if($this->primArc[$v] != 0 && $this->primArc[$v] < $min){  
                                        $key = $v;  
                                        $min = $this->primArc[$v];  
                                }  
                        }  

                        $this->primArc[$key] = 0;  

                        foreach($this->arc[$key] as $k=>$v){  
                                if($this->primArc[$k] != 0 && $v < $this->primArc[$k]){  
                                        $this->primArc[$k] = $v;    
                                        $this->primVexs[$k] = $key;   
                                }  
                        }  
                }  

                return $this->primVexs;  
        }  

        //一般算法,生成最小生成树  
        public function bst(){  
                $this->primVexs = array($this->vexs[0]);  
                $this->primArc = array();  

                next($this->arc[key($this->arc)]);   
                $key = NULL;  
                $current = NULL;  

                while(count($this->primVexs) < count($this->vexs)){  
                        foreach($this->primVexs as $value){  
                                foreach($this->arc[$value] as $k=>$v){  
                                        if(!in_array($k, $this->primVexs) && $v != 0 && $v != $this->infinity){  
                                                if($key == NULL || $v < current($current)){  
                                                        $key = $k;  
                                                        $current = array($value . $k=>$v);  
                                                }  
                                        }  

                                }  
                        }  

                        $this->primVexs[] = $key;  
                        $this->primArc[key($current)] = current($current);  
                        $key = NULL;  
                        $current = NULL;  
                }  

                return array('vexs'=>$this->primVexs, 'arc'=>$this->primArc);  
        }  

        //一般遍历  
        public function reserve(){  
                $this->hasList = array();  

                foreach($this->arc as $key=>$value){  
                        if(!in_array($key, $this->hasList)){  
                                $this->hasList[] = $key;  
                        }  

                        foreach($value as $k=>$v){  
                                if($v == 1 && !in_array($k, $this->hasList)){  
                                        $this->hasList[] = $k;     
                                }  
                        }  
                }  

                foreach($this->vexs as $v){  
                        if(!in_array($v, $this->hasList))  
                                $this->hasList[] = $v;  
                }  

                return implode($this->hasList);  
        }  

        //广度优先遍历  
        public function bfs(){  
                $this->hasList = array();  
                $this->queue = array();  

                foreach($this->arc as $key=>$value){  
                        if(!in_array($key, $this->hasList)){  
                                $this->hasList[] = $key;  
                                $this->queue[] = $value;  

                                while(!empty($this->queue)){  
                                        $child = array_shift($this->queue);  

                                        foreach($child as $k=>$v){  
                                                if($v == 1 && !in_array($k, $this->hasList)){  
                                                        $this->hasList[] = $k;  
                                                        $this->queue[] = $this->arc[$k];     
                                                }  
                                        }  
                                }  
                        }  
                }  

                return implode($this->hasList);  

        }  

        //执行深度优先遍历  
        public function excuteDfs($key){  
                $this->hasList[] = $key;    

                foreach($this->arc[$key] as $k=>$v){  
                        if($v == 1 && !in_array($k, $this->hasList))  
                                $this->excuteDfs($k);     
                }  
        }  

        //深度优先遍历  
        public function dfs(){  
                $this->hasList = array();  

                foreach($this->vexs as $key){  
                        if(!in_array($key, $this->hasList))   
                                $this->excuteDfs($key);   
                }  

                return implode($this->hasList);  
        }  

        //返回图的二维数组表示  
        public function getArc(){  
                return $this->arc;  
        }  

        //返回结点个数  
        public function getVexCount(){  
                return count($this->vexs);  
        }  
}  


$a = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i');  
$b = array('ab'=>'10', 'af'=>'11', 'bg'=>'16', 'fg'=>'17', 'bc'=>'18', 'bi'=>'12', 'ci'=>'8', 'cd'=>'22', 'di'=>'21', 'dg'=>'24', 'gh'=>'19', 'dh'=>'16', 'de'=>'20', 'eh'=>'7','fe'=>'26');//键为边,值权值  

$test = new MGraph($a, $b);  
print_r($test->bst());

[翻译]在phpstorm中配置xdebug

这是一篇翻译文章,原文请见 配置Phpstorm,Xdebug

PhpStorm 和 XDebug 是非常帅气的一对搭档

大部分时候缺少一个debugger工具会导致你无法高效的编写代码,我为一个定制化的wordpress网站工作,通过debugger深度挖掘其功能是必不可少的过程.

配置xdebug与phpstorm工作是非常简单的,但是当两个人同时尝试用相同的IP在同一个远程开发环境中对同一段代码进行debug的时候,这件事情就变得非常微妙,这个时候xdebug无法分别出到底是谁在对代码进行debug.

所以你可以看一下单用户和多用户在配置xdebug时的区别.我会在下面对两者都作出介绍.

单个用户配置phpstorm xdebug 步骤

这有一张单独用户的xdebug工作图
单独用户xdebug图

  • php.ini中做如下配置:
    php.ini

  • 在phpstorm中做如下配置

single-debug-port.png

  • 确保phpstorm是在端口监听状态
    red-to-green.png
  • 下面在你的代码的某处设一个断点
  • 现在你已经可以在你的浏览器中进行debug对话了.这里设置了一个cookie所以服务端的xdebug知道如何执行你的debug需求.
    bookmarklet1.png
  • 在你的浏览器中浏览到'断点'页面(比如你的wordpress主页),如果php+xdebug发现了一个断点,它会发送debug数据到你的IDE.

恭喜你,到现在为止单用户的xdebug配置已经完成了!

未完待续...

散列函数time33学习随笔

年底项目任务中,忙里偷闲更新一篇.
众所周知,PHP拥有一个万能的数据结构--Array.这货实在是太易用太强大了,导致PHP程序员在程序员界都混不下去了.使用PHP的Array几乎可以实现在大学数据结构课本里出现的所有数据结构.
实际上在PHP内部,Array是由Zend引擎中的Zend HashTable实现的.其实不光是Array,包括各种常量,变量,函数体,class等都是用它来组织的.
在PHP的源代码里找到zend_hash.c后不难发现,zend hashtable包含两个结构体,分别是bucket

typedef struct bucket {
ulong h;       /* Used for numeric indexing */
uint nKeyLength;     /* key 长度 */
void *pData;      /* 指向Bucket中保存的数据的指针 */
void *pDataPtr;     /* 指针数据 */
struct bucket *pListNext;   /* 指向HashTable桶列中下一个元素 */
struct bucket *pListLast;    /* 指向HashTable桶列中前一个元素 */
struct bucket *pNext;    /* 指向具有同一个hash值的桶列的后一个元素 */
struct bucket *pLast;    /* 指向具有同一个hash值的桶列的前一个元素 */
char arKey[1];      /* 必须是最后一个成员,key名称*/
} Bucket;

以及_hashtable

typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer;
Bucket *pListHead;
Bucket *pListTail; 
Bucket **arBuckets;
dtor_func_t pDestructor;
zend_bool persistent;
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;

这篇博文的重点并不在这里,相信每一个从事PHP行业的职业PHPer都对这些烂熟于心了.今天主要学习一下PHP使用的DJBX33A哈希算法
DJBX33A (Daniel J. Bernstein, Times 33 with Addition)普遍叫做time33算法,这个算法被广泛运用与多个软件项目,它是已知的最好的哈希算法之一,在处理以字符串为键值的哈希时,有着极快的计算效率和很好哈希分布.
如果用映射关系来表达的话就是如下形式,很简单:

hash(i) = hash(i-1) * 33 + str[i]

以下是PHP内部使用的time33版本:

inline unsigned time33(char const*str, int len) 
{ 
     unsigned long hash = 5381; 
     /* variant with the hash unrolled eight times */ 
     for (; len >= 8; len -= 8) { 
         hash = ((hash << 5) + hash) + *str++; 
         hash = ((hash << 5) + hash) + *str++; 
         hash = ((hash << 5) + hash) + *str++; 
         hash = ((hash << 5) + hash) + *str++; 
        hash = ((hash << 5) + hash) + *str++; 
        hash = ((hash << 5) + hash) + *str++; 
        hash = ((hash << 5) + hash) + *str++; 
        hash = ((hash << 5) + hash) + *str++; 
    } 
    switch (len) { 
        case 7: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ 
        case 6: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ 
        case 5: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ 
        case 4: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ 
        case 3: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ 
        case 2: hash = ((hash << 5) + hash) + *str++; /* fallthrough... */ 
        case 1: hash = ((hash << 5) + hash) + *str++; break; 
        case 0: break; 
    } 
    return hash; 
} 

用位运算代替乘法,效率更高.待续..

抽奖程序中随机概率的测试

最近一个项目里关于抽奖活动的概率引发了一些争执,
讲一下整个抽奖流程:
一共有M种元素,用户集齐其中的N种即算中奖,M种元素获得的概率都不相同.然后用户每天有T次机会可以抽奖,其中满足条件T>M;另说明我不是做游戏的,对于这方面来说有些欠缺,只能摸着石头过河,一段代码来验证概率是否合理.

$arr = array(
    'h1' => 30,
    'h2' => 30,
    'h3' => 8,
    'h4' => 8,
    'h5' => 8,
    'h6' => 8,
    'h7' => 8,
    'h8' => 5,
    ...
);

$lucky = array();
foreach( $arr as $k=>$v )
{
    $lucky += array_fill( count($lucky),$v,$k );
}

$user = array();
$limit = array();
$j = 0;
//采样数量$k
for( $k =0; $k < 1000;$k++ )
{
    $i = 1;
    while(true)
    {
        $a = $lucky[mt_rand(0,99)];
        if( !in_array( $a, $user ) )
        {
            $user[] = $a;
        }
            //达到获奖条件N
        if( count($user) >N )
        {
            $user = array();
            break;
        }
        $i++;
    }

    //用户每天有T次机会
    if( $i <= T )
    {
        $j++;
    }

    $limit[] = $i;

}

//这里得到采样中达到获奖资格平均需要多少次抽奖
var_dump(array_sum( $limit )/1000);

//得到在$k中有多少人可以在抽奖资格内的中奖
echo $k.'人中有多少人第一天能中奖'.($j);

官方wiki:Configuring PhpStorm IDE for Yii

Code completion

  1. Exclude yiilite.php from index:
    • File → Settings → IDE Settings → File Types.
    • yiilite.php to Ignore files and folders.
  2. Exclude not used directories, specify resources.
    • File → Settings → Project settings → Directories.
    • Mark framework/cli/viewsprotected/runtime and assets as excluded.
    • Mark website root as resource root.
  3. Specify path to your PHP.
    • File → Settings → Project settings → PHP → PHP Home.
  4. If your project uses common Yii framework folder you need to include it.
    • File → Settings → Project settings → PHP → PHP Home → Add.
    • Specify a path to framework directory.
  5. If you are writing unit tests you can include PHPUnit to get code completion:
    • File → Settings → Project settings → PHP → PHP Home → Add.
    • Specify a path to PHPUnit.
  • Complete code: Ctrl+Space.
  • Show method arguments: Ctrl+Q.

一个php进程可以处理包含亿级数量的数组么?答案Yes

在之前很多人的认知中,php的原则是quick and dirty,我认为这有一定道理.无论从编码的设计思路还是其特殊的垃圾回收机制都证明着这个观点.

随着时间的推移,PHP的年纪越来越大,喷的人越来越多,但是丝毫没有影响PHP在web世界的霸主地位. 原因是多种多样的. 我认为无论何种编程语言都是为产品服务的,那么只要能稳定,合格的实现产品功能,就可以说这门语言是成功的.

在这个基础上PHP还拥有开发速度快,入门成本低等先天优势,那么风靡世界就是自然而然的事情了. 我很不理解某些自命不凡或者自命清高的程序员,任何职业都是要接地气的,尤其是在中国.实现产品功能是硬性条件,在这个基础上尽量控制开发成本已经开发时间,这是很浅显的道理.不需要多么优雅,我们伟大的party也是农民阶级建立并解放新中国的.简单直接才是硬道理.

好了,不扯一些别的东西了. 最近PHP的大事是5.5 release了,里面有一些有趣的东西. 今天介绍一下Generators这个东东,有一些技术博客将它翻译成生成器, 官方给出的介绍是这样的:Generators provide an easy way to implement simple iterators without the overhead or complexity of implementing a class that implements the Iterator interface.
我理解的意思就是一个简单实现的迭代器. 有了它,php能做的事情更多了.比如说:

function xrange($start, $end, $step = 1) {
    for ($i = $start; $i <= $end; $i += $step) {
        yield $i;
    }
}
foreach (xrange(1, 1000000) as $num) {
    echo $num, "\n";
}

 
上面这个xrange函数提供了和PHP的内建函数range一样的功能。但是不同的是range()函数返回的是一个包含属组值从1到100万的数组(注:请查看手册)。
而xrange函数返回的是依次输出这些值的一个迭代器,而且并不会真正以数组形式计算。 这种方法的优点是显而易见的。它可以让你在处理大数据集合的时候不用一次性的加载到内存中。甚至你可以处理无限大的数据流。 当然,也可以不同通过生成器来实现这个功能,而是可以通过继承Iterator接口实现。通过使用生成器实现起来会更方便,而不用再去实现iterator接口中的5个方法了。 要从生成器认识协同程序,理解它们内部是如何工作的非常重要:生成器是可中断的函数,在它里面,yield构成了中断点。

紧接着上面的例子,如果你调用xrange(1,1000000)的话,xrange()函数里代码没有真正地运行。相反,PHP只是返回了一个实现了迭代器接口的生成器类实例
你对某个对象调用迭代器方法一次,其中的代码运行一次。例如,如果你调用$range->rewind(),那么xrange()里的代码运行到控制流 第一次出现yield的地方。在这种情况下,这就意味着当$i=$start时yield $i才运行。传递给yield语句的值是使用$range->current()获取的。
为了继续执行生成器中的代码,你必须调用$range->next()方法。这将再次启动生成器,直到yield语句出现。因此,连续调用next()和current()方法 你将能从生成器里获得所有的值,直到某个点没有再出现yield语句。对xrange()来说,这种情形出现在$i超过$end时。在这中情况下, 控制流将到达函数的终点,因此将不执行任何代码。一旦这种情况发生,vaild()方法将返回假,这时迭代结束。 再举一个例子: 假如有一个巨大的文件,  

function getLinesFromFile($fileName) {
  if (!$fileHandle = fopen($fileName, 'r')) {
    return;
  }

  while (false !== $line = fgets($fileHandle)) {
    yield $line;
  }

  fclose($fileHandle);
}

我们通过简单的 生成器就可以用一个php进程处理整个文件,只是时间问题而已.

$filename = 'somefile.txt';
$gen = getLinesFromFile($filename);

foreach ($gen as $line) {
  echo $line;
}

引自一个老外的测试结果,还是很惊喜的.

0E4E064A-D25E-4A16-8332-6A5FF080D54D
PHP 5.5给我们的惊喜还不止于此.而且作为一门有一些历史的语言(想比较node,go等)保持高速的进步实属不易,现在官方需要做的是如何加速php世界的版本更新,这个尤为重要. 其实我更想看到的是php能够更加实用和具体的功能,比如: 函数重载. 错误异常中继而不是简单的输出.    

关于内部技术分享,ppt

在公司内部做了一次技术分享,主要是关于xhprof的使用以及一些源码分析,留PPT以做备忘. xhprof.pptx

xdebug的详细配置说明

xdebug调试变量更加友好 Xdebug重写了php里面var_dump()函数。 xdebug里的var_dump()给变量对象有不同的颜色,显示类型长度,还可以控制显示层次,显示的方式经过格式化,清晰友好。 需要使用此功能,有如下参数需注意。 ;是否覆盖php里面的函数var_dump();默认是开启的,值为1;设为0,则关闭; xdebug.overload_var_dump = 1 ;控制数组子元素显示的大小默认为256 xdebug.var_display_max_children = 256 ;控制变量打印的大小,默认为512 xdebug.var_display_max_data = 512 ;控制数组和对象元素显示的层级。默认为3 xdebug.var_display_max_depth = 3

- 阅读剩余部分 -

我的xdebug配置方案.包含日志分析工具

前面的文章介绍过xhprof的实际使用,可谓是PHP性能优化的一大杀器啊. 但是相比较于xdebug来说,功能还是单一一些,xdebug不光能做debug调试,而且对于profile分析也非常犀利,并且比之xhprof来说可以详细到每个内部函数的调用. 废话不多说,先粘出我的配置 [caption id="attachment_566"

align="alignnone" width="566"]

xdebug配置 xdebug配置[/caption]   xdebug.auto_trace=on; ;自动打开“监测函数调用过程”的功模。该功能可以在你指定的目录中将函数调用的监测信息以文件的形式输出。此配置项的默认值为off。 xdebug.collect_params=on; ;打开收集“函数参数”的功能。将函数调用的参数值列入函数过程调用的监测信息中。此配置项的默认值为off。 xdebug.collect_return=on ;打开收集“函数返回值”的功能。将函数的返回值列入函数过程调用的监测信息中。此配置项的默认值为off。 xdebug.profiler_enable=on ;打开效能监测器。 dump相关就是打印global啊 未定义啊这类的变量 保存关闭重启server以后运行程序,会在定义的目录生成xdebug日志. 下面介绍几款日志分析工具. 首先wincachegrind就不多说,windows下最常用的,但是功能性易用性都觉得一般. 主要介绍web版的webgrind. xdebug1 如图 可以清晰的看到每个函数.包括系统函数的调用次数以及耗时.并且可以快速定位到代码.个人认为是linux server下不二选择,可以远程管理. 想比webgrind来说.linux下的kcachegrind还兼有图像生成功能,并且此工具目前有windows移植版本.本文会提供下载. xdebug3   xdebug4   xdebug5   非常好用. 以下是下载地址: webgrind kcachegrind

通过源码分析PHP的一些常用函数

count

count是我们经常用到的一个函数,其功能是返回一个数组的长度。 count这个函数,其复杂度是多少呢? 一种常见的说法是count函数会遍历整个数组然后求出元素个数,因此复杂度是O(n)。那实际情况是不是这样呢? 我们回到count的实现来看一下,通过源码可以发现,对于数组的count操作,函数最终的路径是zif_count-> php_count_recursive-> zend_hash_num_elements,而zend_hash_num_elements的行为是 return ht->nNumOfElements,可见,这是一个O(1)而不是O(n)的操作。实际上,数组在php底层就是一个hash_table,对于hash表,zend中专门有一个元素nNumOfElements记录了当前元素的个数,因此对于一般的count实际上直接就返回了这个值。由此,我们得出结论: 

count是O(1)的复杂度,和具体数组的大小无关。

- 阅读剩余部分 -

《PHP扩展开发及内核应用》

http://www.walu.cc/phpbook github

PHP测试利器之XHProf:将XHProf运用在线上环境

以前写过一篇关于xdebug的使用记录,今天记录一下非死不可提供的一个PHP性能调试工具--XHProf,供备忘. XHProf最新的版本是0.9.2(三年没更新了,汗一个...) 安装起来很简单,因为在PECL里已经包含:http://pecl.php.net/package/xhprof

wget http://pecl.php.net/get/xhprof-0.9.2.tgz
tar zxf xhprof-0.9.2.tgz
cd xhprof-0.9.2/extension/
phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install

  然后在phpini里加入xhprof.so的扩展然后重启nginx即可.

- 阅读剩余部分 -

用PHP精准判断上传文件类型

在54chen的博客发现了这篇文章.是一个不错的思路,其实在实际项目中判断文件类型一直是一个让我头疼的问题. 若是图片,通常都使用getimagesize函数.感觉效果还凑合. 正文开始. 本文目的在于,进一步更正前文所述的mime判断方式,以及增加一个nginx环境里的文件上传大小所影响的代码。 上传类型控制: 在我(54chen)工作中发现,其实修改文件的后缀,浏览器就会很傻瓜地传送错误的mime类型,所以前文的判断是一个半错误的方法(除了C代码是正确的)。 网上流传一段PHP读取文件头判断文件类型的方法,有一些bug,经我(54chen)修改实测,应该是这个样子:

- 阅读剩余部分 -