php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #70278 Allow null to be passed by reference
Submitted: 2015-08-15 15:06 UTC Modified: 2019-03-12 16:48 UTC
From: cyslider at posteo dot de Assigned:
Status: Open Package: *General Issues
PHP Version: Next Major Version OS:
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: cyslider at posteo dot de
New email:
PHP Version: OS:

 

 [2015-08-15 15:06 UTC] cyslider at posteo dot de
Description:
------------
Please allow null - and preferable also any other value - to be directly passed into a function that expects a reference.

If I want to pass 'null' by reference I currently have to do it like this:

function testFunc(Array &$array = null) {
// Do something
}

$null = null;
testFunc($null);

This is cumbersome.

Better would be to also allow:

testFunc(null);

I would suggest some automatical boxing in case a direct value is passed instead of a referenceable variable. That way you could also pass strings directly into a function that expects a reference merely for performance reasons.

function exec (&$sql) {
     //Execute SQL
}

exec("Select 1");

Which currently throws an error.

Greetings,
       CySlider


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-08-15 15:57 UTC] cmb@php.net
-Package: PHP Language Specification +Package: *General Issues
 [2015-08-16 13:19 UTC] rowan dot collins at gmail dot com
Nearly every function that "expects a reference merely for performance reasons" has been written wrong - in many cases, pass by reference actually has worse performance (the compiler is cleverer than people give it credit for). The language shouldn't be making such incorrect usage easier.

There can be cases where you can avoid a copy-on-write using a reference param, but they should be carefully considered, and are unlikely to be in frequently-called code where creating a variable would be burdensome.

Most of the time, if a function is asking for something by reference, it should be because it's expecting to write back to that variable, so passing a non-variable makes no sense. For this case, the current behaviour is entirely correct.

Note that because your first example provides a default parameter value, you can just write testFunc(); anyway if you're not interested in the reference parameter.
 [2015-08-16 14:26 UTC] cyslider at posteo dot de
Thanks for the elaborate reply.

First of all lets not side track. My request was mainly for the null reference. The other request was just a "nice-to-have" addition. 

If I declare a function parameter as "Array &$array = null", however you may judge its use, I expect it to be able to accept a direct "null" value. I don't see your other arguments apply to this.

Anyhow: 

> Nearly every function that "expects a reference merely for 
> performance reasons" has been written wrong - in many 
> cases, pass by reference actually has worse performance 
> (the compiler is cleverer than people give it credit 
> for). The language shouldn't be making such incorrect 
> usage easier.

> There can be cases where you can avoid a copy-on-write 
> using a reference param, but they should be carefully 
> considered, and are unlikely to be in frequently-called 
> code where creating a variable would be burdensome.

Ok, I know compilers have some dark magic tricks up their sleves and are awsome in this, but I don't like to rely on some mechnisms that I do not fully understand. Anyhow, what you claim sounds strange, copying faster than passing by reference? So I made a benchmark.

function testA(Array &$arr) {
	 return count($arr);
}

function testB(Array $arr) {
	 return count($arr);
}

$c = 0;
	
Utils::addTimestamp("Test A: Begin");
for ($i = 0; $i<10000000; $i++) {
        // Huge array defined in 2000 lines of code.
	$c += testA(Globals::$structure); 
}
Utils::addTimestamp("Test A: End");

Utils::addTimestamp("Test B: Begin");
for ($i = 0; $i<10000000; $i++) {
	$c += testB(Globals::$structure);
}
Utils::addTimestamp("Test B: End");

echo "Elements: ".($c/10000000);

Result:

[0.0982170105] +0.0035328865: Test A: Begin
[6.9941301346] +6.8959131241: Test A: End
[6.9941751957] +0.0000450611: Test B: Begin
[9.6694211960] +2.6752460003: Test B: End

So you were not simply right, its amazing 2-3 times faster than not using a reference!! 
But to my understanding this can not so much be an intelligent compiler than a massive screwup in the references implementation...
In one case it needs to copy a 2000 lines Array and in the other it has only to pass a small reference??!?

What am I missing here?


