【问题标题】:Extra variable or object creation -- what is better in PHP?额外的变量或对象创建——PHP 中哪个更好?
【发布时间】:2026-02-24 06:15:01
【问题描述】:

PHP 中什么更好/更快/更高效?

使用额外的变量:

$dateStep = new DateInterval('P1D');

for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add($dateStep) : $startDate;

    ...
}

或者每次需要时都创建对象:

for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add(new DateInterval('P1D')) : $startDate;

    ...
}

我会投票给第一个选项,但我在 PHP 优化和性能方面的知识非常有限。

编辑看来,我表达得不够充分,但我特意要求这样的场景——当对象的创建返回静态值时,即在以下循环中一遍又一遍地使用。

【问题讨论】:

  • 请先做一个语法有效的例子!
  • 当然,最好将一个对象保存在变量中,而不是在内存中为多个对象创建空间。
  • @Rizier123 在我的第一个示例中,什么是无效?顺便说一句:我不知道,您是否注意到每个问题下方都有一个小而闪亮的Edit 链接,在像这样的 community-powered 网站中,该链接通常用于纠正无效问题语法等:>
  • @hek2mgl 你认为我从 1 小时开始在等什么:*.com/questions/29073502/… 提出这个问题并弄清楚:D
  • @hek2mgl 对不起,伙计们。 咖啡不够 + 复制粘贴 = 浪费时间 综合症。希望现在是正确的! :>

标签: php variables for-loop creation


【解决方案1】:

从另一个角度来看,让我们看看脚本和操作码:

脚本1:

<?php

$dateStep = new DateInterval('P1D');
$dateInterval = 5;
$startDate = new DateTime();

for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add($dateStep) : $startDate;

    //...
}

操作码:

number of ops:  26
compiled vars:  !0 = $dateStep, !1 = $dateInterval, !2 = $startDate, !3 = $di, !4 = $theDate
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   3     0  >   FETCH_CLASS                                   4  :0      'DateInterval'
         1      NEW                                              $1      :0
         2      SEND_VAL                                                 'P1D'
         3      DO_FCALL_BY_NAME                              1          
         4      ASSIGN                                                   !0, $1
   4     5      ASSIGN                                                   !1, 5
   5     6      FETCH_CLASS                                   4  :5      'DateTime'
         7      NEW                                              $6      :5
         8      DO_FCALL_BY_NAME                              0          
         9      ASSIGN                                                   !2, $6
   7    10      ASSIGN                                                   !3, 0
        11  >   IS_SMALLER_OR_EQUAL                              ~10     !3, !1
        12    > JMPZNZ                                        F          ~10, ->25
        13  >   PRE_INC                                                  !3
        14    > JMP                                                      ->11
   9    15  >   IS_SMALLER                                       ~12     0, !3
        16    > JMPZ                                                     ~12, ->22
        17  >   INIT_METHOD_CALL                                         !2, 'add'
        18      SEND_VAR                                                 !0
        19      DO_FCALL_BY_NAME                              1  $14     
        20      QM_ASSIGN_VAR                                    $15     $14
        21    > JMP                                                      ->23
        22  >   QM_ASSIGN_VAR                                    $15     !2
        23  >   ASSIGN                                                   !4, $15
  12    24    > JMP                                                      ->13
        25  > > RETURN                                                   1

脚本2:

<?php


$dateInterval = 5;
$startDate = new DateTime();

for($di = 0; $di <= $dateInterval; ++$di)
{
    $theDate = ($di > 0) ? $startDate->add(new DateInterval('P1D')) : $startDate;

   // ...
}

操作码:

number of ops:  25
compiled vars:  !0 = $dateInterval, !1 = $startDate, !2 = $di, !3 = $theDate
line     # *  op                           fetch          ext  return  operands
---------------------------------------------------------------------------------
   4     0  >   ASSIGN                                                   !0, 5
   5     1      FETCH_CLASS                                   4  :1      'DateTime'
         2      NEW                                              $2      :1
         3      DO_FCALL_BY_NAME                              0          
         4      ASSIGN                                                   !1, $2
   7     5      ASSIGN                                                   !2, 0
         6  >   IS_SMALLER_OR_EQUAL                              ~6      !2, !0
         7    > JMPZNZ                                        A          ~6, ->24
         8  >   PRE_INC                                                  !2
         9    > JMP                                                      ->6
   9    10  >   IS_SMALLER                                       ~8      0, !2
        11    > JMPZ                                                     ~8, ->21
        12  >   INIT_METHOD_CALL                                         !1, 'add'
        13      FETCH_CLASS                                   4  :10     'DateInterval'
        14      NEW                                              $11     :10
        15      SEND_VAL                                                 'P1D'
        16      DO_FCALL_BY_NAME                              1          
        17      SEND_VAR_NO_REF                               0          $11
        18      DO_FCALL_BY_NAME                              1  $13     
        19      QM_ASSIGN_VAR                                    $14     $13
        20    > JMP                                                      ->22
        21  >   QM_ASSIGN_VAR                                    $14     !1
        22  >   ASSIGN                                                   !3, $14
  12    23    > JMP                                                      ->8
        24  > > RETURN                                                   1

现在您可以在第二个脚本的操作码中看到,它在每次迭代时都会创建一个类的新实例,并且您会创建一个似乎不需要的巨大开销(查看第二个操作码中的 # * 13 - 16),因此,首先您要创建完全不必要的新实例,其次这会降低您的性能。

只要您不需要每次迭代都需要一个全新的类,那么第一个脚本对您来说会更好,因为它只创建一次 DateInterval 对象并将其分配给变量。


还有一点额外的信息:

【讨论】:

    【解决方案2】:

    两个版本在逻辑上是不同的。

    第一个版本多次传递同一个对象引用。这意味着如果您在循环之后更改 $dateStep,您将更改引用的 all

    第二个版本在您每次调用该方法时应用对单个对象的引用。


    在使用DateTime::add() 方法的示例中,$dateStep 可以看作是一个不会改变的常数值。在这种情况下,第一个版本更快,更可取,因为它不需要每次都调用构造函数,而且它确实需要更少的内存,因为对象只需要创建一次。

    【讨论】:

    • 我当然完全理解逻辑差异,您在回答中强调了。也许,我在我的问题中表达得不够充分(对此感到抱歉),但我专门要求这样的场景——当对象的创建返回静态值时,它会在以下循环中一遍又一遍地使用。在这种情况下,您的第二个回复(以及此处的其他回复)适用。谢谢。
    • 我改写了。它更多地是写给匿名读者的,而不是写给你的。 :) .. 在这种情况下,如果您使用示例中的静态值,第一个版本会更好,因为我提到的原因是:更少的内存使用,更少的函数执行。
    【解决方案3】:

    我会选择下面的

    $dateStep = new DateInterval('P1D');
    
    for($di = 0; $di > $dateInterval; ++$di)
    {
        $theDate = ($di > 0) $startDate->add($dateStep);
    
        ...
    }
    

    除非确实有必要,否则不建议在循环中一遍又一遍地创建对象。在上述情况下,对象是在循环之前创建的,并且传递了对象以节省时间(用于创建另一个对象)和空间(通过不在内存中创建另一个对象)。

    【讨论】:

      【解决方案4】:

      肯定是第一个更快。 N 类实例比 1 个变量声明更昂贵。

      【讨论】:

        最近更新 更多