转到正文

浪淘沙

静观己心,厚积薄发

存档

分类: PHP

特别喜欢PHP自带的自然排序 natsort / natcasesort。为此,还专门看了php 源码,详情请见传送门

这两天,不是太忙,就根据自己意愿用php重新实现了下,并稍微做了点修改。代码如下:

<?php

/**
 * @param $target
 * @param $destination
 *
 * @return int
 */
function strnatcmpExFoldCase0Improve($target, $destination)
{
    $return = natSortExtend($target, count($target), $destination, count($destination), 0);
    return $return;
}

function strnatcmpExFoldCase1Improve($target, $destination)
{
    return natSortExtend($target, count($target), $destination, count($destination), 1);
}

function natSortExtend($target, $targetLen, $destination, $destinationLen, $foldCase)
{
    $aend               = $targetLen;
    $bend               = $destinationLen;
    $targetLeading      = 1;//最后一次是字母或者字母后面紧跟了n个数字'0',可以跳过后面紧跟的0
    $destinationLeading = 1;//最后一次是字母或者字母后面紧跟了n个数字'0',可以跳过后面紧跟的0

    if ($targetLen == 0 || $destinationLen == 0) {
        return ($targetLen == $destinationLen ? 0 : ($targetLen > $destinationLen ? 1 : -1));
    }

    $targetIndex      = 0;
    $destinationIndex = 0;
    while (1) {
        $currentTargetItem      = $target[$targetIndex];
        $currentDestinationItem = $destination[$destinationIndex];

        /* skip over leading zeros */
        while ($targetLeading && $currentTargetItem == '0' && ($targetIndex + 1 < $aend) && ctype_digit(
                        $target[$targetIndex + 1]
                )) {
            $currentTargetItem = $target[++$targetIndex];
        }

        while ($destinationLeading && $currentDestinationItem == '0' && ($destinationIndex + 1 < $bend) && ctype_digit(
                        $destination[$destinationIndex + 1]
                )) {
            $currentDestinationItem = $destination[++$destinationIndex];
        }

        $targetLeading      = 0;
        $destinationLeading = 0;

        /* Skip consecutive whitespace */
        while (ctype_space($currentTargetItem)) {
            $currentTargetItem = $target[++$targetIndex];
        }

        while (ctype_space($currentDestinationItem)) {
            $currentDestinationItem = $destination[++$destinationIndex];
        }

        /* process run of digits */
        if (ctype_digit($currentTargetItem) && ctype_digit($currentDestinationItem)) {
            $fractional = ($currentTargetItem == '0' || $currentDestinationItem == '0');

            $ap = array_slice($target, $targetIndex);
            $bp = array_slice($destination, $destinationIndex);

            if ($fractional) {
                $result = compare_left($ap, count($ap), $bp, count($bp));
            } else {
                $result = compare_right($ap, count($ap), $bp, count($bp));
            }
            if ($result != 0) {
                return $result;
            } else if ($targetIndex == $aend && $destinationIndex == $bend) {
                /* End of the strings. Let caller sort them out. */
                return 0;
            } else {
                /* Keep on comparing from the current point. */
                $currentTargetItem      = $target[$targetIndex];
                $currentDestinationItem = $destination[$destinationIndex];
            }
        }

        if ($foldCase) {
            $currentTargetItem      = strtoupper($currentTargetItem);
            $currentDestinationItem = strtoupper($currentDestinationItem);
        }

        if ($currentTargetItem < $currentDestinationItem) {
            return -1;
        } else if ($currentTargetItem > $currentDestinationItem) {
            return +1;
        }

        $targetIndex++;
        $destinationIndex++;
        if ($targetIndex >= $aend && $destinationIndex >= $bend) {
            /* The strings compare the same.  Perhaps the caller
                will want to call strcmp to break the tie. */
            if ($aend < $bend) {
                return -1;
            } else if ($aend > $bend) {
                return 1;
            } else {
                return 0;
            }
        } else if ($targetIndex >= $aend) {
            return -1;
        } else if ($destinationIndex >= $bend) {
            return 1;
        }

        if (ctype_alpha($currentTargetItem)) {//需要处理后面的0 ,空格可以跳过
            $targetLeading = 1;
        } else if (ctype_digit($currentTargetItem) && $currentTargetItem != '0') {//非0数字 后面的0不可以跳过
            $targetLeading = 0;
        }
        if (ctype_alpha($currentDestinationItem)) {//需要处理后面的0 ,空格可以跳过
            $destinationLeading = 1;
        } else if (ctype_digit($currentDestinationItem) && $currentDestinationItem != '0') {//非0数字 后面的0不可以跳过
            $destinationLeading = 0;
        }
    }

    return 0;
}

