Php 一般地雷

Php 一般地雷,php,performance,Php,Performance,其他人在编写PHP web应用程序时发现了什么惊喜?编译时类继承有一个众所周知且有待解决的问题,但我知道还有一些其他问题,我想尝试构建一个该语言的顶级gotcha列表 注: 我担任过几个高级PHP5开发人员的职位,因此PHP工作为我买单,这个问题并不是说PHP是一种语言,因为我使用过的每一种语言都有一些众所周知或不太为人所知的惊喜。这在事实发生后是很明显的,但众所周知的问题与foreach中使用的范围和引用有关 foreach($myArray as &$element){ //d

其他人在编写PHP web应用程序时发现了什么惊喜?编译时类继承有一个众所周知且有待解决的问题,但我知道还有一些其他问题,我想尝试构建一个该语言的顶级gotcha列表

注:


我担任过几个高级PHP5开发人员的职位,因此PHP工作为我买单,这个问题并不是说PHP是一种语言,因为我使用过的每一种语言都有一些众所周知或不太为人所知的惊喜。

这在事实发生后是很明显的,但众所周知的问题与foreach中使用的范围和引用有关

foreach($myArray as &$element){
   //do something to the element here... maybe trim or something more complicated
}
//Multiple lines or immediately after the loop

$element = $foobar;

数组中的最后一个单元格现在已变为$foobar,因为上面foreach中的引用仍在当前上下文范围内。

过度使用时,require\u once和include\u once通常会导致性能下降。如果您的应用程序包含/需要包含类的文件。。。这样的模式可以节省大量的处理时间

class_exists("myFoo") or require("myFoo.someClass.php");
更新: 这仍然是一个问题-

更新: 阅读以下问题的选定答案:
如果按照这些思路来实现,您将尽可能地减少对文件include/requires的惩罚。

我不确定这是否算数,但编译PHP脚本的需要是一个巨大的性能问题。在任何严肃的PHP项目中,运行PHP时都需要某种编译器缓存,如、、或(商业)。

总内存。许多大型项目只包含所有类文件,并在需要时使用它们。这增加了PHP每次运行所需的总内存

此外,项目还使用框架或iFrame,因为这很容易使内存使用量翻倍


因此,请有条件地加载类文件,不要加载您不使用的任何内容。

\uuu autoload()
最近被证明是我的一颗地雷。我们的一些遗留代码和库使用
class\u exists()
,它尝试自动加载从未打算以这种方式加载的类。许多致命的错误和警告
class_exists()
在自动加载的情况下仍然可以使用,但是第二个参数(自PHP 5.2.0以来新增)必须设置为
false

PHP应用程序的性能问题通常是以下之一:

  • 文件系统访问-读取和写入磁盘
    • 这就是APC、eAccelerator等方便的地方,它们通过在内存中缓存解析的PHP文件来减少文件系统访问
  • 数据库-查询速度慢,数据集大
  • 网络I/O-访问外部资源
PHP(或任何语言编写的web应用程序)很少遇到性能问题。上述问题通常比代码执行慢几个数量级

像往常一样,分析你的代码

不了解可能会导致一些问题:

if ($foo = getSomeValue() && $bar) {
    // …
}
// equals
if ($foo = (getSomeValue() && $bar)) {
    // …
}

我看到人们陷入的最大困境是精确性(在php和其他语言中)

如果你想找点乐子,可以将任何一个浮点数与>=的整数进行比较,找出得到预期结果的次数

这是许多在PHP内部工作的人的失败,他们试图根据不允许四舍五入到整数的比较来做出逻辑决策

例如,织物

织物以1码或1半码为单位出售,并保留织物剩余的精确尺寸

如果这个系统不是用整数来表示的,而是用浮点来表示的,那么就很难得到实数小数

您最好将1半码表示为1,例如,如果您有300码的织物,则库存为600(600半码单位)

不管怎样,这就是我的问题所在——由于不理解精度,我需要重构4个月的编程
  • foreach()在后台以静默方式复制数组,并对该副本进行迭代。如果您有一个大型阵列,这将降低性能。在这些情况下,foreach()的by-reference选项对php5是新的,或者使用for()循环

  • 注意平等(==)与身份(==)之间的关系

  • 请注意构成空()的内容与构成isset()的内容


  • 现在我有了更多的时间,有了更多的地雷:

    • 不要比较浮点数是否相等。PHP不是matlab,也不是为精确的浮点运算而设计的。试试这个:
    if(0.1+0.2==0.3)
    呼应“平等”;
    其他的
    
    回应“不”;// PHP中的另一个陷阱是,我从其他语言的人身上看到了这个错误,但并不经常

    <?php
    /**
     * regular
     */
    echo (true && true); // 1
    echo (true && false); // nothing
    
    echo (true || false); // 1
    echo (false || false); // nothing
    
    echo (true xor false); // 1
    echo (false xor false); // nothing
    
    /**
     * bitwise
     */
    echo (true & true); // 1
    echo (true & false); // 0
    
    echo (true | false); // 1
    echo (false | false); // 0
    
    echo (true ^ false); // 1
    echo (false ^ false); // 0
    ?>
    

    应始终避免使用
    @
    错误消音器

    例如:

    // Don't let the user see an error if this unimportant header file is missing:
    @include 'header.inc.php';
    
    使用上面的代码,您将永远不会知道
    header.inc.php
    中的任何代码中的任何错误,或者从
    header.inc.php
    调用的任何函数中的任何错误,并且如果某个地方出现致命错误,您的网页将停止,无法找到错误所在。

    如果创建两个对象并将它们存储在彼此的属性中,垃圾收集器将永远不会接触它们:

    $a = new stdClass;
    $b = new stdClass;
    $a->b = $b;
    $b->a = $a;
    
    当一个大类创建一个小的辅助对象(通常存储主类)时,这实际上很容易做到:

    // GC will never clean up any instance of Big.
    class Big {
      function __construct() {
        $this->helper = new LittleHelper($this);
      }
    }
    class LittleHelper {
      function __construct(Big $big) {
        $this->big = $big;
      }
    }
    

    只要PHP针对的是快速短页面请求,它们就不可能解决这个问题。这意味着守护进程或其他长寿命的应用程序不能依赖PHP。

    只是想到了另一个惊喜。对数组应用回调的数组映射是一个严重的性能杀手。我不完全清楚为什么,但我认为这与PHP的循环写时复制机制有关。

    NULL和“0”字符串在PHP中是纯粹的邪恶

    if ("0" == false) //true
    if ("0" == NULL)  //true
    if ("0" == "NULL")//true
    

    我最喜欢的PHP gotcha:

    这包括:

    # ... lots of code ...
    $i = 42;
    # ... more code ...
    
    然后,使用以下内容:

    for($i = 0; $i < 10; $i++){
        # ...
        include 'that_other_file.php';
    }
    
    ($i=0;$i<10;$i++)的
    {
    # ...
    我
    
    # ... lots of code ...
    $i = 42;
    # ... more code ...
    
    for($i = 0; $i < 10; $i++){
        # ...
        include 'that_other_file.php';
    }
    
    $iShouldTalkTo = $thisObj || $thatObj;
    
    $iShouldTalkTo = $thisObj ? $thisObj : $thatObj;
    
    switch($someVal) {
    case true  :
        doSomething();
        break;
    case 20    :
        doSomethingElse();
        break;
    }
    
    for($ix = 0; $ix < 10; $ix++) {
        switch($ix) {
        case 3  :
            continue;
        default :
            echo ':';
        }
        echo $ix;
    }
    
    $a = 1;
    echo $a;      # 1
    echo "$a";    # 1
    echo '$a';    # $a
    
    if( $foo )
    {
      some_function();
    }
    else
    {
      non_existing_function();   // oops!
    }
    
    error_reporting( E_ALL );
    
    // missing mysql_real_escape_string() or an int cast !
    $sql = "SELECT * FROM persons WHERE id=$id";
    
    // missing htmlentities() and urlencode() !
    $html = "<a href='?page=$id'>$text</a>";  
    
    <?php
    function TestCount(&$aArray)
    {
        $aArray = range(0, 100000);
        $fStartTime = microtime(true);
    
        for ($iIter = 0; $iIter < 1000; $iIter++)
        {
            $iCount = count($aArray);
        }
    
        $fTaken = microtime(true) - $fStartTime;
    
        print "took $fTaken seconds\n";
    }
    
    $aArray = array();
    TestCount($aArray);
    ?>
    
    php > $a = array(1=>'one');
    php > $b = array(2=>'two');
    php > var_dump($a+$b); /* plus preserves original keys */
    array(2) {
      [1]=>
      string(3) "one"
      [2]=>
      string(3) "two"
    }
    php > var_dump(array_merge($a,$b)); /* array_merge reindexes numeric keys */
    array(2) {
      [0]=>
      string(3) "one"
      [1]=>
      string(3) "two"
    }
    
    
    php > $a = array(1=>'one');
    php > $b = array(1=>'another one');
    php > var_dump($a+$b);  /* plus ignores duplicate keys, keeping the first value */
    array(1) {
      [1]=>
      string(3) "one"
    }
    php > var_dump(array_merge($a,$b)); /* array_merge just adds them all, reindexing */
    array(2) {
      [0]=>
      string(3) "one"
      [1]=>
      string(11) "another one"
    }
    
    php > $a = array(1,2,3);
    php > $b = array(4,5,6);
    /* non-associative arrays are really associative arrays with numeric keys… */
    php > var_dump($a+$b);  /* … so plus doesn’t work as you’d normally expect */
    array(3) {
      [0]=>
      int(1)
      [1]=>
      int(2)
      [2]=>
      int(3)
    }
    php > var_dump(array_merge($a,$b));  /* you should use array_merge instead */
    array(6) {
      [0]=>
      int(1)
      [1]=>
      int(2)
      [2]=>
      int(3)
      [3]=>
      int(4)
      [4]=>
      int(5)
      [5]=>
      int(6)
    }
    
    php > var_dump('nada' == 0);
    bool(true)
    
    php > var_dump('nada' === 0);
    bool(false)
    
    php > var_dump(0.0 === 0);
    bool(false)
    
    php > var_dump("0" == "00");
    bool(true)
    
    php > var_dump(array('00'=>'str(zerozero)', '0'=>'str(zero)'));
    array(2) {
      ["00"]=>
      string(13) "str(zerozero)"
      [0]=>
      string(9) "str(zero)"
    }
    
    $x = array();
    $x == null ? "true": "false";
    
    $x = array("foo");
    $x == null ? "true": "false";