This question already has answers here:
Stacking Multiple Ternary Operators in PHP
(11 answers)
Closed 2 years ago.
I was in class the other day and this snippet of code was presented:
<?php
//Intialize the input
$score=rand(50,100);
//Determine the Grade
$grade=($score>=90)?'A':(
($score>=80)?'B':(
($score>=70)?'C':(
($score>=60)?'D':'F')));
//Output the Results
echo "<h1>A score of $score = $grade</h1>";
?>
At the time I questioned the order of operations within the nested ternary operators, thinking that they would evaluate from the inside out, that is it would evaluate if $score were >= 60 first, then if $score >= 70, etc -- working through the whole stack every time regardless of the score.
To me it seems that this construct should follow the same order of precedence given to mathematical operators -- resolving the inner-most set of parentheses first and then working out, unless there is some order of operations unique to the ternary.
Unfortunately the discussion in class quickly became about winning an argument, when I really just wanted to understand how it works. So my questions are two:
(1)How would this statement me interpreted and why?
and
(2)Is the some sort of stack trace or step through tool that would allow me to watch how this code executes?
PHP respects brackets. Expressions inside the innermost ( ... ) are evaluated first, like we are taught in elementary school.
PHP is unusual in that ternary operators are left-associative. This means without brackets, the ternary expression is evaluated left to right.
But in this particular case, the brackets force the expression to be evaluated right to left. This code is equivalent to:
if ($score >= 90) {
$grade = 'A';
}
elseif ($score >= 80) {
$grade = 'B';
}
elseif ($score >= 70) {
$grade = 'C';
}
...
The ternary operator is left associative, but with brackets applied, it evaluates right to left.
You might use xdebug or phpdbg as a step debugger to step through your code and see how it evaluates.
There is also VulcanLogicDumper around, which shows the instructions:
http://3v4l.org/QeF9i/vld#tabs compared to an if-elseif-else structure http://3v4l.org/bZE6M/vld#tabs
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > SEND_VAL 50
1 SEND_VAL 100
2 DO_FCALL 2 $0 'rand'
3 ASSIGN !0, $0
5 4 IS_SMALLER_OR_EQUAL ~2 90, !0
5 > JMPZ ~2, ->8
6 > QM_ASSIGN ~3 'A'
7 > JMP ->24
6 8 > IS_SMALLER_OR_EQUAL ~4 80, !0
9 > JMPZ ~4, ->12
10 > QM_ASSIGN ~5 'B'
11 > JMP ->23
7 12 > IS_SMALLER_OR_EQUAL ~6 70, !0
13 > JMPZ ~6, ->16
14 > QM_ASSIGN ~7 'C'
15 > JMP ->22
8 16 > IS_SMALLER_OR_EQUAL ~8 60, !0
17 > JMPZ ~8, ->20
18 > QM_ASSIGN ~9 'D'
19 > JMP ->21
20 > QM_ASSIGN ~9 'F'
21 > QM_ASSIGN ~7 ~9
22 > QM_ASSIGN ~5 ~7
23 > QM_ASSIGN ~3 ~5
24 > ASSIGN !1, ~3
10 25 ADD_STRING ~11 '%3Ch1%3EA+score+of+'
26 ADD_VAR ~11 ~11, !0
27 ADD_STRING ~11 ~11, '+%3D+'
28 ADD_VAR ~11 ~11, !1
29 ADD_STRING ~11 ~11, '%3C%2Fh1%3E'
30 ECHO ~11
31 > RETURN 1
How to read these Opcodes
I will try to explain the first JMPZ in the opcode, in order to understand how it evaluates:
Of interest is Line 5, Opcode Number 5:
5 > JMPZ ~2, ->8
This means: If compare with 90 (opcode 4) is false, then JUMP to Opcode 8.
Warning: ->8 doesn't mean jump to Line 8.
Now, what is Opcode 8? The comparison with 80
6 8 > IS_SMALLER_OR_EQUAL ~4 80, !0
And now it's safe to say that this doesn't evaluate, like you expected from inside out (90->60->70), but like an if-elseif-else structure (90->80->70).
The ternary operator short-circuits. Only the appropriate operand is evaluated. This means that the parens do not matter until they are actually tested.
echo false ? (crash() / 0) : "Worked.";
ternary are left to right:
http://php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary
So it will evaluate what is to the left of the ? and based on that, evaluate either the 1st or 2nd side of the :.
You could put in a function call that has a side effect to demonstrate this:
function p($a,$b) { echo $a . " >= " . $b; return $a>=$b; }
$grade=(p($score,90))?'A':(
p($score,80)?'B':(
p($score,70)?'C':(
p($score,60)?'D':'F')));
Related
Setting a variable twice
$var = 2;
if ($someThing) {
$var = 1;
}
VS
Using an else
if ($someThing) {
$var = 1;
}
else {
$var = 2;
}
I know that$someThing will get evaluated in both cases. $var Is also going to be set in both cases. In the former it's set once then 50/50 chance to get set again. The later it's set only once, but there's an else block.
I was just curious if anyone has done any sort of testing with something similar. I know this is really micro-optimizing, but just a random thought that I had.
You can look at the opcode steps for each option using the Vulcan Logic Dump
Option #1
compiled vars: !0 = $var, !1 = $someThing
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > ASSIGN !0, 2
4 1 > JMPZ !1, ->3
5 2 > ASSIGN !0, 1
3 > > RETURN 1
Option #2
compiled vars: !0 = $someThing, !1 = $var
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > > JMPZ !0, ->3
4 1 > ASSIGN !1, 1
2 > JMP ->4
7 3 > ASSIGN !1, 2
4 > > RETURN 1
and also using the Ternary operator
compiled vars: !0 = $var, !1 = $someThing
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
3 0 E > > JMPZ !1, ->3
1 > QM_ASSIGN ~2 1
2 > JMP ->4
3 > QM_ASSIGN ~2 2
4 > ASSIGN !0, ~2
5 > RETURN 1
EDIT
As Barmar points out, not all methods execute all the steps, because there are jumps here, and not all steps have an equal processing cost either
It's not about chance whether $someThing is fulfilled, but about the effort the machine has to make. Let's see:
$var = 2;
if ($someThing) {
$var = 1;
}
means min. 1 assignment and 1 check and max. 2 assignments and 1 check.
if ($someThing) {
$var = 1;
}
else {
$var = 2;
}
means min. 1 assignment and 1 check and max. 1 assignment and 1 check.
The else is optimal.
Note: If $someThing depends on $var outside this piece of code and it is called repeatedly and want to be absolutely sure that your code is optimal you'll have to do an amotized cost analysis which isn't too trivial.
You have to imagine it in assembly, which can differ based on architectures.
Here's some basic pseudocode:
write $var 2
bne $something x
write $var 1
x:
or
bne $something x
write $var 1
jmp y:
x:
write $var 2
y:
The first one has an extra write operation sometimes. The second one has an extra jump instruction sometimes. I would think that the jump instruction would be faster, and branch prediction would allow it to be optimised further under the hood, so I'd go for the second option.
I am quite new to PHP, and I have a question about IF statements.
Example 1:
<?php
if($a == 1){
if($b == 1){
echo 'test';
}
}
?>
Example 2:
<?php
if($a == 1 && $b ==1){
echo 'test';
}
?>
Both have the same result, but which one is faster? Does it even matter?
This is Premature Optimization & Micro Benchmark , you really need to read Don't be STUPID: GRASP SOLID! to understand why i said so
But if you want to know if($a == 1 && $b ==1) seems faster is most PHP versions
See Benchmark
If you want to know the real difference then look at the opcodes
First Code :
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > IS_EQUAL ~0 !0, 1
1 > JMPZ ~0, ->7
3 2 > IS_EQUAL ~1 !1, 1
3 > JMPZ ~1, ->6
4 4 > ECHO 'test'
5 5 > JMP ->6
6 6 > > JMP ->7
7 > > RETURN 1
Secound Code
line # * op fetch ext return operands
---------------------------------------------------------------------------------
2 0 > IS_EQUAL ~0 !0, 1
1 > JMPZ_EX ~0 ~0, ->4
2 > IS_EQUAL ~1 !1, 1
3 BOOL ~0 ~1
4 > > JMPZ ~0, ->7
3 5 > ECHO 'test'
4 6 > JMP ->7
7 > > RETURN 1
Can you see how similar with very minimal difference. And that is why it does not make sense to worry about this light this but write good and readable code.
They are the same - in both cases if the first condition is false, the second will not be tested.
Both are the same. There are not much code to be optimized you can even write to make shorter syntax.
<?php
echo $a && b ? 'test' : '';
?>
Does the same.
I've modified a bit Baba's benchmark to check the results for shorthand syntax.
Results
Preoptimization is the root to all evil.
That said, your first piece of code is a tiny bit faster (but again, minimally - don't bother to change your code to this - readability is way more important than the tiny speed incremention you get from changing your conditions.
3,000,000 iterations of the first piece of code: ~ 0.9861679077 seconds
3,000,000 iterations of the second piece of code: ~ 1.0684559345 seconds
Difference: ~ 0.0822880268 seconds
Difference per iteration: ~ 0.0000000274 seconds (or 274 nano seconds).
No, it does not matter. Such little performance tweaks are usually overcome by the running environment. Such as inefficient algorithms or client side best practices being ignored.
Both are same, because PHP interpreter is "smart enough" to figure out that.
I'm trying to understand the op code for an simple code.
The code is:
<?php
$a = TRUE;
$b = FALSE;
if($a && $b) {
echo 'done';
}
The op code for the above code is:
php -dvld.active=1 test.php
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = 3, Position 2 = 4
Branch analysis from position: 3
Jump found. Position 1 = 5, Position 2 = 7
Branch analysis from position: 5
Jump found. Position 1 = 7
Branch analysis from position: 7
Return found
Branch analysis from position: 7
Branch analysis from position: 4
filename: /home/starlays/learning/test.php
function name: (null)
number of ops: 8
compiled vars: !0 = $a, !1 = $b
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > ASSIGN !0, true
5 1 ASSIGN !1, false
7 2 > JMPZ_EX ~2 !0, ->4
3 > BOOL ~2 !1
4 > > JMPZ ~2, ->7
8 5 > ECHO 'done'
9 6 > JMP ->7
10 7 > > RETURN 1
branch: # 0; line: 3- 7; sop: 0; eop: 2; out1: 3; out2: 4
branch: # 3; line: 7- 7; sop: 3; eop: 3; out1: 4
branch: # 4; line: 7- 7; sop: 4; eop: 4; out1: 5; out2: 7
branch: # 5; line: 8- 9; sop: 5; eop: 6; out1: 7
branch: # 7; line: 10- 10; sop: 7; eop: 7
path #1: 0, 3, 4, 5, 7,
path #2: 0, 3, 4, 7,
path #3: 0, 4, 5, 7,
path #4: 0, 4, 7,
I'm trying to understand what is happening on line 7, how is the evaluation done? How many values does it enter in the expression of if for evaluation? It enters 3 values, or it enters the 2 values the value of $a and value of $b and the expression from the parentheses of if is evaluated afterwards?
I have read the manual for JMPZ_EX, I've understand what is happening in the op code until step 2 after that is a little bit mixed up and it is very hard to me to understand what are exact steps that php is doing.
Another thing that I need to understand is what are all the branches in the op code, which of all that branches will be used at the end?
Unless you are proficient at ASM, I think the easiest way to understand what is happening is looking at the same code by reading its (almost) 1:1 representation in PHP:
if(!$a) goto end;
if(!$b) goto end;
echo 'done';
end: return 0;
The intermediate representation is based on the negations of your actual clauses to make a jump over the code contained in the if block.
If you want to really understand how PHP transforms its input to this opcode array, you'll have to learn about PHP internals, but not before studying the dragon book, particularily the parts about intermediate representation, which is part of the compilation pipeline.
The rest of the opcodes are "background noise", intermediate values, or even one instruction which makes no sense 9 6 > JMP ->7 which simply exists probably because it didn't make sense to put the effort into making the PHP parser spit out the most optimal opcode array for the ZendVM to be run by.
line # * op fetch ext return operands
---------------------------------------------------------------------------------
3 0 > ASSIGN !0, true
5 1 ASSIGN !1, false
7 2 > JMPZ_EX ~2 !0, ->4
3 > BOOL ~2 !1
4 > > JMPZ ~2, ->7
8 5 > ECHO 'done'
9 6 > JMP ->7
10 7 > > RETURN 1
Going by the line numbers #
0 assigns true to !0, !0 is just the internal representation of $A
1 assigns true to !1, !1 is $B
JMPZ means to jump to code if the value is 0. I'm not sure the specific difference of JMPZ_EX it looks like it allows the return of a boolean result.
So:
2 JMPZ_EX, Jump to #4 (->4) if !0 ($A) is 0 (FALSE) and assign the result to ~2
3 BOOL !1 return ~2. ~2 is now equal to the BOOLean value of !1 ($B)
4 JMPZ ~2, Jump to #7 if ~2 is zero
5 ECHO, our echo statement. If any of the JMPZ had jumped, this part would be skipped.
6 JMP -7, jumps to #7
7 RETURN, ends the function call
Some notes:
It seems like the JMPZ_EX is unnecessary in this case, but would be useful in more complex if statements where you need to use the value in calculating further values.
6 JMP -7 is probably in there to allow for an else block. If this was the main part of the if block, finishing it could then jump over the portion of code that was the else block.
What exactly does this mean?
$number = ( 3 - 2 + 7 ) % 7;
It's the modulus operator, as mentioned, which returns the remainder of a division operation.
Examples: 3%5 returns 3, as 3 divided by 5 is 0 with a remainder of 3.
5 % 10 returns 5, for the same reason, 10 goes into 5 zero times with a remainder of 5.
10 % 5 returns 0, as 10 divided by 5 goes exactly 2 times with no remainder.
In the example you posted, (3 - 2 + 7) works out to 8, giving you 8 % 7, so $number will be 1, which is the remainder of 8/7.
It is the modulus operator:
$a % $b = Remainder of $a
divided by $b.
It is often used to get "one element every N elements". For instance, to only get one element each three elements:
for ($i=0 ; $i<10 ; $i++) {
if ($i % 3 === 0) {
echo $i . '<br />';
}
}
Which gets this output:
0
3
6
9
(Yeah, OK, $i+=3 would have done the trick; but this was just a demo.)
It is the modulus operator. In the statement $a % $b the result is the remainder when $a is divided by $b
Using this operator one can easily calculate odd or even days in month for example, if needed for schedule or something:
<?php echo (date(j) % 2 == 0) ? 'Today is even date' : 'Today is odd date'; ?>
% means modulus.
Modulus is the fancy name for "remainder after divide" in mathematics.
(numerator) mod (denominator) = (remainder)
In PHP
<?php
$n = 13;
$d = 7
$r = "$n % $d";
echo "$r is ($n mod $d).";
?>
In this case, this script will echo
6 is (13 mod 7).
Where $r is for the remainder (answer), $n for the numerator and $d for the denominator. The modulus operator is commonly used in public-key cryptography due to its special characteristic as a one-way function.
Since so many people say "modulus finds the remainder of the divisor", let's start by defining exactly what a remainder is.
In mathematics, the remainder is the amount "left over" after
performing some computation. In arithmetic, the remainder is the
integer "left over" after dividing one integer by another to produce
an integer quotient (integer division).
See: http://en.wikipedia.org/wiki/Remainder
So % (integer modulus) is a simple way of asking, "How much of the divisor is left over after dividing?"
To use the OP's computation of (3 - 2 + 7) = 8 % 7 = 1:
It can be broken down into:
(3 - 2 + 7) = 8
8 / 7 = 1.143 #Rounded up
.143 * 7 = 1.001 #Which results in an integer of 1
7 can go into 8 1 time with .14 of 7 leftover
That's all there is to it. I hope this helps to simplify how exactly modulus works.
Additional examples using different divisors with 21.
Breakdown of 21 % 3 = 0:
21 / 3 = 7.0
3 * 0 = 0
(3 can go into 21 7 times with 0 of 3 leftover)
Breakdown of 21 % 6 = 3:
21 / 6 = 3.5
.5 * 6 = 3
(6 can go into 21 3 times with .5 of 6 leftover)
Breakdown of 21 % 8 = 5:
21 / 8 = 2.625
.625 * 8 = 5
(8 can go into 21 2 times with .625 of 8 leftover)
In this question a code bit is presented and the questioner wants to make it faster by eliminating the use of variables. Seems to me he's looking in the wrong place, but far be it from me to know. Here's the code
while ($item = current($data))
{
echo '<ATTR>',$item, '</ATTR>', "\n";
next($data);
}
Seems to me that the recreation of the strings <ATTR> etc. -- more than once on each line and every time the line is processed -- would have a cost associated with them (both in terms of speed and memory). Or perhaps the PHP processor smart enough so that there's no penalty to not putting the strings into variables before the loop?
I use variables for clarity and centralization in any case, but: is there a cost associated with using variables, not using variables, or what? (Anybody who wants to answer for other similar languages please feel free.)
If you really want to micro-optimize this way (I don't think it is that relevant or useful, btw -- but I understand it's fun ^^ ), you can have a look at a PHP extension called Vulcan Logic Disassembler
It allows you to get the bytecode generated for a PHP script.
Then, you must use a command like this one, in command line, to launch the script :
php -dextension=vld.so -dvld.active=1 tests/temp/temp.php
For instance, with this script :
$data = array('a', 'b', 'c', 'd');
while ($item = current($data))
{
echo '<ATTR>',$item, '</ATTR>', "\n";
next($data);
}
You will get this bytecode dump :
line # op fetch ext return operands
-------------------------------------------------------------------------------
8 0 EXT_STMT
1 INIT_ARRAY ~0 'a'
2 ADD_ARRAY_ELEMENT ~0 'b'
3 ADD_ARRAY_ELEMENT ~0 'c'
4 ADD_ARRAY_ELEMENT ~0 'd'
5 ASSIGN !0, ~0
9 6 EXT_STMT
7 EXT_FCALL_BEGIN
8 SEND_REF !0
9 DO_FCALL 1 'current'
10 EXT_FCALL_END
11 ASSIGN $3 !1, $2
12 JMPZ $3, ->24
11 13 EXT_STMT
14 ECHO '%3CATTR%3E'
15 ECHO !1
16 ECHO '%3C%2FATTR%3E'
17 ECHO '%0A'
12 18 EXT_STMT
19 EXT_FCALL_BEGIN
20 SEND_REF !0
21 DO_FCALL 1 'next'
22 EXT_FCALL_END
13 23 JMP ->7
37 24 RETURN 1
25* ZEND_HANDLE_EXCEPTION
And with this script :
$data = array('a', 'b', 'c', 'd');
while ($item = current($data))
{
echo "<ATTR>$item</ATTR>\n";
next($data);
}
You will get :
line # op fetch ext return operands
-------------------------------------------------------------------------------
19 0 EXT_STMT
1 INIT_ARRAY ~0 'a'
2 ADD_ARRAY_ELEMENT ~0 'b'
3 ADD_ARRAY_ELEMENT ~0 'c'
4 ADD_ARRAY_ELEMENT ~0 'd'
5 ASSIGN !0, ~0
20 6 EXT_STMT
7 EXT_FCALL_BEGIN
8 SEND_REF !0
9 DO_FCALL 1 'current'
10 EXT_FCALL_END
11 ASSIGN $3 !1, $2
12 JMPZ $3, ->25
22 13 EXT_STMT
14 INIT_STRING ~4
15 ADD_STRING ~4 ~4, '%3CATTR%3E'
16 ADD_VAR ~4 ~4, !1
17 ADD_STRING ~4 ~4, '%3C%2FATTR%3E%0A'
18 ECHO ~4
23 19 EXT_STMT
20 EXT_FCALL_BEGIN
21 SEND_REF !0
22 DO_FCALL 1 'next'
23 EXT_FCALL_END
24 24 JMP ->7
39 25 RETURN 1
26* ZEND_HANDLE_EXCEPTION
(This ouput is with PHP 5.2.6, which is the default on Ubuntu Jaunty)
In the end , you will probably notice there is not that much differences, and that it's often really just micro-optimisation ^^
What might be more interesting is to look at the differences between versions of PHP : you might seen that some operations have been optimized between PHP 5.1 and 5.2, for instance.
For more informations, you can also have a look at Understanding Opcodes
Have fun !
EDIT : adding another test :
With this code :
$attr_open = '<ATTR>';
$attr_close = '</ATTR>';
$eol = "\n";
$data = array('a', 'b', 'c', 'd');
while ($item = current($data))
{
echo $attr_open, $item, $attr_close, $eol;
next($data);
}
You get :
line # op fetch ext return operands
-------------------------------------------------------------------------------
19 0 EXT_STMT
1 ASSIGN !0, '%3CATTR%3E'
20 2 EXT_STMT
3 ASSIGN !1, '%3C%2FATTR%3E'
21 4 EXT_STMT
5 ASSIGN !2, '%0A'
23 6 EXT_STMT
7 INIT_ARRAY ~3 'a'
8 ADD_ARRAY_ELEMENT ~3 'b'
9 ADD_ARRAY_ELEMENT ~3 'c'
10 ADD_ARRAY_ELEMENT ~3 'd'
11 ASSIGN !3, ~3
24 12 EXT_STMT
13 EXT_FCALL_BEGIN
14 SEND_REF !3
15 DO_FCALL 1 'current'
16 EXT_FCALL_END
17 ASSIGN $6 !4, $5
18 JMPZ $6, ->30
26 19 EXT_STMT
20 ECHO !0
21 ECHO !4
22 ECHO !1
23 ECHO !2
27 24 EXT_STMT
25 EXT_FCALL_BEGIN
26 SEND_REF !3
27 DO_FCALL 1 'next'
28 EXT_FCALL_END
28 29 JMP ->13
43 30 RETURN 1
31* ZEND_HANDLE_EXCEPTION
And, with this one (concatenations instead of ',') :
$attr_open = '<ATTR>';
$attr_close = '</ATTR>';
$eol = "\n";
$data = array('a', 'b', 'c', 'd');
while ($item = current($data))
{
echo $attr_open . $item . $attr_close . $eol;
next($data);
}
you get :
line # op fetch ext return operands
-------------------------------------------------------------------------------
19 0 EXT_STMT
1 ASSIGN !0, '%3CATTR%3E'
20 2 EXT_STMT
3 ASSIGN !1, '%3C%2FATTR%3E'
21 4 EXT_STMT
5 ASSIGN !2, '%0A'
23 6 EXT_STMT
7 INIT_ARRAY ~3 'a'
8 ADD_ARRAY_ELEMENT ~3 'b'
9 ADD_ARRAY_ELEMENT ~3 'c'
10 ADD_ARRAY_ELEMENT ~3 'd'
11 ASSIGN !3, ~3
24 12 EXT_STMT
13 EXT_FCALL_BEGIN
14 SEND_REF !3
15 DO_FCALL 1 'current'
16 EXT_FCALL_END
17 ASSIGN $6 !4, $5
18 JMPZ $6, ->30
26 19 EXT_STMT
20 CONCAT ~7 !0, !4
21 CONCAT ~8 ~7, !1
22 CONCAT ~9 ~8, !2
23 ECHO ~9
27 24 EXT_STMT
25 EXT_FCALL_BEGIN
26 SEND_REF !3
27 DO_FCALL 1 'next'
28 EXT_FCALL_END
28 29 JMP ->13
43 30 RETURN 1
31* ZEND_HANDLE_EXCEPTION
So, never much of a difference ^^
Here is an interesting one, my initial tests show that storing the newline char into a variable instead of PHP parsing it with each iteration is faster. See below:
$nl = "\n";
while ($item = current($data))
{
echo '<ATTR>',$item, '</ATTR>',$nl;
next($data);
}
There seems to be no measurable difference in using the string literals inside the loop vs. moving them to variables outside the loop. I threw together the following simple script to test this:
$length = 100000;
$data = array();
$totals = array();
for ($i = 0; $i < $length; $i++) {
$data[] = rand(1,1000);
}
$start = xdebug\_time\_index();
while ($item = current($data))
{
echo '<ATTR>',$item,'</ATTR>',PHP_EOL;
next($data);
}
$end = xdebug\_time\_index();
$total = $end - $start;
$totals["Warmup:"] = $total;
reset($data);
$start = xdebug\_time\_index();
while ($item = current($data))
{
echo '<ATTR>',$item,'</ATTR>',PHP_EOL;
next($data);
}
$end = xdebug\_time\_index();
$total = $end - $start;
$totals["First:"] = $total;
reset($data);
$startTag = '<ATTR>';
$endTag = '</ATTR>';
$start = xdebug\_time\_index();
while ($item = current($data))
{
echo $startTag,$item,$endTag,PHP_EOL;
next($data);
}
$end = xdebug\_time\_index();
$total = $end - $start;
$totals["Second:"] = $total;
foreach ($totals as $label => $data) {
echo $label,' ', $data,PHP_EOL;
}
I ran this several times and saw no discernable difference between the differing methods. In fact, sometimes the warmup was the fastest of the three.
When trying to microoptimize things such as this you really end up measuring the performance of the machine you are on more often than the actual code. Of note, you may want to use PHP_EOL instead of \n or defining a variable containing such.
Actually this is probably the fastest implementation. You could try to concat all in to one string but all of the concat operations are pretty expensive.
Everything has a cost. The goal is to minimize that cost as much as possible.
If you were thinking about concatenation check this resource for information on its performance. It's probably best to leave the code as-is.
If you really want to speed this up, use this instead:
ob_start();
while ($item = current($data))
{
echo '<ATTR>',$item, '</ATTR>', "\n";
next($data);
}
Output buffering flushes content more efficiently to the client, which speeds up your code much more than any micro-optimization can.
As an aside, in my experience micro-optimization is a useless endeavour when it comes to PHP code. I've never seen a performance problem get solved by clever use of a particular concatenation or variable declaration method. Real solutions tend to involve change to design or architecture or the use of less complicated algorithms.