function compare_left($a, $aend, $b, $bend)
{
    $aIndex = 0;
    $bIndex = 0;
    /* Compare two left-aligned numbers: the first to have a
       different value wins. */
    for (; ; $aIndex++, $bIndex++) {
        if ($aIndex >= count($a)) {
            echo 'warning $aIndex: ', $aIndex, ' => ', var_export($a);
            $ca = 0;
        } else {
            $ca = $a[$aIndex];//warning..
        }

        if ($bIndex >= count($b)) {
            echo 'warning $bIndex:', $bIndex, ' => ', var_export($b);
            $cb = 0;
        } else {
            $cb = $b[$bIndex];//warning..
        }

        if (($aIndex == $aend - 1 || !ctype_digit($ca)) && ($bIndex == $bend - 1 || !ctype_digit($cb))) {
            return 0;
        } else if ($aIndex == $aend - 1 || !ctype_digit($ca)) {
            return -1;
        } else if ($bIndex == $bend - 1 || !ctype_digit($cb)) {
            return +1;
        } else if ($ca < $cb) {
            return -1;
        } else if ($ca > $cb) {
            return +1;
        }
    }

    return 0;
}

function compare_right($a, $aend, $b, $bend)
{
    $aIndex = 0;
    $bIndex = 0;
    $bias   = 0;

    /* The longest run of digits wins.  That aside, the greatest
       value wins, but we can't know that it will until we've scanned
       both numbers to know that they have the same magnitude, so we
       remember it in BIAS. */
    for (; ; $aIndex++, $bIndex++) {
        if ($aIndex >= count($a)) {
            echo 'warning $aIndex: ', $aIndex, ' => ', var_export($a);
            $ca = 0;
        } else {
            $ca = $a[$aIndex];//warning..
        }

        if ($bIndex >= count($b)) {
            echo 'warning $bIndex:', $bIndex, ' => ', var_export($b);
            $cb = 0;
        } else {
            $cb = $b[$bIndex];//warning..
        }
        if (($aIndex == $aend - 1 || !ctype_digit($ca)) && ($bIndex == $bend - 1 || !ctype_digit($cb))) {
            return $bias;
        } else if ($aIndex == $aend - 1 || !ctype_digit($ca)) {
            return -1;
        } else if ($bIndex == $bend - 1 || !ctype_digit($cb)) {
            return +1;
        } else if ($ca < $cb) {
            if (!$bias) {
                $bias = -1;
            }
        } else if ($ca > $cb) {
            if (!$bias) {
                $bias = +1;
            }
        }
    }

    return 0;
}

$data = array(
        '1ab' => 'abc001',
        '2ab' => 'abc021',
        '3ab' => 'abc1',
        '4ab' => 'abc2',
        '5ab' => 'abc4',
        '6ab' => 'abc50',
        '1ae' => 'aec001',
        '2ae' => 'aec021',
        '3ae' => 'aec1',
        '4ae' => 'aec2',
        '5ae' => 'aec4',
        '6ae' => 'aec50',
        '1be' => 'bec001',
        '2be' => 'bec021',
        '3be' => 'bec1',
);

