Hi all,
Following a long and fairly embarrassing exchange of views with one of my
php|architect authors, I finally discovered that
allow_call_time_pass_reference is still on by default in PHP_5_3 (d'oh).
This makes no sense to me because:
- it throws an
E_DEPRECATED
warning, which nobody will see during
development because - it's a PHP_INI_SYSTEM|PHP_INI_PERDIR setting that is switched on by
default and - on production sites, where it should be switched off,
E_DEPRECATED
is
fairly likely to go unreported - we've been advising people to switch it off for over 8 years
Does anyone have a good reason for keeping it switched on by default in PHP
5.3? Like, would switching it off by default break a lot of existing code,
given that most users are a bit beyond PHP 3 now?
If not, there's a trivial patch attached for main.c and both php.ini-*
files.
As an aside, by-ref is actually faster than by-value under Windows in my
tests, for small amounts of data. That was what you might call an unexpected
result - it definitely isn't the case under *nix, or with very large arrays.
- Steph
...
Does anyone have a good reason for keeping it switched on by default in PHP
5.3? Like, would switching it off by default break a lot of existing code,
given that most users are a bit beyond PHP 3 now?
Well, I can at least comment on how it is used in the code that
I inherited. First it must be noted that the following throws
a fatal error:
function f(&$x=5) { ....
There are functions in our application that will use information
if it is available and update the information if it is provided,
but don't absolutely require such information in the first place.
I don't know of another clean way to do that besides call-time
pass-by-reference. Here is a useless example of the functionality.
(This is an example, and only an example. In the event of real
code, something useful would be accomplished. ;) )
<?php
// $x is sometimes received by reference
function f($x=5) {
if ($x < 10) {
print "blue\n";
$x += 10;
$retval = true;
} else {
print "green\n";
$x += 20;
$retval = false;
}
return $retval;
}
f();
f(10); // In this case info is provided, but the update is discarded
$y=5;
f(&$y);
f(&$y);
?>
Without call-time pass-by-reference, that becomes:
function g(&$x) {
if ($x === null) {
$x = 5;
}
if ($x < 10) {
print "blue\n";
$x += 10;
$retval = true;
} else {
print "green\n";
$x += 20;
$retval = false;
}
return $retval;
}
$y = null;
g($y);
$y = 10;
g($y);
$y=5;
g($y);
g($y);
So, without call-time pass-by-reference, "if" blocks would be
needed to set defaults and temp variables would be needed
anywhere an rvalue is used as an argument. It's true that
the former has more opportunities for design error and without
the comment about $x being received by reference there could
be confusion, but going back and changing all that code is not
desirable. "Easier to read" is always a tricky argument, but
I'm sure many would find the first code above easier to read.
- Todd
Hi!
I think we also had a proposal back then to have func_gets_arg[s]
ability to accept params by reference, and it should help with some
scenarios.
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
Hi Stas,
I think we also had a proposal back then to have func_gets_arg[s]
ability to accept params by reference, and it should help with some
scenarios.
Sorry, I failed to parse that. What would help with some scenarios?
- Steph
Hi Todd,
Well, I can at least comment on how it is used in the code that
I inherited.
Thanks for that.
Just to be very clear, I'm not talking about removing it (even though it's
been marked deprecated for 8 years plus). I'm just talking about switching
it off by default. It's been turned off by default in php.ini-recommended
since PHP 4.0, but never in php.ini-dist or in the source.
In PHP 5.3 we have better support for PHP_INI_DIR, so it'd probably make
better sense to do it now than before.
- Steph
...
Does anyone have a good reason for keeping it switched on by default in PHP
5.3? Like, would switching it off by default break a lot of existing code,
given that most users are a bit beyond PHP 3 now?Well, I can at least comment on how it is used in the code that
I inherited. First it must be noted that the following throws
a fatal error:function f(&$x=5) { ....
No it doesn't.
That line does however not work exactly as you would expect..
-Hannes
...
Does anyone have a good reason for keeping it switched on by default in PHP
5.3? Like, would switching it off by default break a lot of existing code,
given that most users are a bit beyond PHP 3 now?Well, I can at least comment on how it is used in the code that
I inherited. First it must be noted that the following throws
a fatal error:function f(&$x=5) { ....
No it doesn't.
That line does however not work exactly as you would expect..
Quite right. I should have said "leads to fatal errors".
It doesn't throw a fatal error on it's own, but if you have old
code that you must maintain in which a function is sometimes called
as f(), sometimes called as f(10), and sometimes called as f(&$y),
trying to "fix it" with the declaration above will lead to fatal
errors for the f(10) calls. (The defaulting part isn't the
problem for that case; it's using a non-variable when a reference
is needed that leads to the fatal error.)
BTW, in php4, there was an incentive to use call-time
pass-by-reference. Calling f(g()) was a very bad thing if
f always accepted its parameter by reference. There are a number
of other similar cases. In php5 (and the last few releases of
php4), it's still bad, but at least memory doesn't get
corrupted now. In contrast, not declaring f to accept its
parameter by reference and instead using call-time
pass-by-reference was (and is) safe (though deprecated).
- Todd
Does anyone have a good reason for keeping it switched on by default in PHP
5.3? Like, would switching it off by default break a lot of existing code,
given that most users are a bit beyond PHP 3 now?
I've never understood the warning that switch throws:
bjori@lindsay:~$ php -dallow_call_time_pass_reference=0 -r 'strpos(&$var, 0);'
Warning: Call-time pass-by-reference has been deprecated; If you
would like to pass it by reference, modify the declaration of
strpos()
. If you would like to enable call-time pass-by-reference,
you can set allow_call_time_pass_reference to true in your INI file.
in Command line code on line 1
However, turning it on will not throw any warning or deprecated notice, wtf?
I'd say throw the warning if the switch is on and someone does
call-time-pass-by-reference and turn that switch off by default.
-Hannes
Hannes,
I'd say throw the warning if the switch is on and someone does
call-time-pass-by-reference and turn that switch off by default.
That's exactly the situation we have now - a warning when the INI setting is
changed, for code that by default runs silently.
And we offer a recommendation to throw that warning in production. That's
the real WTF!
If call-time pass-by-ref is something that needs to be warned about, warn by
default so everyone will get to see it during development. If it isn't
something that needs to be warned about, why throw a warning at all?
- Steph
-Hannes
Folks,
I'd say throw the warning if the switch is on and someone does
call-time-pass-by-reference and turn that switch off by default.That's exactly the situation we have now - a warning when the INI setting
is changed, for code that by default runs silently.And we offer a recommendation to throw that warning in production. That's
the real WTF!If call-time pass-by-ref is something that needs to be warned about, warn
by default so everyone will get to see it during development. If it isn't
something that needs to be warned about, why throw a warning at all?
Did I hit the wrong season with this? Could someone with karma and history
please respond?
On investigation, it seems allow_call_time_pass_reference shouldn't be
E_DEPRECATED
but E_STRICT, assuming it should throw a warning at all. It
hasn't been removed in CVS HEAD, there are occasional genuine uses for it,
and going by Stas' oblique message earlier in the week there may well be
future genuine uses for it too, even if Derick mutters sth off-list about
the spawn of Satan.
Off by default, off in php.ini-dist, possibly on in php.ini-recommended for
BC reasons?
It's still the wrong way around.
- Steph
Steph Fox wrote:
Hi all,
Does anyone have a good reason for keeping it switched on by default in
PHP 5.3? Like, would switching it off by default break a lot of existing
code, given that most users are a bit beyond PHP 3 now?
As a new PHP extension author, it was one the first things I had to
make sure was enabled to avoid the warning. The extension has over 150
server API functions exposed and a majority have a C/C++ prototype of:
BOOL func(input, output);
So our C/C++ to converter make them pass back structures as arrays, for
example:
boolean wcLookupName(string name, TUserInfo &uinfo);
But because of this issue, we are slowing making functions return
results as array:
TUserInfo wcLookupName(string name);
In many cases, it makes sense to have both, but I also think the former
allows for easier programming:
if (!wcLookup("hector", &$uinfo)) {
die("error");
}
as opposed to:
if (($uinfo = wcLookup("hector") === null) {
die("error");
}
Six in one....
May I suggest to make it [Extension] INI ready?
I explored this and it seem to work, but not sure if its really the
case. It appears that adding it in my PHP.INI [extension_name] section
appears to enabled the option only when the extension is loaded.
--
Hector
Hi Hector,
<huge snip />As a new PHP extension author, it was one the first things I had to make
sure was enabled to avoid the warning.
May I suggest to make it [Extension] INI ready?
I explored this and it seem to work, but not sure if its really the case.
It appears that adding it in my PHP.INI [extension_name] section appears
to enabled the option only when the extension is loaded.
The more I look into this, the weirder it seems.
"The first issue that we raised was changing the E_NOTICE
error for
call-time-pass-by-reference to an E_ERROR, or simply throwing a parse error.
We argued over this case and we decided to change this E_NOTICE
to an
E_STRICT
instead as it was argued that there is nothing wrong with doing a
call-time pass by reference."
http://php.net/~derick/meeting-notes.html#deprecated-behaviour
There's nothing wrong with it?
So why throw an error at all?
I have to say, I'm really confused over this. Either it's wrong, so you
deliver a warning, or it's OK, so you don't. But what really bothers me is
that the community seems to be under the impression that the rules changed
with PHP 5, which is absolutely untrue. This threw an E_NOTICE
under PHP 4.0
which has been shifted, first to E_STRICT
(I haven't checked this) and then
to E_DEPRECATED
in 5.3.
All I know is it's slower in most (not all) situations. So the real question
is: should there be a warning at all? And if so, shouldn't it be there by
default?
- Steph
Steph Fox wrote:
The more I look into this, the weirder it seems.
[snip]
There's nothing wrong with it?
So why throw an error at all?
I have to say, I'm really confused over this. Either it's wrong, so you
deliver a warning, or it's OK, so you don't. But what really bothers
me is that the community seems to be under the impression that the rules
changed with PHP 5, which is absolutely untrue. This threw anE_NOTICE
under PHP 4.0 which has been shifted, first toE_STRICT
(I haven't
checked this) and then toE_DEPRECATED
in 5.3.
+1, I agree. But I can only speak for 5.x since this is where the
platform we decided to begin with PHP extension support.
I didn't see the reason for it, but I also thought that it was to
aggressive too. Meaning, I can see it applied to explicit PHP code, but
not extensions. I can also see it as a LINT concept at the PHP code
level which is one of the areas one might fall into bugs, i.e, not
passing a reference when it was expected. This is a common concept
with strong type-casting compilers, i.e. C/C++.
I recall talking it over with some PHP veteran users helping with adding
direct PHP support and the only reason I saw for it was a "Coding Style"
push or mindset towards OOPs and functional programming; x = func(y), as
oppose to func(x,&y);
All I know is it's slower in most (not all) situations. So the real
question is: should there be a warning at all? And if so, shouldn't it
be there by default?
Passing by reference should be faster, not slower since the latter
requires data to be pushed into the stack. But I see that as a moot
point as both are natural considerations for programmers.
In general, unless something is absolutely critical, I always leaned
toward the theory of "Less Surprises & Working Code."
I can see this more applicable to compiling PHP code or run time
warnings for explicit PHP code. For extensions, where the Programmer
has to do a lot more to make parameter referencing work with ZEND in the
first place, there is an explicit intent to expose an API with functions
containing variable pointers.
In this case, IMO, the compromise might be to offer the option as an
[Extension_name] option.
I think in the end Steve, users are going to do what it takes anyway.
Set the option on to support some package they really, really, really
want to use. I mean, they might not have a choice in the matter. I
don't see a security reason for it. Its more of a coding style issue
and users really don't give a hoot as long the package works for them.
My 2 pennies of course :-)
--
Hector