PHP 8.4.2 Released!

表达式

表达式是 PHP 最重要的构建部分。在 PHP 中,几乎所写的任何东西都是表达式。简单但却最精确的定义表达式的方式就是“任何有值的东西”。

最基本的表达式形式是常量和变量。当键入 $a = 5,即值 5 将分配给变量 $a。很明显,5的值为 5,换句话说 5 是一个值为 5 的表达式(在这里,5 是一个整型常量)。

赋值之后,期待 $a 的值为 5,因而如果写下 $b = $a,则期望其行为就像 $b = 5 一样。换句话说,$a 也是值为 5 的表达式。如果一切正确,那这正是将要发生的事情。

稍微复杂的表达式例子就是函数。例如,考虑以下函数:

<?php
function foo ()
{
return
5;
}
?>

假如已经熟悉了函数的概念(如果没有,请查看函数的相关章节),那么本质上键入 $c = foo() 就如同写下 $c = 5,这是对的。函数是具有返回值的表达式。由于 foo() 返回 5,表达式“foo()”的值也是 5。通常函数不仅会返回静态值,还会计算一些东西。

当然,PHP 中的值并非都是整型的。PHP 支持四种标量值(标量值不能拆分为更小的单元,例如,和数组不同)类型:int 值、浮点数值(float)、string 值和 bool 值。PHP 也支持两种复合类型:数组和对象。这些值类型可以赋值给变量或者从函数返回。

PHP 和其它语言一样在表达式的道路上发展,但推进得更深远。PHP 是一种面向表达式的语言,从这一方面来讲几乎一切都是表达式。考虑刚才已经研究过的例子 $a = 5。很显然这里涉及到两个值,整数常量 5 的值以及变量 $a 的值,也被更新为 5。但事实是这里还涉及到一个额外的值,即附值语句本身的值。赋值语句本身求值为被赋的值,即 5。实际上这意味着 $a = 5,无论做什么,都是一个值为 5 的表达式。因此编写 $b = ($a = 5) 跟编写 $a = 5; $b = 5;(分号标志着语句的结束)是一样的。因为赋值操作的顺序是由右到左的,也可以写成 $b = $a = 5

另外一个很好的面向表达式的例子就是前、后递增和递减。PHP 和多数其它语言的用户应该比较熟悉 variable++variable-- 符号。即递增和递减运算符。在 PHP 和 C 语言中,有两种类型的递增——前递增和后递增。本质上来讲,前递增和后递增均增加了变量的值,并且对于变量的影响是相同的。不同的是递增表达式的值。前递增,写做 ++$variable,求增加后的值(PHP 在读取变量的值之前,增加变量的值,因而称之为“前递增”)。后递增,写做 $variable++,求 $variable 未递增之前的原始值(PHP 在读取变量的值之后,增加变量的值,因而叫做“后递增”)。

一个常用到的表达式类型是比较表达式。这些表达式求值 falsetrue。PHP 支持 >(大于)、>=(大于等于)、==(等于)、!=(不等于)、<(小于)、<= (小于等于)。PHP 还支持全等运算符 ===(值和类型均相同)和非全等运算符 !==(值或者类型不同)。这些表达式都是在条件判断语句,比如 if

这里,将要研究的最后一个例子是组合的运算赋值表达式。已经知道如果想要为变量 $a 加 1,可以简单的写 $a++ 或者 ++$a。但是如果想为变量增加大于 1 的值,比如 3,该怎么做?可以写 $a++ 多次,但这样明显不是一种高效舒适的方法,一个更加通用的做法是 $a = $a + 3$a + 3 计算 $a 加上 3 的值,并将其赋予给 $a,于是 $a 的值增加了 3。在 PHP 及其它几种类似 C 的语言中,可以以一种更加简短的形式完成上述功能,随着时间的推移,会变得更加清晰、更容易理解。为 $a 的当前值加 3,可以这样写 $a += 3。这里的意思是“取变量 $a 的值,加 3,得到的结果再次分配给变量 $a”。除了更短和清楚外,也可以加快执行速度。$a += 3 的值,如同一个正常赋值操作的值,是赋值后的值。注意它不是 3,而是 $a 的值加上3 之后的值(此值将被赋给 $a)。任何二元运算符都可以用运算赋值模式,例如 $a -= 5(从变量 $a 的值中减去 5),$b *= 7(变量 $b 乘以 7),等等。

还有一个表达式,如果没有在别的语言中看到过的话,可能看上去很奇怪,即三元条件运算符:

<?php
$first
? $second : $third
?>

如果第一个子表达式的值是 true(非零),那么计算第二个子表达式的值,其值即为整个表达式的值。否则,将第三个子表达式的值作为整个表达式的值。

下面的例子有助于更好的理解前、后递增和表达式:

<?php
function double($i)
{
return
$i*2;
}
$b = $a = 5; /* 赋值 5 到变量 $a 和 $b */
$c = $a++; /* 后自增,将 $a(5)的原始值赋给 $c */
$e = $d = ++$b; /* 前自增,将 $b(6)的递增值赋给 $d 和 $e */

/* 此时, $d 和 $e 都等于 6 */

