Clarify the syntax and role of arguments for echo and print

- Rather than implying that parentheses are optional, make clear
  that they are never part of the syntax for these keywords.
- Don't spend so many examples on different types of string.
- Do include examples of non-string arguments.
- Add a note with additional examples of how parentheses interact.
- Don't refer to either construct as "function-like", instead use
  the term "expression" consistently.
- Additional tidying to make the pages more consistent with each
  other.
- Add "print" to the list of language constructs which are
  documented as functions.
- Remove mention of what PHP versions below 5.4 did with short
  echo tags.
This commit is contained in:
Rowan Tommins 2021-03-03 15:00:46 +00:00 committed by Christoph M. Becker
parent 77a77dbd29
commit ede9d209f6
3 changed files with 224 additions and 80 deletions

View file

@ -28,6 +28,7 @@
<member><function>array</function></member>
<member><function>echo</function></member>
<member><function>eval</function></member>
<member><function>print</function></member>
</simplelist>
</para>
</sect2>

View file

@ -10,27 +10,23 @@
&reftitle.description;
<methodsynopsis>
<type>void</type><methodname>echo</methodname>
<methodparam><type>string</type><parameter>arg</parameter></methodparam>
<methodparam rep="repeat"><type>string</type><parameter>args</parameter></methodparam>
<methodparam rep="repeat"><type>string</type><parameter>expressions</parameter></methodparam>
</methodsynopsis>
<simpara>
Outputs all parameters. No additional newline is appended.
Outputs one or more expressions, with no additional newlines or spaces.
</simpara>
<para>
<literal>echo</literal> is not actually a function (it is a
language construct), so you are not required to use parentheses
with it. <literal>echo</literal> (unlike some other language
constructs) does not behave like a function, so it cannot
always be used in the context of a function. Additionally, if you want to
pass more than one parameter to <literal>echo</literal>, the parameters
must not be enclosed within parentheses.
<literal>echo</literal> is not a function but a language language construct.
Its arguments are a list of expressions following the <literal>echo</literal>
keyword, separated by commas, and not delimited by parentheses.
Unlike some other language constructs, <literal>echo</literal> does not have
any return value, so it cannot be used in the context of an expression.
</para>
<para>
<literal>echo</literal> also has a shortcut syntax, where you can
immediately follow the opening tag with an equals sign. Prior to PHP 5.4.0,
this short syntax only works with the
<link linkend="ini.short-open-tag">short_open_tag</link> configuration
setting enabled.
immediately follow the opening tag with an equals sign. This syntax is available
even with the <link linkend="ini.short-open-tag">short_open_tag</link> configuration
setting disabled.
<informalexample>
<programlisting role="php">
<![CDATA[
@ -40,8 +36,8 @@ I have <?=$foo?> foo.
</informalexample>
</para>
<para>
The major differences to <literal>print</literal> are that
<literal>echo</literal> accepts an argument list and doesn't have a return value.
The major differences to <function>print</function> are that
<literal>echo</literal> accepts multiple arguments and doesn't have a return value.
</para>
</refsect1>
@ -50,17 +46,13 @@ I have <?=$foo?> foo.
<para>
<variablelist>
<varlistentry>
<term><parameter>arg</parameter></term>
<listitem>
<para>
The parameter to output.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><parameter>args</parameter></term>
<term><parameter>expressions</parameter></term>
<listitem>
<para>
One or more string expressions to output, separated by commas.
Non-string values will be coerced to strings, even when
<link linkend="language.types.declarations.strict">the
<literal>strict_types</literal> directive</link> is enabled.
</para>
</listitem>
</varlistentry>
@ -83,22 +75,45 @@ I have <?=$foo?> foo.
<programlisting role="php">
<![CDATA[
<?php
echo "Hello World";
echo "echo does not require parentheses.";
// Strings can either be passed individually as multiple arguments or
// concatenated together and passed as a single argument
echo 'This ', 'string ', 'was ', 'made ', 'with multiple parameters.', chr(10);
echo 'This ', 'string ', 'was ', 'made ', 'with multiple parameters.', "\n";
echo 'This ' . 'string ' . 'was ' . 'made ' . 'with concatenation.' . "\n";
// Because echo does not behave like a function, the following code is invalid.
// No newline or space is added; the below outputs "helloworld" all on one line
echo "hello";
echo "world";
// Same as above
echo "hello", "world";
echo "This string spans
multiple lines. The newlines will be
output as well";
echo "This string spans\nmultiple lines. The newlines will be\noutput as well.";
// The argument can be any expression which produces a string
$foo = "example";
echo "foo is $foo"; // foo is example
$fruits = ["lemon", "orange", "banana"];
echo implode(" and ", $fruits); // lemon and orange and banana
// Non-string expressions are coerced to string, even if declare(strict_types=1) is used
echo 6 * 7; // 42
// Because echo does not behave as an expression, the following code is invalid.
($some_var) ? echo 'true' : echo 'false';
// However, the following examples will work:
($some_var) ? print 'true' : print 'false'; // print is also a construct, but
// it behaves like a function, so
// it may be used in this context.
// it is a valid expression, returning 1,
// so it may be used in this context.
echo $some_var ? 'true': 'false'; // changing the statement around
echo $some_var ? 'true': 'false'; // evaluating the expression first and passing it to echo
?>
]]>
</programlisting>
@ -109,31 +124,82 @@ echo $some_var ? 'true': 'false'; // changing the statement around
<refsect1 role="notes">
&reftitle.notes;
&note.language-construct;
<note>
<title>Using with parentheses</title>
<para>
Surrounding a single argument to <literal>echo</literal> with parentheses will not
raise a syntax error, and produces syntax which looks like a normal
function call. However, this can be misleading, because the parentheses are actually
part of the expression being output, not part of the <literal>echo</literal>
syntax itself.
</para>
<para>
<example>
<title/>
<programlisting role="php">
<![CDATA[
<?php
echo "hello";
// outputs "hello"
echo("hello");
// also outputs "hello", because ("hello") is a valid expression
echo(1 + 2) * 3;
// outputs "9"; the parentheses cause 1+2 to be evaluated first, then 3*3
// the echo statement sees the whole expression as one argument
echo "hello", " world";
// outputs "hello world"
echo("hello"), (" world");
// outputs "hello world"; the parentheses are part of each expression
echo("hello", " world");
// Throws a Parse Error because ("hello", " world") is not a valid expression
?>
]]>
</programlisting>
</example>
</para>
</note>
<tip>
<para>
A benefit to passing in multiple arguments over using concatenation in
<function>echo</function> regards the precedence of the period operator in
PHP. If multiple arguments are passed in, then parentheses will not be
required to enforce precedence:
Passing multiple arguments to <literal>echo</literal> can avoid
complications arising from the precedence of the concatenation operator in
PHP. For instance, the concatenation operator has higher precedence than
the ternary operator, and prior to PHP 8.0.0 had the same precedence as addition
and subtraction:
</para>
<programlisting role="php">
<![CDATA[
<![CDATA[
<?php
echo "Sum: ", 1 + 2;
echo "Hello ", isset($name) ? $name : "John Doe", "!";
// Below, the expression 'Hello ' . isset($name) is evaluated first,
// and is always true, so the argument to echo is always $name
echo 'Hello ' . isset($name) ? $name : 'John Doe' . '!';
// The intended behaviour requires additional parentheses
echo 'Hello ' . (isset($name) ? $name : 'John Doe') . '!';
// In PHP prior to 8.0.0, the below outputs "2", rather than "Sum: 3"
echo 'Sum: ' . 1 + 2;
// Again, adding parentheses ensures the intended order of evaluation
echo 'Sum: ' . (1 + 2);
]]>
</programlisting>
<para>
With concatenation, the period operator has the same precedence as
the addition operator, and higher precedence than the ternary operator, so parentheses must be used for the
correct behaviour:
If multiple arguments are passed in, then parentheses will not be
required to enforce precedence, because each expression is separate:
</para>
<programlisting role="php">
<![CDATA[
<?php
echo 'Sum: ' . (1 + 2);
echo 'Hello ' . (isset($name) ? $name : 'John Doe') . '!';
echo "Hello ", isset($name) ? $name : "John Doe", "!";
echo "Sum: ", 1 + 2;
]]>
</programlisting>
</tip>
@ -146,7 +212,7 @@ echo 'Hello ' . (isset($name) ? $name : 'John Doe') . '!';
<member><function>print</function></member>
<member><function>printf</function></member>
<member><function>flush</function></member>
<member><link linkend="language.types.string.syntax.heredoc">Heredoc syntax</link></member>
<member><link linkend="language.types.string">Ways to specify literal strings</link></member>
</simplelist>
</para>
</refsect1>

View file

@ -10,19 +10,20 @@
&reftitle.description;
<methodsynopsis>
<type>int</type><methodname>print</methodname>
<methodparam><type>string</type><parameter>arg</parameter></methodparam>
<methodparam><type>string</type><parameter>expression</parameter></methodparam>
</methodsynopsis>
<para>
Outputs <parameter>arg</parameter>.
Outputs <parameter>expression</parameter>.
</para>
<para>
<literal>print</literal> is not actually a real function (it is a
language construct) so you are not required to use parentheses
with its argument list.
<literal>print</literal> is not a function but a language language construct.
Its argument is the expression following the <literal>print</literal> keyword,
and is not delimited by parentheses.
</para>
<para>
The major differences to <literal>echo</literal> are that
<literal>print</literal> only accepts a single argument and always returns 1.
The major differences to <function>echo</function> are that
<literal>print</literal> only accepts a single argument and always returns
<literal>1</literal>.
</para>
</refsect1>
@ -31,10 +32,12 @@
<para>
<variablelist>
<varlistentry>
<term><parameter>arg</parameter></term>
<term><parameter>expression</parameter></term>
<listitem>
<para>
The input data.
The expression to be output. Non-string values will be coerced to strings,
even when <link linkend="language.types.declarations.strict">the
<literal>strict_types</literal> directive</link> is enabled.
</para>
</listitem>
</varlistentry>
@ -57,41 +60,36 @@
<programlisting role="php">
<![CDATA[
<?php
print("Hello World");
print "print does not require parentheses.";
print "print() also works without parentheses.";
// No newline or space is added; the below outputs "helloworld" all on one line
print "hello";
print "world";
print "This spans
print "This string spans
multiple lines. The newlines will be
output as well";
print "This spans\nmultiple lines. The newlines will be\noutput as well.";
print "This string spans\nmultiple lines. The newlines will be\noutput as well.";
print "escaping characters is done \"Like this\".";
// The argument can be any expression which produces a string
$foo = "example";
print "foo is $foo"; // foo is example
// You can use variables inside a print statement
$foo = "foobar";
$bar = "barbaz";
$fruits = ["lemon", "orange", "banana"];
print implode(" and ", $fruits); // lemon and orange and banana
print "foo is $foo"; // foo is foobar
// Non-string expressions are coerced to string, even if declare(strict_types=1) is used
print 6 * 7; // 42
// You can also use arrays
$bar = array("value" => "foo");
// Because print has a return value, it can be used in expressions
// The following outputs "hello world"
if ( print "hello" ) {
echo " world";
}
print "this is {$bar['value']} !"; // this is foo !
// Using single quotes will print the variable name, not the value
print 'foo is $foo'; // foo is $foo
// If you are not using any other characters, you can just print variables
print $foo; // foobar
print <<<END
This uses the "here document" syntax to output
multiple lines with $variable interpolation. Note
that the here document terminator must appear on a
line with just a semicolon no extra whitespace!
END;
// The following outputs "true"
( 1 === 1 ) ? print 'true' : print 'false';
?>
]]>
</programlisting>
@ -101,6 +99,85 @@ END;
<refsect1 role="notes">
&reftitle.notes;
<note>
<title>Using with parentheses</title>
<para>
Surrounding the argument to <literal>print</literal> with parentheses will not
raise a syntax error, and produces syntax which looks like a normal
function call. However, this can be misleading, because the parentheses are actually
part of the expression being output, not part of the <literal>print</literal>
syntax itself.
</para>
<para>
<example>
<title/>
<programlisting role="php">
<![CDATA[
<?php
print "hello";
// outputs "hello"
print("hello");
// also outputs "hello", because ("hello") is a valid expression
print(1 + 2) * 3;
// outputs "9"; the parentheses cause 1+2 to be evaluated first, then 3*3
// the print statement sees the whole expression as one argument
if ( print("hello") && false ) {
print " - inside if";
}
else {
print " - inside if";
}
// outputs "0 is the answer"
// the expression ("not the answer") && false is first evaluated, giving false
// this is coerced to the empty string "" and printed
// the print construct then returns 1, so code in the if block is run
?>
]]>
</programlisting>
</example>
</para>
<para>
When using <literal>print</literal> in a larger expression, placing both the
keyword and its argument in parentheses may be necessary to give the intended
result:
</para>
<para>
<example>
<title/>
<programlisting role="php">
<![CDATA[
<?php
if ( (print "hello") && false ) {
print " - inside if";
}
else {
print " - inside else";
}
// outputs "hello - inside else"
// unlike the previous example, the expression (print "hello") is evaluated first
// after outputting "hello", print returns 1
// since 1 && false is false, code in the else block is run
print "hello " && print "world";
// outputs "world1"; print "world" is evaluated first,
// then the expression "hello " && 1 is passed to the left-hand print
(print "hello ") && (print "world");
// outputs "hello world"; the parentheses force the print expressions
// to be evaluated before the &&
?>
]]>
</programlisting>
</example>
</para>
</note>
&note.language-construct;
</refsect1>
@ -111,7 +188,7 @@ END;
<member><function>echo</function></member>
<member><function>printf</function></member>
<member><function>flush</function></member>
<member><link linkend="language.types.string.syntax.heredoc">Heredoc syntax</link></member>
<member><link linkend="language.types.string">Ways to specify literal strings</link></member>
</simplelist>
</para>
</refsect1>