function prestrnatcmpEx2($data, $fold_case)
{
    foreach ($data as $index => $item) {
        $data[$index] = str_split($item);
    }

    if ($fold_case == 0) {
        usort($data, 'strnatcmpExFoldCase0Improve');
    } else {
        usort($data, 'strnatcmpExFoldCase1Improve');

    }

    foreach ($data as $index => $item) {
        $data[$index] = implode('', $item);
    }

    echo '<pre>';
    var_export($data);
    echo '</pre>';
}

prestrnatcmpEx2($data, 0);

$data = array(
        '1ab' => 'abc001',
        '2ab' => 'abc021',
        '3ab' => 'abc1',
        '4ab' => 'abc2',
        '5ab' => 'abc4',
        '6ab' => 'abc50',
        '1ae' => 'aec001',
        '2ae' => 'aec021',
        '3ae' => 'aec1',
        '4ae' => 'aec2',
        '5ae' => 'aec4',
        '6ae' => 'aec50',
        '1be' => 'bec001',
        '2be' => 'bec021',
        '3be' => 'bec1',
);

natsort($data);
echo '<pre>';
var_export($data);
echo '</pre>';


//输出结果
array (
  0 => 'abc1',
  1 => 'abc001',
  2 => 'abc2',
  3 => 'abc4',
  4 => 'abc021',
  5 => 'abc50',
  6 => 'aec1',
  7 => 'aec001',
  8 => 'aec2',
  9 => 'aec4',
  10 => 'aec021',
  11 => 'aec50',
  12 => 'bec1',
  13 => 'bec001',
  14 => 'bec021',
)
array (
  '1ab' => 'abc001',
  '2ab' => 'abc021',
  '3ab' => 'abc1',
  '4ab' => 'abc2',
  '5ab' => 'abc4',
  '6ab' => 'abc50',
  '1ae' => 'aec001',
  '2ae' => 'aec021',
  '3ae' => 'aec1',
  '4ae' => 'aec2',
  '5ae' => 'aec4',
  '6ae' => 'aec50',
  '1be' => 'bec001',
  '2be' => 'bec021',
  '3be' => 'bec1',
)

natsort的核心实现方法如下:

/* {{{ strnatcmp_ex
*/
PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len, int fold_case)
{
    unsigned char ca, cb;   
    char const *ap, *bp;
    char const *aend = a + a_len,
    *bend = b + b_len;
    int fractional, result;
    short leading = 1;

    if (a_len == 0 || b_len == 0) {
        return (a_len == b_len ? 0 : (a_len > b_len ? 1 : -1));
    }  

    ap = a;
    bp = b;
    while (1) {
        ca = *ap; cb = *bp;

        /* skip over leading zeros */
        while (leading && ca == '0' && (ap+1 < aend) && isdigit((int)(unsigned char)*(ap+1))) {
            ca = *++ap;
        }

        while (leading && cb == '0' && (bp+1 < bend) && isdigit((int)(unsigned char)*(bp+1))) {
            cb = *++bp;
        }

        leading = 0;

        /* Skip consecutive whitespace */
        while (isspace((int)(unsigned char)ca)) {
            ca = *++ap;
        }

        while (isspace((int)(unsigned char)cb)) {
            cb = *++bp;
        }

        /* process run of digits */
        if (isdigit((int)(unsigned char)ca)  &&  isdigit((int)(unsigned char)cb)) {
            fractional = (ca == '0' || cb == '0');

            if (fractional)
                result = compare_left(&ap, aend, &bp, bend);
            else
                result = compare_right(&ap, aend, &bp, bend);

            if (result != 0)
                return result;
            else if (ap == aend && bp == bend)
                /* End of the strings. Let caller sort them out. */
                return 0;
            else {
                /* Keep on comparing from the current point. */
                ca = *ap; cb = *bp;
            }
        }

        if (fold_case) {
            ca = toupper((int)(unsigned char)ca);
            cb = toupper((int)(unsigned char)cb);
        }

        if (ca < cb)
            return -1;
        else if (ca > cb)
            return +1;

        ++ap; ++bp;
        if (ap >= aend && bp >= bend)
            /* The strings compare the same.  Perhaps the caller
                will want to call strcmp to break the tie. */
            return 0;
        else if (ap >= aend)
            return -1;
        else if (bp >= bend)
            return 1;
    }
}
/* }}} */

