Rewrite type juggling page (#1542)

Describe all the different ways and contexts in which PHP can type juggle.

The only bit which is not described is how ``++`` and ``--`` behave as they have peculiar behaviour which is documented on the operator page already.
This commit is contained in:
George Peter Banyard 2022-06-06 14:12:31 +01:00 committed by GitHub
parent 38f5bb6cc0
commit 8de1190205
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,65 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<sect1 xml:id="language.types.type-juggling">
<title>Type Juggling</title>
<simpara>
PHP does not require (or support) explicit type definition in variable
declaration; a variable's type is determined by the context in which the
variable is used. That is to say, if a <type>string</type> value is assigned
to variable <varname>$var</varname>, <varname>$var</varname> becomes a
<type>string</type>. If an <type>int</type> value is then assigned to
<varname>$var</varname>, it becomes an <type>int</type>.
PHP does not require explicit type definition in variable declaration.
In this case, the type of a variable is determined by the value it stores.
That is to say, if a <type>string</type> is assigned to variable
<varname>$var</varname>, then <varname>$var</varname> is of type
<type>string</type>. If afterwards an <type>int</type> value is assigned
to <varname>$var</varname>, it will be of type <type>int</type>.
</simpara>
<para>
An example of PHP's automatic type conversion is the multiplication operator '*'.
If either operand is a <type>float</type>, then both operands are evaluated as
<type>float</type>s, and the result will be a <type>float</type>. Otherwise,
the operands will be interpreted as <type>int</type>s, and the result will
also be an <type>int</type>. Note that this does <emphasis>not</emphasis>
change the types of the operands themselves; the only change is in how the
operands are evaluated and what the type of the expression itself is.
PHP may attempt to convert the type of a value to another automatically
in certain contexts. The different contexts which exist are:
<itemizedlist>
<listitem>
<simpara>Numeric</simpara>
</listitem>
<listitem>
<simpara>String</simpara>
</listitem>
<listitem>
<simpara>Logical</simpara>
</listitem>
<listitem>
<simpara>Integral and string</simpara>
</listitem>
<listitem>
<simpara>Comparative</simpara>
</listitem>
<listitem>
<simpara>Function</simpara>
</listitem>
</itemizedlist>
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$foo = "1"; // $foo is string (ASCII 49)
$foo *= 2; // $foo is now an integer (2)
$foo = $foo * 1.3; // $foo is now a float (2.6)
$foo = 5 * "10 Little Piggies"; // $foo is integer (50)
$foo = 5 * "10 Small Pigs"; // $foo is integer (50)
?>
]]>
<!-- bad example, no real operator (must be used with variable, modifies it too)
$foo++; // $foo is the string "2" (ASCII 50)
TODO: explain ++/- - behaviour with strings
examples:
++'001' = '002'
++'abc' = 'abd'
++'xyz' = 'xza'
++'9.9' = '9.0'
++'-3' = '-4'
- -'9' = 8 (integer!)
- -'5.5' = '5.5'
- -'-9' = -10 (integer)
- -'09' = 8 (integer)
- -'abc' = 'abc'
-->
</programlisting>
</informalexample>
<simpara>
If the last two examples above seem odd, see how
<link linkend="language.types.numeric-strings">numeric strings</link>
convert to integers.
</simpara>
<note>
<simpara>
When a value needs to be interpreted as a different type, the value itself
does <emphasis>not</emphasis> change types.
</simpara>
</note>
<simpara>
To force a variable to be evaluated as a certain type, see the section on
@ -67,119 +49,222 @@ examples:
type of a variable, see the <function>settype</function> function.
</simpara>
<para>
To test any of the examples in this section, use the
<function>var_dump</function> function.
</para>
<sect2>
<title>Numeric contexts</title>
<note>
<para>
The behaviour of an automatic conversion to <type>array</type> is currently
undefined.
</para>
<simpara>
This is the context when using an
<link linkend="language.operators.arithmetic">arithmetical operator</link>.
</simpara>
<simpara>
In this context if either operand is a <type>float</type> (or not
interpretable as an <type>int</type>), both operands are interpreted as
<type>float</type>s, and the result will be a <type>float</type>.
Otherwise, the operands will be interpreted as <type>int</type>s,
and the result will also be an <type>int</type>.
As of PHP 8.0.0, if one of the operands cannot be interpreted a
<classname>TypeError</classname> is thrown.
</simpara>
</sect2>
<sect2>
<title>String contexts</title>
<simpara>
This is the context when using <function>echo</function>,
<function>print</function>, string interpolation, or the string
<link linkend="language.operators.string">concatenation operator</link>.
</simpara>
<simpara>
In this context the value will be interpreted as <type>string</type>.
</simpara>
</sect2>
<sect2>
<title>Logical contexts</title>
<simpara>
This is the context when using conditional statements, the
<link linkend="language.operators.comparison.ternary">ternary operator</link>,
or a <link linkend="language.operators.logical">logical operator</link>.
</simpara>
<simpara>
In this context the value will be interpreted as <type>bool</type>.
</simpara>
</sect2>
<sect2>
<title>Integral and string contexts</title>
<simpara>
This is the context when using a
<link linkend="language.operators.bitwise">bitwise operators</link>.
</simpara>
<simpara>
In this context if all operands are of type <type>string</type> the result
will also be a <type>string</type>.
Otherwise, the operands will be interpreted as <type>int</type>s,
and the result will also be an <type>int</type>.
As of PHP 8.0.0, if one of the operands cannot be interpreted a
<classname>TypeError</classname> is thrown.
</simpara>
</sect2>
<sect2>
<title>Comparative contexts</title>
<simpara>
This is the context when using a
<link linkend="language.operators.comparison">comparison operator</link>.
</simpara>
<simpara>
The type conversions which occur in this context are explained in the
Comparison with Various Types
<link linkend="language.operators.comparison.types">table</link>.
</simpara>
</sect2>
<sect2>
<title>Function contexts</title>
<simpara>
This is the context when a value is passed to a typed parameter, property,
or returned from a function which declares a return type.
</simpara>
<para>
Also, because PHP supports indexing into <type>string</type>s via offsets
using the same syntax as <type>array</type> indexing, the following example
holds true for all PHP versions:
In this context, when coercive typing mode is active (the default),
only scalar values may be converted to another scalar value.
For simple types declarations the behaviour is as follows:
<itemizedlist>
<listitem>
<simpara>
<type>bool</type> type declaration: value is interpreted as <type>bool</type>.
</simpara>
<simpara>
<type>int</type> type declaration: value is interpreted as <type>int</type>
if conversion is well-defined. I.e. the string is
<link linkend="language.types.numeric-strings">numeric</link>.
</simpara>
<simpara>
<type>float</type> type declaration: value is interpreted as <type>float</type>
if conversion is well-defined. I.e. the string is
<link linkend="language.types.numeric-strings">numeric</link>.
</simpara>
<simpara>
<type>string</type> type declaration: value is interpreted as <type>string</type>.
</simpara>
</listitem>
</itemizedlist>
If the type declaration is a union, see the section about
<link linkend="language.types.declarations.union.coercive">Coercive typing with union types</link>.
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$a = 'car'; // $a is a string
$a[0] = 'b'; // $a is still a string
echo $a; // bar
?>
]]>
</programlisting>
</informalexample>
<para>
See the section titled <link linkend="language.types.string.substr">String
access by character</link> for more information.
</para>
</note>
<warning>
<simpara>
<!-- TODO add link to internal function definition if we have? -->
Internal functions automatically coerce &null; to scalar types,
this behaviour is <emphasis>DEPRECATED</emphasis> as of PHP 8.1.0.
</simpara>
</warning>
</sect2>
<sect2 xml:id="language.types.typecasting">
<title>Type Casting</title>
<para>
Type casting in PHP works much as it does in C: the name of the desired type
is written in parentheses before the variable which is to be cast.
</para>
<simpara>
Type casting converts the value to a chosen type by writing the type within
parentheses before the value to convert.
</simpara>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$foo = 10; // $foo is an integer
$bar = (boolean) $foo; // $bar is a boolean
$bar = (bool) $foo; // $bar is a boolean
?>
]]>
</programlisting>
</informalexample>
<para>
<simpara>
The casts allowed are:
</para>
</simpara>
<itemizedlist>
<listitem>
<simpara>(int), (integer) - cast to <type>int</type></simpara>
</listitem>
<listitem>
<simpara>(bool), (boolean) - cast to <type>bool</type></simpara>
</listitem>
<listitem>
<simpara>(float), (double), (real) - cast to <type>float</type></simpara>
</listitem>
<listitem>
<simpara>(string) - cast to <type>string</type></simpara>
</listitem>
<listitem>
<simpara>(array) - cast to <type>array</type></simpara>
</listitem>
<listitem>
<simpara>(object) - cast to <type>object</type></simpara>
</listitem>
<listitem>
<simpara>(unset) - cast to <type>NULL</type></simpara>
</listitem>
</itemizedlist>
<simplelist>
<member><literal>(int)</literal> - cast to <type>int</type></member>
<member><literal>(bool)</literal> - cast to <type>bool</type></member>
<member><literal>(float)</literal> - cast to <type>float</type></member>
<member><literal>(string)</literal> - cast to <type>string</type></member>
<member><literal>(array)</literal> - cast to <type>array</type></member>
<member><literal>(object)</literal> - cast to <type>object</type></member>
<member><literal>(unset)</literal> - cast to <type>NULL</type></member>
</simplelist>
<para>
(binary) casting and b prefix exists for forward support. Note that
the (binary) cast is essential the same as (string), but it should not be relied
upon.
</para>
<note>
<para>
<literal>(integer)</literal> is an alias of the <literal>(int)</literal> cast.
<literal>(boolean)</literal> is an alias of the <literal>(bool)</literal> cast.
<literal>(binary)</literal> is an alias of the <literal>(string)</literal> cast.
<literal>(double)</literal> and <literal>(real)</literal> are aliases of
the <literal>(float)</literal> cast.
These casts do not use the canonical type name and are not recommended.
</para>
</note>
<para>
The (unset) cast has been deprecated as of PHP 7.2.0. Note that the (unset) cast
is the same as assigning the value <type>NULL</type> to the variable or call. The
(unset) cast is removed as of PHP 8.0.0.
</para>
<warning>
<simpara>
The <literal>(real)</literal> cast alias has been deprecated as of PHP 8.0.0.
</simpara>
</warning>
<para>
Note that tabs and spaces are allowed inside the parentheses, so the
following are functionally equivalent:
</para>
<informalexample>
<programlisting role="php">
<warning>
<simpara>
The <literal>(unset)</literal> cast has been deprecated as of PHP 7.2.0.
Note that the <literal>(unset)</literal> cast is the same as assigning the
value <type>NULL</type> to the variable or call.
The <literal>(unset)</literal> cast is removed as of PHP 8.0.0.
</simpara>
</warning>
<caution>
<simpara>
The <literal>(binary)</literal> cast and <literal>b</literal> prefix exists
for forward support. Currently <literal>(binary)</literal> and
<literal>(string)</literal> are identical, however this may change and
should not be relied upon.
</simpara>
</caution>
<note>
<para>
Whitespaces are ignored within the parentheses of a cast.
Therefore, the following are two casts are equivalent:
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$foo = (int) $bar;
$foo = ( int ) $bar;
?>
]]>
</programlisting>
</programlisting>
</informalexample>
</para>
</note>
<para>
<informalexample>
<simpara>
Casting literal <type>string</type>s and variables to binary
<type>string</type>s:
</para>
</simpara>
<programlisting role="php">
<![CDATA[
@ -191,11 +276,12 @@ $binary = b"binary string";
</programlisting>
</informalexample>
<!-- TODO Remove or move into string context section? -->
<note>
<para>
<simpara>
Instead of casting a variable to a <type>string</type>, it is also possible
to enclose the variable in double quotes.
</para>
</simpara>
<informalexample>
<programlisting role="php">
@ -218,60 +304,56 @@ if ($fst === $str) {
<para>
It may not be obvious exactly what will happen when casting between certain
types. For more information, see these sections:
<simplelist>
<member><link linkend="language.types.boolean.casting">Converting to boolean</link></member>
<member><link linkend="language.types.integer.casting">Converting to integer</link></member>
<member><link linkend="language.types.float.casting">Converting to float</link></member>
<member><link linkend="language.types.string.casting">Converting to string</link></member>
<member><link linkend="language.types.array.casting">Converting to array</link></member>
<member><link linkend="language.types.object.casting">Converting to object</link></member>
<member><link linkend="language.types.resource.casting">Converting to resource</link></member>
<member><link linkend="language.types.null.casting">Converting to NULL</link></member>
<member><link linkend="types.comparisons">The type comparison tables</link></member>
</simplelist>
</para>
<itemizedlist>
<listitem>
<simpara>
<link linkend="language.types.boolean.casting">Converting to boolean</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.integer.casting">Converting to integer</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.float.casting">Converting to float</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.string.casting">Converting to string</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.array.casting">Converting to array</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.object.casting">Converting to object</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.resource.casting">Converting to
resource</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="language.types.null.casting">Converting to NULL</link>
</simpara>
</listitem>
<listitem>
<simpara>
<link linkend="types.comparisons">The type comparison tables</link>
</simpara>
</listitem>
</itemizedlist>
</sect2>
<simplesect>
<!-- when can this happen? TODO Remove? -->
<note>
<simpara>
The behaviour of an automatic conversion to <type>array</type> is currently
undefined.
</simpara>
</note>
<note>
<simpara>
Also, because PHP supports indexing into <type>string</type>s via offsets
using the same syntax as <type>array</type> indexing, the following example
holds true for all PHP versions:
</simpara>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$a = 'car'; // $a is a string
$a[0] = 'b'; // $a is still a string
echo $a; // bar
?>
]]>
</programlisting>
</informalexample>
<simpara>
See the section titled <link linkend="language.types.string.substr">String
access by character</link> for more information.
</simpara>
</note>
</simplesect>
</sect1>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml