php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77336 objects with __toString() get destroyed when typehinted and passed by reference
Submitted: 2018-12-21 22:47 UTC Modified: 2019-05-15 10:54 UTC
Votes:1
Avg. Score:1.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: alex dot howansky at gmail dot com Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 7.x OS: Ubuntu 16.04
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: alex dot howansky at gmail dot com
New email:
PHP Version: OS:

 

 [2018-12-21 22:47 UTC] alex dot howansky at gmail dot com
Description:
------------
If an object has a __toString() method and is passed to a function as a parameter that is typehinted to string and passed by reference, then the object gets destroyed and replaced with the string. Without the typehint, the object passed by reference remains intact. Defining a typehint on a function's parameter should not impact the calling code beyond raising a TypeError exception.

Test script:
---------------
function one(&$str) { }

function two(string &$str) { }

class Foo {
    public function __toString() { return ''; }
}

$foo = new Foo();
echo gettype($foo); // object

one($foo);
echo gettype($foo); // object

two($foo);
echo gettype($foo); // string

Expected result:
----------------
The passed object should not be destroyed. The typehint should be used only to verify that the parameter meets a requirement, not to actively change its type.

In addition, if overwriting the object is determined to be the correct behavior, then I feel it should at least raise an E_NOTICE when changing a variable's type.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-12-21 23:03 UTC] nikic@php.net
-PHP Version: 7.3.0 +PHP Version: 7.x
 [2018-12-21 23:03 UTC] nikic@php.net
Type declarations ensure that the type of the parameter is correct on entry to the function, possibly through use of a type coercion. In the case of references, the variable inside the function and outside the function are the same, so the coercion also affects the value outside the function.

I think the only possible alternative behavior here would be to forbid type coercions entirely if the parameter is a reference -- there is no way to only change the type inside the function, while preserving the reference.

In any case, you can opt out of the type coercions by specifying declare(strict_types=1), as is good practice to avoid these kinds of surprises.  (And of course, best combined with not using references...)
 [2018-12-22 10:36 UTC] cmb@php.net
If two() is not supposed to ever modify the passed argument, why
declare the parameter as by-reference, in the first place?  If
two() may modify the passed argument, one cannot assume that $foo
will retain its value, anyway.  Suggested reading:
<http://schlueters.de/blog/archives/125-Do-not-use-PHP-references.html>
 [2019-05-15 10:54 UTC] nikic@php.net
-Status: Open +Status: Not a bug
 [2019-05-15 10:54 UTC] nikic@php.net
Per my above comment, this is working as intended and there's really no other way it can work within weak typing semantics.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 01:01:28 2024 UTC