gettype获取变量的类型

string gettype ( mixed $var )  返回 PHP 变量的类型 var .

PHP manual 有个大大的warning:

不要使用 gettype() 来测试某种类型,因为其返回的字符串在未来的版本中可能需要改变。此外,由于包含了字符串的比较,它的运行也是较慢的。

使用 is_* 函数代替。

但是有时候 我们又不得不使用gettype这样的功能来判断参数的类型,这个时候我们应该怎么办?O(∩_∩)O哈哈~ 热心的网友已经帮我们重新实现了这个逻辑。代码如下:

 
function get_type($var)
{
    if (is_object($var)) {
        return get_class($var);
    }
    if (is_null($var)) {
        return 'null';
    }
    if (is_string($var)) {
        return 'string';
    }
    if (is_array($var)) {
        return 'array';
    }
    if (is_int($var)) {
        return 'integer';
    }
    if (is_bool($var)) {
        return 'boolean';
    }
    if (is_float($var)) {
        return 'float';
    }
    if (is_resource($var)) {
        return 'resource';
    }
    return 'unknown';
}

另一个版本

/**
 * Returns the type of the var passed.
 *
 * @param mixed $var Variable
 *
 * @return string Type of variable
 */
function myGetType($var)
{
    if (is_array($var)) {
        return "array";
    }
    if (is_bool($var)) {
        return "boolean";
    }
    if (is_float($var)) {
        return "float";
    }
    if (is_int($var)) {
        return "integer";
    }
    if (is_null($var)) {
        return "NULL";
    }
    if (is_numeric($var)) {
        return "numeric";
    }
    if (is_object($var)) {
        return "object";
    }
    if (is_resource($var)) {
        return "resource";
    }
    if (is_string($var)) {
        return "string";
    }
    return "unknown type";
}

 

原文地址:理解Javascript的闭包

前言:还是一篇入门文章。Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包对于那些使用传统静态语言C/C++的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性,并结合一点ECMAScript语言规范来使读者可以更深入的理解闭包。

注:本文是入门文章,例子素材整理于网络,如果你是高手,欢迎针对文章提出技术性建议和意见。本文讨论的是Javascript,不想做语言对比,如果您对Javascript天生不适,请自行绕道。

什么是闭包

闭包是什么?闭包是Closure,这是静态语言所不具有的一个新特性。但是闭包也不是什么复杂到不可理解的东西,简而言之,闭包就是:

• 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。

• 闭包就是就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配

• 当在一个函数内定义另外一个函数就会产生闭包

上面的第二定义是第一个补充说明,抽取第一个定义的主谓宾——闭包是函数的‘局部变量’集合。只是这个局部变量是可以在函数返回后被访问。(这个不是官方定义,但是这个定义应该更有利于你理解闭包)

做为局部变量都可以被函数内的代码访问,这个和静态语言是没有差别。闭包的差别在于局部变变量可以在函数执行结束后仍然被函数外的代码访问。这意味着函数必须返回一个指向闭包的“引用”,或将这个”引用”赋值给某个外部变量,才能保证闭包中局部变量被外部代码访问。当然包含这个引用的实体应该是一个对象,因为在Javascript中除了基本类型剩下的就都是对象了。可惜的是,ECMAScript并没有提供相关的成员和方法来访问闭包中的局部变量。但是在ECMAScript中,函数对象中定义的内部函数() inner function是可以直接访问外部函数的局部变量,通过这种机制,我们就可以以如下的方式完成对闭包的访问了。