> Most of the time, if a function is asking for something by 
> reference, it should be because it's expecting to write 
> back to that variable, so passing a non-variable makes no 
> sense. For this case, the current behaviour is entirely 
> correct.

I personally think in an object oriented language this should be achieved by passing objects instead of directly passing literals as reference. I know this is a leftover of PHP 4 times, but nowerdays, the only reason for me to use "by reference" on literals should be performance.

> Note that because your first example provides a default 
> parameter value, you can just write testFunc(); anyway if 
> you're not interested in the reference parameter.

I know that. This was just a quick example. In my case the parameter in question is in midst of other parameters that need to be specified. The default parameter is just specified, because else its not allowed to pass 'null' at all.

Beside that, often this functions are declared in includes so I have no control over how the function is defined.

Anyhow, thanks for this yet again unexpected insight about PHP. Guess I have to rethink my strategy reguarding references....  Alot....
 [2015-08-17 17:59 UTC] rowan dot collins at gmail dot com
> If I declare a function parameter as "Array &$array = null", however 
> you may judge its use, I expect it to be able to accept a direct 
> "null" value. I don't see your other arguments apply to this.

The =null does two things in this example: it relaxes the type-checking of the "array" hint to allow nulls, and it allows you to omit the parameter when you call the function (eliminating the need to pass null at all).

There is a case where I can see that it might be useful to pass an explicit null, which is if the function had parameters in an awkward order, e.g. function foo($required, &$optional_output=null, $useful_flags=null) I think this case - a parameter which is by-reference, defaultable, and not at the end of the list - would be relatively rare, though. There has been some discussion of adding a keyword to specify that you want the default value, or a syntax for named parameters, which might fit this use-case better anyway.

Allowing null, but not other literals, for *all* reference parameters seems like it might cause confusion; not allowing literals for reference parameters in general is, I believe, a deliberate design decision.



> In one case it needs to copy a 2000 lines Array and in the other 
> it has only to pass a small reference??!?
> What am I missing here?

You should have a look at an introduction to PHP's Internals, and in particular look up the concept of "copy-on-write".

Because count() doesn't need to make any changes to the array, your testB() function doesn't actually need a new copy of the data - nothing else can change the data, so it can just look at the existing copy. So this is exactly what the Zend Engine in PHP does: it creates a reference to the original data internally, and only if you actually change it does it make a full copy of the data. 

I won't go into the reasons here, but because of how Zend Engine 2 (i.e. PHP 5.x) implements this, passing or assigning by reference sometimes actually defeats this optimisation, forcing the engine to create an unnecessary copy of the data. (Zend Engine 3 / PHP 7.x has a slightly different implementation, I believe.)



> I personally think in an object oriented language this should be 
> achieved by passing objects instead of directly passing literals 
> as reference.

In that case, simply do not use pass-by-reference at all. :)



> the only reason for me to use "by reference" on literals should
> be performance.

As discussed, and as you yourself have seen, this is generally NOT a good reason to use pass-by-reference.



The pass-by-reference semantics are there so you can write things like this:

function populate_data(&$output) {
$output = [1, 2, 'buckle my shoe'];
}

Here populate_data(null) is clearly an error, since it is equivalent to writing this nonsensical code:

null = [1, 2, 'buckle my shoe'];

Obviously there are more borderline cases, but in general the requirement that reference parameters be something you can assign back to is a deliberate and useful one.



> Beside that, often this functions are declared in includes so 
> I have no control over how the function is defined.

This is certainly true, but that doesn't automatically mean that the language should be changed, rather than just the third-party code you are calling into. File a bug with them if you think their function signatures could be more useful - you'll probably get a faster turnaround than waiting for a new stable version of PHP anyway!
 [2019-03-11 18:14 UTC] cyslider at posteo dot de
> I think this case - a parameter which is by-reference, defaultable, and not at the end of the list - would be relatively rare, though.

Well I just again came across this using exec

$null = null
exec($script, $null, $result)

So I would still be glad to be allowed to simply pass null in such cases.
 [2019-03-11 18:19 UTC] spam2 at rhsoft dot net