$f = double($d++); /* 自增前将两倍值赋值给 $d ,即 2*6 = 12 赋值给 $f */
$g = double(++$e); /* 自增后将两倍值赋值给 $e ,即 2*7 = 14 赋值给 $g */
$h = $g += 10; /* 首先,$g 自增 10 后等于 24。然后将值(24)赋值给了 $h,
$h 的值也等于 24。 */
?>

一些表达式可以被当成语句。这时,一条语句的形式是 'expr ;',即一个表达式加一个分号结尾。在 $b = $a = 5; 中,$a = 5 是一个有效的表达式,但它本身不是一条语句。$b = $a = 5; 是一条有效的语句。

最后一件值得提起的事情就是表达式的真假值。在许多事件中,主要是在条件执行和循环中,不要关心表达式中的具体值,反而要注意表达式的值是否是 true 或者 false。常量 truefalse(大小写无关)是两种可能的 布尔值。必要时,表达式将自动转换为布尔值。参见类型强制转换获取详细信息。

PHP 提供了一套完整强大的表达式实现,并且完全纪录它超出了本手册的范围。上面的例子帮助了解了表达式的概念以及如何构建有用的表达式。在本手册的其余部分,将编写 expr 来指示任何有效的 PHP 表达式。

添加备注

用户贡献的备注 12 notes

up
55
Magnus Deininger, dma05 at web dot de
15 years ago
Note that even though PHP borrows large portions of its syntax from C, the ',' is treated quite differently. It's not possible to create combined expressions in PHP using the comma-operator that C has, except in for() loops.

Example (parse error):

<?php

$a
= 2, $b = 4;

echo
$a."\n";
echo
$b."\n";

?>

Example (works):
<?php

for ($a = 2, $b = 4; $a < 3; $a++)
{
echo
$a."\n";
echo
$b."\n";
}

?>

This is because PHP doesn't actually have a proper comma-operator, it's only supported as syntactic sugar in for() loop headers. In C, it would have been perfectly legitimate to have this:

int f()
{
int a, b;
a = 2, b = 4;

return a;
}

or even this:

int g()
{
int a, b;
a = (2, b = 4);

return a;
}

In f(), a would have been set to 2, and b would have been set to 4.
In g(), (2, b = 4) would be a single expression which evaluates to 4, so both a and b would have been set to 4.
up
47
yasuo_ohgaki at hotmail dot com
23 years ago
Manual defines "expression is anything that has value", Therefore, parser will give error for following code.

<?php
($val) ? echo('true') : echo('false');
Note: "? : " operator has this syntax "expr ? expr : expr;"
?>

since echo does not have(return) value and ?: expects expression(value).

However, if function/language constructs that have/return value, such as include(), parser compiles code.

Note: User defined functions always have/return value without explicit return statement (returns NULL if there is no return statement). Therefore, user defined functions are always valid expressions.
[It may be useful to have VOID as new type to prevent programmer to use function as RVALUE by mistake]

For example,

<?php
($val) ? include('true.inc') : include('false.inc');
?>

is valid, since "include" returns value.

The fact "echo" does not return value(="echo" is not a expression), is less obvious to me.

Print() and Echo() is NOT identical since print() has/returns value and can be a valid expression.
up
20
chriswarbo at gmail dot com
11 years ago
Note that there is a difference between a function and a function call, and both
are expressions. PHP has two kinds of function, "named functions" and "anonymous
functions". Here's an example with both:

<?php
// A named function. Its name is "double".
function double($x) {
return
2 * $x;
}

// An anonymous function. It has no name, in the same way that the string
// "hello" has no name. Since it is an expression, we can give it a temporary
// name by assigning it to the variable $triple.
$triple = function($x) {
return
3 * $x;
};
?>

We can "call" (or "run") both kinds of function. A "function call" is an
expression with the value of whatever the function returns. For example:

<?php
// The easiest way to run a function is to put () after its name, containing its
// arguments (if any)
$my_numbers = array(double(5), $triple(5));
?>

$my_numbers is now an array containing 10 and 15, which are the return values of
double and $triple when applied to the number 5.

Importantly, if we *don't* call a function, ie. we don't put () after its name,
then we still get expressions. For example:

<?php
$my_functions
= array('double', $triple);
?>

$my_functions is now an array containing these two functions. Notice that named
functions are more awkward than anonymous functions. PHP treats them differently
because it didn't use to have anonymous functions, and the way named functions
were implemented didn't work for anonymous functions when they were eventually
added.

This means that instead of using a named function literally, like we can with
anonymous functions, we have to use a string containing its name instead. PHP
makes sure that these strings will be treated as functions when it's
appropriate. For example:

<?php
$temp
= 'double';
$my_number = $temp(5);
?>

$my_number will be 10, since PHP has spotted that we're treating a string as if
it were a function, so it has looked up that named function for us.

Unfortunately PHP's parser is very quirky; rather than looking for generic
patterns like "x(y)" and seeing if "x" is a function, it has lots of
special-cases like "$x(y)". This makes code like "'double'(5)" invalid, so we
have to do tricks like using temporary variables. There is another way around
this restriction though, and that is to pass our functions to the
"call_user_func" or "call_user_func_array" functions when we want to call them.
For example:

<?php
$my_numbers
= array(call_user_func('double', 5), call_user_func($triple, 5));
?>

$my_numbers contains 10 and 15 because "call_user_func" called our functions for
us. This is possible because the string 'double' and the anonymous function
$triple are expressions. Note that we can even use this technique to call an
anonymous function without ever giving it a name:

<?php
$my_number
= call_user_func(function($x) { return 4 * $x; }, 5);
?>

$my_number is now 20, since "call_user_func" called the anonymous function,
which quadruples its argument, with the value 5.

Passing functions around as expressions like this is very useful whenever we
need to use a 'callback'. Great examples of this are array_map and array_reduce.
up
19
Mattias at mail dot ee
22 years ago
A note about the short-circuit behaviour of the boolean operators.

1. if (func1() || func2())
Now, if func1() returns true, func2() isn't run, since the expression
will be true anyway.

2. if (func1() && func2())
Now, if func1() returns false, func2() isn't run, since the expression
will be false anyway.

The reason for this behaviour comes probably from the programming
language C, on which PHP seems to be based on. There the
short-circuiting can be a very useful tool. For example:

int * myarray = a_func_to_set_myarray(); // init the array
if (myarray != NULL && myarray[0] != 4321) // check
myarray[0] = 1234;

Now, the pointer myarray is checked for being not null, then the
contents of the array is validated. This is important, because if
you try to access an array whose address is invalid, the program
will crash and die a horrible death. But thanks to the short
circuiting, if myarray == NULL then myarray[0] won't be accessed,
and the program will work fine.
up
13
egonfreeman at gmail dot com
17 years ago
It is worthy to mention that:

$n = 3;
$n * --$n

WILL RETURN 4 instead of 6.

It can be a hard to spot "error", because in our human thought process this really isn't an error at all! But you have to remember that PHP (as it is with many other high-level languages) evaluates its statements RIGHT-TO-LEFT, and therefore "--$n" comes BEFORE multiplying, so - in the end - it's really "2 * 2", not "3 * 2".

It is also worthy to mention that the same behavior will change:

$n = 3;
$n * $n++

from 3 * 3 into 3 * 4. Post- operations operate on a variable after it has been 'checked', but it doesn't necessarily state that it should happen AFTER an evaluation is over (on the contrary, as a matter of fact).

So, if you ever find yourself on a 'wild goose chase' for a bug in that "impossible-to-break, so-very-simple" piece of code that uses pre-/post-'s, remember this post. :)

(just thought I'd check it out - turns out I was right :P)
up
15
winks716
17 years ago
reply to egonfreeman at gmail dot com
04-Apr-2007 07:45

the second example u mentioned as follow:
=====================================

$n = 3;
$n * $n++

from 3 * 3 into 3 * 4. Post- operations operate on a variable after it has been 'checked', but it doesn't necessarily state that it should happen AFTER an evaluation is over (on the contrary, as a matter of fact).

===========================================

everything works correctly but one sentence should be modified:

"from 3 * 3 into 3 * 4" should be "from 3 * 3 into 4 * 3"

best regards~ :)
up
9
petruzanauticoyahoo?com!ar
17 years ago
Regarding the ternary operator, I would rather say that the best option is to enclose all the expression in parantheses, to avoid errors and improve clarity:

<?php
print ( $a > 1 ? "many" : "just one" );
?>

PS: for php, C++, and any other language that has it.
up
7
oliver at hankeln-online dot de
22 years ago
The short-circuiting IS a feature. It is also available in C, so I suppose the developers won?t remove it in future PHP versions.

It is rather nice to write:

$file=fopen("foo","r") or die("Error!");

Greets,
Oliver
up
6
denzoo at gmail dot com
16 years ago
To jvm at jvmyers dot com:
Your first two if statements just check if there's anything in the string, if you wish to actually execute the code in your string you need eval().
up
6
shawnster
17 years ago
An easy fix (although intuitively tough to do...) is to reverse the comparison.

if (5 == $a) {}

If you forget the second '=', you'll get a parse error for trying to assign a value to a non-variable.
up
2
Bichis Paul
7 years ago
Regarding 12345alex at gmx dot net's example:

I think you miss the identical equal documentation line from: http://php.net/manual/en/language.operators.comparison.php

$a == $b Equal TRUE if $a is equal to $b after type juggling.
$a === $b Identical TRUE if $a is equal to $b, and they are of the same type.

Try:
print array() === NULL ? "True" : "False";

Check this:
var_dump(is_null(array()));
up
2
antickon at gmail dot com
12 years ago
evaluation order of subexpressions is not strictly defined for all operators

<?php
function a() {echo 'a';}
function
b() {echo 'b';}
a() == b(); // outputs "ab", ie evaluates left-to-right

$a = 3;
var_dump( $a == $a = 4 ); // outputs bool(true), ie evaluates right-to-left
?>

this is not a bug: "we [php developers] make no guarantee about the order of evaluation".
See https://bugs.php.net/bug.php?id=61188
To Top