If you are somewhat into PHP, you might have come across PHP language constructs or statements about them. For example, “echo
is not a function, but a language construct”, “you cannot call a class ‘Case’, because this is a reserved keyword”, or “there’s no need to use parentheses with language constructs such as require
”. Just like this tweet by Alain Schlesser.
Quick #PHP tip:
require/include are not functions, they are language constructs – no parentheses needed!include __DIR__ . '/filename.php';
— Alain Schlesser (@schlessera) July 28, 2017
This post gives basic background information about PHP language constructs, and explains one specific quirk: the usage of parentheses.
PHP Language Constructs
Just like in a lot of other programming languages, in PHP there are several words with a special meaning. As these words form the foundation of PHP, and therefore construct the language, they are often referred to as PHP language constructs, or (reserved) keywords.
What Are They Good For?
The individual nature of these keywords is arbitrary. Some are specific to program (or control) flow, while others are (special) operators. Quite a few PHP keywords are used to define structures in the language itself, and yet others behave like regular functions (although they’re not).
Some keywords are used in more than one situation, for example, use
for both namespace imports and trait usage, or static
for method and variable/property definition as well as late static binding (e.g., new static();
).
After all, you cannot really do anything (substantial) in PHP without language constructs. Well, OK, except for setting variables when only using constants, scalars and other variables.
To Put or Not to Put in Parentheses
Now, one of the quirks of PHP language constructs is the usage of parentheses. In order to understand what works, what is required and what is syntactically incorrect, we first have a look at parentheses in PHP in general.
Note for all regular PHP developers: the following code examples adhere to the WordPress PHP Coding Standards, which means that there almost always is a space wherever you can put one. 😉 Nonetheless, the code examples are designed to work with PHP 7 or higher. While some examples also work with older versions of PHP, others do not.
Using Parentheses in PHP
There are two types of parentheses: required ones, and optional ones. The next sections explain when and why what is the case.
Required Parentheses
Parentheses can be required by definition of a specific construct in PHP. An example is the declaration of a function (or method). Irrelevant of the number of parameters, parentheses are always required, as illustrated in the following code block:
// Works.
function yes() {}
function yep( $param ) {}
function yeah( $param = null ) {}
// Parse error!
function no {}
function nope $param {}
function hell_no $param = null {}
In contrast to that, some situations require parentheses depending on the individual use case. To better understand this, we have a look at a code example.
function panic() {
echo "PANIC!!!\n";
}
class C {
public $f = 'panic';
public function f() {
echo "Easy, man.\n";
}
}
In the above code, we define a function, and a class with a public field and a public method, both having the same name, f
; that is no problem in PHP. The field holds the name of the function defined before (i.e., a callable), so it can be called or executed.
Now, we instantiate an object of our class, and call f
on it. We have to do this by putting parentheses after it, irrelevant of any arguments passed (i.e., a function call, by definition, requires parentheses). We see that not the panic
function is executed, but the class method with the name f
:
$c = new C();
$c->f();
// Easy, man.
So, how can we execute the callable stored in the field? Well, we have to group the expression with the object operator, like so:
( $c->f )();
// PANIC!!!
A slightly more complex example would include instantiating the object and directly executing the method or callable in one line. This would require an additional pair of parentheses:
( new C() )->f();
// Easy, man.
( ( new C() )->f )();
// PANIC!!!
Optional Parentheses
Almost always where we can use values or value-like expressions (e.g., a variable, some arithmetics, or even some arithmetics stored in a variable), we can wrap these in parentheses. In the end, 42
, ( 42 )
and even ( ( 42 ) )
are the same.
In the following example, we create an array of elements with different numbers of (optional) parentheses, and compute the sum:
echo array_sum( [
1,
( 2 ),
( ( 3 ) ),
] );
// 6
By wrapping, we group what is in between the parentheses. Sometimes this is required to achieve what we want. Consider the following simple calculation:
$result = ( $a + $b ) * ( $a - $b );
In the above code—that’s the third binomial formula, by the way—we have to use parentheses to get the correct result.
Sometimes, however, using optional parentheses can help us understand more complex expressions faster. Here is an example:
return ( $a < 23 )
? ( ( $a and $b ) or ( $a || ( $c && $d ) ) )
: ( ( ( $a || $c ) and ( $b && $d ) ) xor ( $b & $c ) );
No parenthesis is required, but they make reading such code so much easier. Granted, people writing code like this should be forced to survive one full day of Modern Talking, cranked up to eleven. 😀
However, there are situations where you cannot use optional parentheses. One example is when we have a double-quoted string and want to interpolate a variable:
$answer = 42;
echo "The answer is $answer.\n";
// The answer is 42.
echo "The answer is {$answer}.\n";
// The answer is 42.
We know we can (and actually should) wrap variables in curly braces, so both times the expected string is rendered.
Now what if we wrap the variable in parentheses?
echo "The answer is ($answer).\n";
// The answer is (42).
echo "The answer is {($answer)}.\n";
// The answer is {(42)}.
Interesting. The parentheses end up in the output. But that actually makes sense, if we think about that for a while. What might be a surprise, however, is that wrapping the grouped variable in curly braces not only did not result in the output we expected, but the curly braces end up in the output as well. This is because the thing directly on the right of the opening curly brace is not a variable.
Semi-required Parentheses
Last but not least, there are situations where parentheses are required depending on the exact usage. For example, when you instantiate an object with the new
keyword, parentheses might or might not be required. The definition is as follows: if you pass one or more arguments to the constructor, you must use parentheses. Without arguments (i.e., the constructor does not have any mandatory parameters), you can. (And then there are two camps of people who say you should or should not, no matter what.)
Let us construct an example for this.
class LoneRanger {
}
// Works.
new LoneRanger();
new LoneRanger;
In the above code, we have a class constructor that does not have any (mandatory) parameters; actually, it is not even specified. This means we can instantiate an object with or without parentheses.
But what if there was a parameter?
class Person {
public function __construct ( $name = null ) {
$this->name = $name ?? 'Jane';
}
}
In this second example, there is a new class that has a constructor with one parameter. Since there is a default value for that parameter, it is not mandatory, though. Therefore, we can use the constructor without arguments, and thus use parentheses as we like:
// Works.
new Person();
new Person;
As soon as we pass an argument, however, we must use parentheses. If not, we run into a parse error:
// Works, too.
new Person( 'John' );
// Parse error!
new Person 'John';
Using Parentheses With Language Constructs
So, now that we revisited the general parentheses usage in PHP, we finally take a look at language constructs.
Sorry for the spoiler, but … know what? They are no different from what we had before. While some language constructs are really, and I mean really really odd things, they follow the same mechanics: some require parentheses, some don’t care, and some actually require no parentheses.
One of the most well-known language constructs is the if
keyword, which requires parentheses:
if ( true );
if ( true ) {
}
if ( true ) {
echo 'Yatta!';
}
As we learned before, values can be wrapped in additional parentheses, most of the times. In the above example, we could therefore wrap true
in as many pairs of parentheses as we want. If we were to leave all parentheses off, however, we run into the well-known parse error. 😉
There are several language constructs following the same rule as if
, for example, foreach
, switch
, and while
.
Next up are the language constructs that require no parentheses. A few examples are const
, instanceof
, and new
:
// Parse error!
const ( untrue = false );
class Oracle {
// Parse error!
const ( ANSWER ) = 42;
}
// Parse error!
var_dump( ( new stdClass() ) instanceof ( stdClass ) );
// Parse error!
new ( stdClass() );
Finally, let’s come to the example from the tweet with which all started: including files.
In PHP, there are four ways to include a file into the current script: include
, require
, and for each of these a *_once
version. All of these language constructs are in the group with the motto don’t need, don’t care, meaning you don’t have to use parentheses, but you can:
include ( './foo.php' );
include_once MYBASEPATH . '/bar.php';
require dirname( __DIR__ ) . '/baz.php';
require_once ( 'qux.php' );
As you can see in the above code—and as we know already—using parentheses around values (here: the strings containing the file path) is no problem. It is important, however, to know that these parentheses are not the (required) parenthesis of a function—because these language constructs are no regular functions. The parentheses are just for grouping, which is unnecessary here as the string concatenation will be performed before the language constructs take the according file path.
Unnecessary stuff that also isn’t beneficial in any way is no good. Hence, do not use parentheses when including files. Since WordPress does not make use of an autoloader, this advice is a good one for WordPress developers, and especially core contributors. 😉
OK, got that. Is there anything else?
Yes, there is. Other very well-known and frequently used language constructs are echo
and return
. Just like the previous ones, they don’t require any parentheses, so do not use parentheses with echo
and return
, unless you actually have to, or it makes for better readability.
echo foo() . '-' . $bar->baz( $qux );
// Parentheses are required to always end the sentence with a full stop.
echo 'Howdy, ' . ( $person ? $person->name() : 'stranger' ) . '.';
function magic() {
// Parentheses are not required, but I like (formatting) it much better with them.
return (
some_long_function_name( $long_variable )
&& yet_another_function( true, false, false, $long_name_again )
);
}
Summary
Language constructs are odd creatures. Most of them don’t require any parentheses. Having superfluous stuff in your code isn’t (always) good, so maybe have a look at your projects, and get rid of parentheses that neither are required, nor improve readability.
Want a handy regular expression for searching? There you go:
(break|case|clone|continue|echo|include(_once)?|print|require(_once)?|return|throw)\s*\(
One final thing: it’s not correct to not use parentheses with include
just because it is a language construct. As we have seen, there are language constructs that actually require parentheses. The correct reason is simply that include
is one of the language constructs that don’t require parentheses.
News For You?
So, did you know all of this, or did you learn something new?
Are there (still) things unclear? Do you think I left out any important aspects?
And … do you use parentheses with include(_once)
and require(_once)
, or not? 🙂
Please, tell me. Thanks!
Really good article, language constructs are really interesting haha 😀
Thank you from Brazil!