> $null = null
> exec($script, $null, $result)

there is no reason for the first line to begin with at all

[harry@srv-rhsoft:/downloads]$ php test.php

[harry@srv-rhsoft:/downloads]$ cat test.php
<?php declare(strict_types=1);
exec('hostname', $null, $result);
?>
 [2019-03-12 09:04 UTC] cyslider at posteo dot de
> there is no reason for the first line to begin with at all 

Tell this my IDE that warns me of an undefined variable which is, at least in eclipse, not suppressible.

But far more annoying is that I have to remember this as I always initially pass null and get that error, then have to go back and correct it. 
The extra line is less of an issue to me.

Is it really that hard, to allow null at the point where this error is generated, at least if the default value allows null? Is there a reason not to always allow null, even?


>> Beside that, often this functions are declared in includes so 
>> I have no control over how the function is defined.

>This is certainly true, but that doesn't automatically mean that the language 
>should be changed, rather than just the third-party code you are calling into. 
>File a bug with them if you think their function signatures could be more 
>useful - you'll probably get a faster turnaround than waiting for a new stable 
>version of PHP anyway!

exec is part of PHP core. So consider this advice of rowan collins followed :-)
 [2019-03-12 09:37 UTC] spam2 at rhsoft dot net
yes it is because the default value means "don't store it anywhere" and honestly what sort of argumentation is "i always need to remember it"?

for your IDE:make a bugreport there that they should stop warn about correct usage and start warning about your repeated mistake you never remember
 [2019-03-12 13:48 UTC] cyslider at posteo dot de
All I am saying is that having to pass null by reference to a function that explicitly states to accept null as a value, even as its default, is odd.
 [2019-03-12 14:49 UTC] cmb@php.net
The default value of the $output parameter of exec() is not NULL;
actually, this parameter has no default value.  Either the
argument is omitted, or not.

Anyhow, this feature request is obviously controversial, so it
should be discussed on <internals@lists.php.net>, and might need
the RFC process[1].

[1] <https://wiki.php.net/rfc/howto>
 [2019-03-12 15:37 UTC] cyslider at posteo dot de
That is what my IDE shows me. Also a bug?

function exec ($command, array &$output = null, &$return_var = null) {}

I understand 'array &$output = null' as having null by default. Sorry if this is not meant that way internally. I am not aware of that. I thought this notation always refers to setting a default value in case the value is omitted.
 [2019-03-12 15:59 UTC] spam2 at rhsoft dot net
it is a BY-REFERENCE param so what do you think means a default of NULL?

omit errors, do nothing at all and no it would not be better allow function(null) as call because you technically can't pass anything else then variables as reference

just don't specify that optional param at all if you don't make use of it, don't call the variable $null because it is wrong and to satisfy your buggy IDE write $output = '' instead null and you are done - hell, it's a REFERENCE and so it don't matter what value it has to satisfy your IDE because after the call it has the output of the called function
 [2019-03-12 16:48 UTC] cyslider at posteo dot de
I am really sorry if I did rub you the wrong way, I did not intend to do so.

I can not omit this parameter as I have to pass the parameter coming after it.
I also have no problem getting a warning free solution by using.

$null=null;
exec($script, $null, $result);

I prefer this over just 

exec($script, $null, $result);

as I don't want to use an uninitialized variable this way and would not be surprised if a future PHP version will issue some kind of warning for this.

I merely find it an odd choice to not accept a direct null here and wanted to bring this to the developers attention, that there exists at least one user of their product that would consider this an improvement. But if I am the only one, this certainly is not worth your time and you should just close this as WON'T FIX.

Thank you, for hearing me out.
 [2019-03-12 17:30 UTC] spam2 at rhsoft dot net
> I can not omit this parameter as I have to pass the parameter coming after it

than complain about that https://wiki.php.net/rfc/named_params still don't work in PHP as it did in VB6 20 years ago

> I also have no problem getting a warning free solution by using.
> $null=null;
> exec($script, $null, $result);

hell write $null=''; and file a bugreprot at your IDE which is clearly wrong
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Mon Aug 19 13:01:27 2019 UTC