Type casting while array destructuring

  109270
March 24, 2020 13:32 enno.woortmann@web.de (Enno Woortmann)
Hi,

currently when using array destructuring the variables are assigned as
they are. For example we split a string with explode all variables will
contain strings:

$data = "foo:*:1023:1000::/home/foo:/bin/sh";
[$user, $pass, $uid, $gid, $gecos, $home, $shell] = explode(":", $data);

If we want to write functions consuming $uid and $gid as integer values
with strict types enabled we need to cast the values afterwards:

$uid = (int) $uid;
$gid = (int) $gid;
// or during the function call
$myConsumingObject->myConsumingFunction((int) $uid, (int) $gid);

I think you get my point. How about adding some syntactic sugar and
allow type casting inside the detructuring expression?

$data = "foo:*:1023:1000::/home/foo:/bin/sh";
[$user, $pass, (int) $uid, (int) $gid, $gecos, $home, $shell] =
explode(":", $data);
// $uid and $gid are integer values now. All other variables remain as
they are and contain strings

An example with associative arrays in loops:

$array = [
     [
         'name' => 'a',
         'id' => '1'
     ],
     [
         'name' => 'b',
         'id' => '2'
     ],
];

foreach ($array as ['id' => (int) $id, 'name' => $name]) {
     // $id contains integer values
}

Further thoughts: when using the list() reference assignment implemented
in PHP7.3 the referenced value could be casted (something to discuss
about, maybe as future scope as casting a reference assignment currently
isn't supported):

$array = [1, 2];
[(string) $a, (string) &$b] = $array;
// $a would be a string: '1'
// $b would be a string: '2'
// $array would contain one integer and one string element: [1, '2']

Thoughts?
  109273
March 24, 2020 14:23 rowan.collins@gmail.com (Rowan Tommins)
On 24 March 2020 13:32:35 GMT+00:00, Enno Woortmann woortmann@web.de> wrote:
> How about adding some syntactic sugar and >allow type casting inside the detructuring expression? > >$data = "foo:*:1023:1000::/home/foo:/bin/sh"; >[$user, $pass, (int) $uid, (int) $gid, $gecos, $home, $shell] = >explode(":", $data);
On the one hand, this seems fairly straightforward, and although casts wouldn't normally appear on the left side of an expression, the intent is pretty clear. On the other hand, this is exactly the kind of thing where strict_types=1 makes things worse - you'll actually get *better* error output if you use strict_types=0 and pass the string to a function marked as requiring int. By forcing a cast, you're actually silencing the error, and turning all unrecognised values to 0. If the desire is for stricter type handling, we probably need stricter casts, perhaps using different syntax like (int!) rather than just more places to put the existing ones. Regards, -- Rowan Tommins [IMSoP]
  109274
March 24, 2020 14:28 h.reindl@thelounge.net (Reindl Harald)
Am 24.03.20 um 15:23 schrieb Rowan Tommins:
> On the other hand, this is exactly the kind of thing where strict_types=1 makes things worse - you'll actually get *better* error output if you use strict_types=0 and pass the string to a function marked as requiring int no!
with strict_types=0 the casting simp,y happens by the caller and you get no error at all
  109275
March 24, 2020 14:46 rowan.collins@gmail.com (Rowan Tommins)
On Tue, 24 Mar 2020 at 14:28, Reindl Harald reindl@thelounge.net> wrote:

> > Am 24.03.20 um 15:23 schrieb Rowan Tommins: > > On the other hand, this is exactly the kind of thing where > strict_types=1 makes things worse - you'll actually get *better* error > output if you use strict_types=0 and pass the string to a function marked > as requiring int > no! > > with strict_types=0 the casting simp,y happens by the caller and you get > no error at all >
Only if the string is a valid integer; compare https://3v4l.org/uvPYZ with https://3v4l.org/h1aT5 function foo(int $x) { var_dump($x); } declare(strict_types=1); $a = 'hello'; $a = (int)$a; // cast doesn't produce any errors foo($a); // dumps int(0) declare(strict_types=0); $a = 'hello'; foo($a); // TypeError: Argument 1 passed to foo() must be of the type int, string given That's what I mean about "stricter casting" - (int)$a basically always succeeds, so it would be useful to have a version that rejects things like non-integer strings. That could be Yet Another Runtime Mode using declare(), but it could just be a different syntax, so that you'd write this: // regardless of which strict_types mode you're in $a = 'hello'; $a = (int!)$a; // TypeError: string value is not valid for strict cast to type int foo($a); // statement never reached Regards, -- Rowan Tommins [IMSoP]