php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76749 call_user_func / invoke break strict typing
Submitted: 2018-08-15 21:59 UTC Modified: 2018-08-16 09:28 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: j dot vanbeele at gmail dot com Assigned:
Status: Duplicate Package: *General Issues
PHP Version: 7.0.31 OS: Any
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: j dot vanbeele at gmail dot com
New email:
PHP Version: OS:

 

 [2018-08-15 21:59 UTC] j dot vanbeele at gmail dot com
Description:
------------
See below.

Test script:
---------------
<?php declare(strict_types=1);

namespace poop; // Commenting this fixes strict typing

// A.php
class A {
	public function MyFunction(string $str) {
		return $str;
	}

}

// B.php
class B {
	public function __call($func, $args) {
		// Uncommenting this breaks strict typing given namespace
		// return call_user_func_array(array(new A, $func), $args);

		// Uncommenting this breaks strict typing given namespace
		// $ref = new \ReflectionMethod('poop\A', 'MyFunction');
		// return $ref->invokeArgs(new A, $args);
		
		// Doesn't break strict typing
		return (new A)->$func($args);

	}

}

// Script.php
var_dump((new B)->MyFunction(1));


Expected result:
----------------
The expected result is that none of the cases above should break strict typing.

As a side note, if theres classes were stored in the following way:

A.php
B.php
Script.php

then the declare(strict_types=1) should be done in B.php over Script.php since due to __call, B is technically the caller, and not Script.php. 

This is already the case with (new A)->$func($args) and hopefully that behaviour will be reproduced given this bug will be fixed.

P.S - Is there any way I could pass multiple arguments to (new A)->$func() for the timebeing?

Actual result:
--------------
Using invoke or call_user_func will make the code resort back to weak typing unless the namespace is commented out.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-08-16 04:38 UTC] requinix@php.net
-Status: Open +Status: Feedback
 [2018-08-16 04:38 UTC] requinix@php.net
Not sure what you mean by it "fixes" or "breaks" strict typing.

If I keep the namespace, fix B.php to use "new poop\A", and add require()s to Script.php, then I get
> Fatal error: Uncaught TypeError: Argument 1 passed to poop\A::MyFunction() must be of the type string, array
> given, called in ...\B.php on line 14 and defined in ...\A.php:7
which, by the way, is not an issue of strict typing but of the fact that PHP can't coerce an array to a string.

And with call_user_func_array and invokeArgs, the calls work and value is a string.

So that all looks correct.


And yes, you can use argument unpacking:
  return (new A)->$func(...$args);
http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
 [2018-08-16 08:56 UTC] j dot vanbeele at gmail dot com
Sorry, allow me to clarify the issue:

In the code #1, 'A' is already in the `namespace` 'poop' and I'm expecting a `TypeError` because I'm passing an `integer` to a `string` with `strict_types=1`. Instead, it coerces the `integer` to a `string` which is weak typing. #1: http://sandbox.onlinephpfunctions.com/code/b5c058883c5edc4f9d9cf6e48fe6a02dd68b5428

When I change `call_user_func_array` to `invoke`, I'm also expecting a `TypeError` because I'm passing an `integer` to a `string` with `strict_types=1`. Instead, it coerces the `integer` to a `string` which is weak typing. #2:
http://sandbox.onlinephpfunctions.com/code/9e9dfe97441f99523ce29875afb5febf4688ab2b

The only time I'm getting what I expect - a TypeError - is when I use `variable functions`. #3:
http://sandbox.onlinephpfunctions.com/code/628d561c767f88ebac6da6660426872457258bcf

Or, when I remove the `namespace`. #4:
http://sandbox.onlinephpfunctions.com/code/ba91f91aa360ec23d35dcc9e1b6bb7deb23b4226
 [2018-08-16 09:27 UTC] requinix@php.net
-Status: Feedback +Status: Duplicate
 [2018-08-16 09:27 UTC] requinix@php.net
Ah, I see.

Take a look at bug #72248.
Also check out https://3v4l.org/q3qk5 to see the difference between the regular/PHP 5.6 and the optimized calls.

The correct behavior is that call_user_func/_array and ReflectionFunction::invoke/Args perform the function calls by themselves (regardless of optimization), like the current behavior without a namespace. Since PHP does not use strict_types for its own calls, yes: the arguments will be coerced without warnings. And no, there is no way to turn it on internally.

What should you do to avoid this problem?

With argument unpacking and PHP 7's syntax improvements, there's not much of a reason to use call_user_func/_array anymore. You can write the function calls inline.
As for reflection... well, it's expensive so you really should be avoiding this if at all possible anyways.
 [2018-08-16 09:28 UTC] requinix@php.net
> like the current behavior without a namespace
*with a namespace
 [2018-08-16 10:59 UTC] j dot vanbeele at gmail dot com
I kind of expected that to be the case but wasn't sure if that was intended behaviour. Also, using argument unpacking that way is awesome. I'll probably avoid given functions for given reasons ie performance.

Thank you so much for your time.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 14:01:29 2024 UTC