Type the List: A Proposal to support type-casting in PHP’s list language construct

I wrote recently about some changes that I’d like to propose for list() in core PHP for version 8.3: namely that it should work with iterables as well with arrays, and that it should allow a variadic “argument” that would assign all entries that were part of the original array being listed, but that weren’t explicitly assigned in the list; and another proposal to support default values list assignments when an element was missing from the array.

Those weren’t the only changes that I would like to see implemented for list().

Another situation that I find with list is if I want to type those variables. There are a number of PHP functions that I use regularly that return an array of string values such as preg_match() and the resultant $matches, and I can assign those matches directly to variables using list().

Using an example from PhpSpreadsheet, I use a regular expression to split a cell reference into a column id and a row number; and I can use list() to assign the entries from the returned $matches argument to individual named variables:

define('A1_COORDINATE_REGEX', '/^(\$?)([A-Z]{1,3})(\$?)(\d{1,7})$/i');

$cellAddress = 'A$1';

preg_match(A1_COORDINATE_REGEX, $cellAddress, $matches);
[, $absoluteColumn, $columnId, $absoluteRow, $row] = $matches;

var_dump($matches, $absoluteColumn, $columnId, $absoluteRow, $row);

This works well enough; but $absoluteColumn and $absoluteRow should really be boolean values (if the value is a $ symbol rather than an empty string), and $row should be an integer, so I need to explicitly cast them as such because preg_match() returns everything as a string.

define('A1_COORDINATE_REGEX', '/^(\$?)([A-Z]{1,3})(\$?)(\d{1,7})$/i');

$cellAddress = '$IV$256';

preg_match(A1_COORDINATE_REGEX, $cellAddress, $matches);
[, $absoluteColumn, $columnId, $absoluteRow, $row] = $matches;

$absoluteColumn = $absoluteColumn === "\$";
$absoluteRow = $absoluteRow === "\$";
$row = (int) $row;

or I can apply basic PHP type coercion for the two boolean values:

$absoluteColumn = (bool) $absoluteColumn;
$absoluteRow = (bool) $absoluteRow;

rather than the strict comparison with the $ character, because I know the regular expression will only ever return an empty string (which will cast to false) or a string containing a $ symbol (which will cast to true), and that’s the only test that I really need to make to determine if it’s absolute or not.

So how can we reduce the work of typing our array values?

I’ve used an arrow function in my previous posts as a way of implementing a variadic for a list, and for setting default values for elements that don’t exist in the array; and I can apply the same principle for type-casting:

define('A1_COORDINATE_REGEX', '/^(\$?)([A-Z]{1,3})(\$?)(\d{1,7})$/i');

$cellAddress = 'XFD256';

preg_match(A1_COORDINATE_REGEX, $cellAddress, $matches);

$splitData = fn($fullMatch, bool $absoluteColumn, $columnId, bool $absoluteRow, int $row) => [$absoluteColumn, $columnId, $absoluteRow, $row];

[$absoluteColumn, $columnId, $absoluteRow, $row] = $splitData(...$matches);

var_dump($matches, $absoluteColumn, $columnId, $absoluteRow, $row);

But the limitations are still the same as when I applied this approach to adding a variadic argument, and providing defaults for when an array element didn’t exist. Methods can only work with the order of arguments for enumerated arrays, or named arguments that match the associative keys in our array; so it will create issues if we have any name/key mismatches, or if an enumerated array isn’t in key order, or if there are gaps in the enumeration.

But wouldn’t it be great if list() could do that type coercion for us, and save us from all those extra lines of code? Or that extra complexity in our code?

Something like:

[1 => (bool) $absoluteColumn, 2 => $columnId, 3 => (bool) $absoluteRow, 4 => (int) $row] = $matches;

Where no type-hint is set, then the extracted value will retain its datatype, otherwise it will be coerced to the defined datatype using standard PHP type coercion rules. Only scalar types would be permitted, and two of the four compound datatypes (boolean, int, float and string, array or object); no union types or intersection types; and new types for PHP 8.2 like true, false and null would not be permitted.

Unlike type-hinting in function/method definitions, strict mode wouldn’t apply, and there’s no option for nullable; this is more akin to type-casting than type-hinting.

As with my previous proposals for changes to list(): with just three weeks to go until feature freeze, it’s too late to put forward any new proposals for PHP 8.2; but once 8.2 has reached a GA (General Availability) release, then I shall be submitting an RFC (Requests for Comment) to internals for this proposed change to be targeted for the PHP 8.3 release.

This entry was posted in PHP and tagged , , , , . Bookmark the permalink.

2 Responses to Type the List: A Proposal to support type-casting in PHP’s list language construct

  1. Pingback: List-o-mania | Mark Baker's Blog

  2. Pingback: Splat the List: A Proposal to support Variadics in PHP’s list language construct | Mark Baker's Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s