function greeting(name) {
    var text = 'Hello ' + name; // local variable
    return function() { alert(text); }  // 每次调用时,产生闭包,并返回内部函数对象给调用者
}
var sayHello=greeting("Closure");
sayHello()  // 通过闭包访问到了局部变量text

上述代码的执行结果是:Hello Closure,因为sayHello()函数在greeting函数执行完毕后,仍然可以访问到了定义在其之内的局部变量text。

好了,这个就是传说中闭包的效果,闭包在Javascript中有多种应用场景和模式,比如Singleton,Power Constructor等这些Javascript模式都离不开对闭包的使用。

ECMAScript闭包模型

ECMAScript到底是如何实现闭包的呢?想深入了解的亲们可以获取ECMAScript 规范进行研究,我这里也只做一个简单的讲解,内容也是来自于网络。

在ECMAscript的脚本的函数运行时,每个函数关联都有一个执行上下文场景(Execution Context) ,这个执行上下文场景中包含三个部分

  • 文法环境(The LexicalEnvironment)
  • 变量环境(The VariableEnvironment)
  • this绑定

其中第三点this绑定与闭包无关,不在本文中讨论。文法环境中用于解析函数执行过程使用到的变量标识符。我们可以将文法环境想象成一个对象,该对象包含了两个重要组件,环境记录(Enviroment Recode),和外部引用(指针)。环境记录包含包含了函数内部声明的局部变量和参数变量,外部引用指向了外部函数对象的上下文执行场景。全局的上下文场景中此引用值为NULL。这样的数据结构就构成了一个单向的链表,每个引用都指向外层的上下文场景。

例如上面我们例子的闭包模型应该是这样,sayHello函数在最下层,上层是函数greeting,最外层是全局场景。如下图:

因此当sayHello被调用的时候,sayHello会通过上下文场景找到局部变量text的值,因此在屏幕的对话框中显示出”Hello Closure”

变量环境(The VariableEnvironment)和文法环境的作用基本相似,具体的区别请参看ECMAScript的规范文档。

闭包的样列

前面的我大致了解了Javascript闭包是什么,闭包在Javascript是怎么实现的。下面我们通过针对一些例子来帮助大家更加深入的理解闭包,下面共有5个样例,例子来自于JavaScript Closures For Dummies(镜像)

例子1:闭包中局部变量是引用而非拷贝

function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() { alert(num); }
    num++;
    return sayAlert;
}
var sayAlert = say667();
sayAlert()

因此执行结果应该弹出的667而非666。

例子2:多个函数绑定同一个闭包,因为他们定义在同一个函数内。

function setupSomeGlobals() {
    // Local variable that ends up within closure
    var num = 666;
    // Store some references to functions as global variables
    gAlertNumber = function() { alert(num); }
    gIncreaseNumber = function() { num++; }
    gSetNumber = function(x) { num = x; }
}
setupSomeGolbals(); // 为三个全局变量赋值
gAlertNumber(); //666
gIncreaseNumber();
gAlertNumber(); // 667
gSetNumber(12);//
gAlertNumber();//12

例子3:当在一个循环中赋值函数时,这些函数将绑定同样的闭包

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + list[i];
        result.push( function() {alert(item + ' ' + list[i])} );
    }
    return result;
}
function testList() {
    var fnlist = buildList([1,2,3]);
    // using j only to help prevent confusion - could use i
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

testList的执行结果是弹出item3 undefined窗口三次,因为这三个函数绑定了同一个闭包,而且item的值为最后计算的结果,但是当i跳出循环时i值为4,所以list[4]的结果为undefined.

例子4:外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后。

function sayAlice() {
    var sayAlert = function() { alert(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return sayAlert;
}
var helloAlice=sayAlice();
helloAlice();

执行结果是弹出”Hello Alice”的窗口。即使局部变量声明在函数sayAlert之后,局部变量仍然可以被访问到。

例子5:每次函数调用的时候创建一个新的闭包

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        alert('num: ' + num +
        '\\nanArray ' + anArray.toString() +
        '\\nref.someVar ' + ref.someVar);
    }
}
closure1=newClosure(40,{someVar:'closure 1'});
closure2=newClosure(1000,{someVar:'closure 2'});
closure1(5); // num:45 anArray[1,2,3,45] ref:'someVar closure1'
closure2(-10);// num:990 anArray[1,2,3,990] ref:'someVar closure2'

闭包的应用

Singleton 单件:

var singleton = function () {
    var privateVariable;
    function privateFunction(x) {
        ...privateVariable...
    }
    return {
        firstMethod: function (a, b) {
            ...privateVariable...
        },
        secondMethod: function (c) {
            ...privateFunction()...
        }
    };
}();

这个单件通过闭包来实现。通过闭包完成了私有的成员和方法的封装。匿名主函数返回一个对象。对象包含了两个方法,方法1可以方法私有变量,方法2访问内部私有函数。需要注意的地方是匿名主函数结束的地方的’()’,如果没有这个’()’就不能产生单件。因为匿名函数只能返回了唯一的对象,而且不能被其他地方调用。这个就是利用闭包产生单件的方法。

参考:

JavaScript Closures For Dummies(镜像) 可惜都被墙了。

Advance Javascript (Douglas Crockford 大神的视频,一定要看啊)

原文地址:理解Javascript的闭包

另外一篇 Javascript中Closure及其相关概念  感觉也不错。

关于js闭包的问题 自己还是需要好好地研究研究

原文地址:http://xenojoshua.com/2011/05/php-why-include-not-require/

为什么不用require而用include呢?PHPer都应该知道,include是动态执行的,也就是说执行到include这个语句时才把文件包 含进来,而require是预先执行的,也就是说,在php文件在执行前就已经把相关的文件包含进来,组成一个大文件,如果include改成 require,当然也可以执行,但是这样并不会达到好的性能。

原文地址:http://xenojoshua.com/2011/05/php-why-include-not-require/

 

PS:以前我只知道 include 的文件不存在的时候 会产生一个 warning的信息,require 的文件不存在的时候 会产生一个fatal error。 今天见戴神文章,又长见识了。

今天发现 PHP中include和require的区别详解 这篇文章介绍的比较清楚了,如果有兴趣可以前往拜读

sscanf

(PHP 4 >= 4.0.1, PHP 5)

sscanf — Parses input from a string according to a format。

其他的就不多说了。就说下。使用sscanf format大数字的情况。


$line = '100004318055950,100';
list($uid, $expendGoldNum) = sscanf($line, '%f,%d');
echo '$uid:',$uid, '&nbsp;&nbsp;&nbsp;$expendGoldNum":',$expendGoldNum;

运行结果为:


$uid:1.0000431805595E+14      $expendGoldNum":100

逗号前面的内容直接显示为使用科学计数法表示。


$line = '100004318055950,100';
list($uid, $expendGoldNum) = sscanf($line, '%s,%d');
echo '$uid:',$uid, '&nbsp;&nbsp;&nbsp;$expendGoldNum":',$expendGoldNum;

运行结果为:


$uid:100004318055950,100   $expendGoldNum":

如果将%f修改为%s 则因为%s将所有的内容全部匹配了,不符合要求。


$line = '100004318055950,100';
list($uid, $expendGoldNum) = sscanf($line, '%[0-9],%d');
echo '$uid:',$uid, '&nbsp;&nbsp;&nbsp;$expendGoldNum":',$expendGoldNum;

运行结果为:


$uid:100004318055950   $expendGoldNum":100

这次正确了。

 

Swift Mailer

12月 10

 

Swift Mailer 是一个面向对象的PHP邮件发送包,不依赖于 PHP 自带的mail() 函数,因为该函数在发送多个邮件时占用的系统资源很高。Swift 直接与 SMTP 服务器通讯,具有非常高的发送速度和效率。

详情请移步:http://swiftmailer.org/

 

原文地址:http://blogread.cn/it/article/6649

PHP的性能一直在提高。然而,若是用的不恰当,或是一个不留神,还是可能会踩到PHP内部实现方面的坑的。我在前几天的一个性能问题上就碰到了。

事情是这样子的,一位同事反馈我们的一个接口每次返回需要5秒之久,我们一起review了代码,“惊喜”的发现居然在循环(大约900次)中调用了一个读缓存的操作,而这个缓存的key并没有改变,因此我们把这段代码移到了循环外面,再测,接口返回时间降到了2秒,呜呼!虽然提升了1倍,但明显不是我们能接受的结果!

出现性能问题的代码量并不大,我们排除了IO问题以后,写了一段测试代码,果然问题很快重现。

<?php
$y="1800";
$x = array();
for($j=0;$j<2000;$j++){
        $x[]= "{$j}";
}

for($i=0;$i<3000;$i++){
        if(in_array($y,$x)){
                continue;
        }
}
?>
shell$ time /usr/local/php/bin/php test.php
real0m1.132s
user0m1.118s
sys0m0.015s

对的,我们用的就是字符串型的数字,从缓存拿出来就是这样子的啦!所以这里是特意转成字符串的(如果直接是数字,并不会出现这个问题 ,各位可以自行验证)。可以看出时间耗掉了1秒,才3000次循环,后面的sys用时也注定我们用strace不会拿到什么有效信息。

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

1

我们只看到这两次系统调用之间的延时非常大,却并不知道干了什么?一筹莫展了,幸好,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

如图2

2

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

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

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

3

知道了症结所在,我们解决的办法就很多了,最简单的就是为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
real0m0.267s
user0m0.247s
sys0m0.020s

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

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

如下图:

4

__ctype_tolower_loc占用了最多的时间!查了一下库函数__ctype_tolower_loc是干嘛的:简单的理解是将字符串转换成小写,那么这说明in_array比较字符串不区分大小写吗?其实这个函数调用已经和我们这个in_array感觉联系不大了,关于in_array的实现,还是去看看PHP的源码,大概理解的更为透彻了,好了,没法往下说了,欢迎与我交流,写的不对的地方请多多斧正。

原文地址:http://blogread.cn/it/article/6649

Array Operators

$a + $b Union Union of $a and $b.
The + operator returns the right-hand array appended to the left-hand array; for keys that exist in both arrays, the elements from the left-hand array will be used, and the matching elements from the right-hand array will be ignored.
<?php
$a = array("a" => "apple", "b" => "banana");
$b = array("a" => "pear", "b" => "strawberry", "c" => "cherry");

$c = $a + $b; // Union of $a and $b
echo "Union of \$a and \$b: \n";
var_dump($c);

$c = $b + $a; // Union of $b and $a
echo "Union of \$b and \$a: \n";
var_dump($c);

继续阅读

php为function传递参数的时候,可以按值传递(Passing by Reference),也可以按引用传递。这两种传递方式唯一的区别就是,按值传递的花,再function内部的修改只会保留在function内部,而在function外部不会体现出参数的修改,而按引用传递的话,所有再function内部修改的内容都会保留下来。

如果声明function的时候参数为按值传递,但是再call-time的时候使用按引用传递的话就会就会触发一个E_DEPRECATED级别的错误提示。直接上代码


function passByReference($ori_arr)
{
$ori_arr['hello'] = 'world';
}

$test = array('test' => 'test string');
passByReference(&$test);

//get a warning saying that "call-time pass-by-reference" is deprecated when you use & in foo(&$a);. And as of PHP 5.4.0, call-time pass-by-reference was removed, so using it will raise a fatal error.

var_export($test);

继续阅读