Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

PHP7 Stack Trace Mess

46 views
Skip to first unread message

Thomas Mlynarczyk

unread,
Jun 23, 2015, 10:28:29 AM6/23/15
to
According to
<https://github.com/php/php-src/blob/php-7.0.0alpha1/UPGRADING>, with
PHP7...

| exception backtraces will no longer display the original value that
| was passed to a function and show the modified value instead. For
| example
|
| function foo($x) {
| $x = 42;
| throw new Exception;
| }
| foo("string");
|
| will now result in the stack trace
|
| Stack trace:
| #0 file.php(4): foo(42)
| #1 {main}
|
| while previously it was:
| Stack trace:
| #0 file.php(4): foo('string')
| #1 {main}

I fail to see any advantage in this change. It basically means that
stack traces will be misleading, making debugging harder. While it may
be simpler for the engine not having to "remember" the original values,
I find a messed-up stack trace an unacceptably high price to pay for
such an optimization (if that is indeed the reason).

How am I supposed to get correct stack trace information in PHP7?

Greetings,
Thomas

--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)

Stefan Froehlich

unread,
Jun 23, 2015, 12:58:36 PM6/23/15
to
On Tue, 23 Jun 2015 16:28:23 Thomas Mlynarczyk wrote:
> | exception backtraces will no longer display the original value that
> | was passed to a function and show the modified value instead. For
> | example
> |
> | function foo($x) {
> | $x = 42;
> | throw new Exception;
> | }
> | foo("string");
> |
> | will now result in the stack trace
> |
> | Stack trace:
> | #0 file.php(4): foo(42)
> | #1 {main}

> I fail to see any advantage in this change.

Neither do I.

> It basically means that stack traces will be misleading, making
> debugging harder.

I'd rather say that the trace is simply *wrong*, because foo(42) has
never been called.

> While it may be simpler for the engine not having to "remember"
> the original values, I find a messed-up stack trace an
> unacceptably high price to pay for such an optimization (if that
> is indeed the reason).

It's the only reason I can think of. Otherwise all arguments would
have to be copied eventually - just in case.

At the very minimum I'd expect an ini-setting to control this
behaviour, but:

> How am I supposed to get correct stack trace information in PHP7?

Not at all, I am afraid.

Bye,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Stefan - pampische Liebe, die selten schnuppert.
(Sloganizer)

Christoph M. Becker

unread,
Jun 23, 2015, 2:00:54 PM6/23/15
to
Stefan Froehlich wrote:

> On Tue, 23 Jun 2015 16:28:23 Thomas Mlynarczyk wrote:
>
>> While it may be simpler for the engine not having to "remember"
>> the original values, I find a messed-up stack trace an
>> unacceptably high price to pay for such an optimization (if that
>> is indeed the reason).
>
> It's the only reason I can think of. Otherwise all arguments would
> have to be copied eventually - just in case.

Yes, indeed that has been done for performance reasons, and the change
has been introduced very early in the development of PHP 7.0.0, while it
has been called PHPNG (<https://wiki.php.net/phpng>).

> At the very minimum I'd expect an ini-setting to control this
> behaviour, but:
>
>> How am I supposed to get correct stack trace information in PHP7?
>
> Not at all, I am afraid.

I see two possibilities:

* don't assign to parameters
* make a feature request (<https://bugs.php.net/>) to introduce an ini
setting to be able to retain the original arguments

--
Christoph M. Becker

Thomas Mlynarczyk

unread,
Jun 23, 2015, 4:07:57 PM6/23/15
to
On 23/06/15 18:58, Stefan Froehlich wrote:
> On Tue, 23 Jun 2015 16:28:23 Thomas Mlynarczyk wrote:
>> [...] it may be simpler for the engine not having to "remember"
>> the original values [...]
> It's the only reason I can think of. Otherwise all arguments would
> have to be copied eventually - just in case.

Not necessarily. PHP implements "copy on write". If the argument is not
modified by the function, no copy will be made.

Christoph M. Becker

unread,
Jun 23, 2015, 4:15:29 PM6/23/15
to
Thomas Mlynarczyk wrote:

> On 23/06/15 18:58, Stefan Froehlich wrote:
>> On Tue, 23 Jun 2015 16:28:23 Thomas Mlynarczyk wrote:
>>> [...] it may be simpler for the engine not having to "remember"
>>> the original values [...]
>> It's the only reason I can think of. Otherwise all arguments would
>> have to be copied eventually - just in case.
>
> Not necessarily. PHP implements "copy on write". If the argument is not
> modified by the function, no copy will be made.

Still, at least the zvals (16 bytes each) would have to be copied.

--
Christoph M. Becker

Thomas Mlynarczyk

unread,
Jun 23, 2015, 4:22:35 PM6/23/15
to
On 23/06/15 20:00, Christoph M. Becker wrote:
> * don't assign to parameters

The problem is that I am not perfect. It just *might* happen that I
accidentally assign to a parameter. And that's precisely when I would
need a correct stack trace to track down the error.

> * make a feature request (<https://bugs.php.net/>) to introduce an ini
> setting to be able to retain the original arguments

The idea somehow feels... surrealistic: A correct stack trace as an
optional feature: ini_set( 'zend.report_correct_stacktrace', true ).

Christoph M. Becker

unread,
Jun 23, 2015, 6:03:37 PM6/23/15
to
Thomas Mlynarczyk wrote:

> On 23/06/15 20:00, Christoph M. Becker wrote:
>
>> * make a feature request (<https://bugs.php.net/>) to introduce an ini
>> setting to be able to retain the original arguments
>
> The idea somehow feels... surrealistic: A correct stack trace as an
> optional feature: ini_set( 'zend.report_correct_stacktrace', true ).

I would not necessarily assume a stack trace to include any arguments at
all. It would be nice to have them, and it would even be nicer to have
the actually given arguments. However, it doesn't help to argue in this
newsgroup about it. Go ahead and file a bug report at
<https://bugs.php.net/>, if you consider the changed behavior a bug. :)

--
Christoph M. Becker

Stefan Froehlich

unread,
Jun 24, 2015, 2:13:40 AM6/24/15
to
On Tue, 23 Jun 2015 22:07:50 Thomas Mlynarczyk wrote:
> On 23/06/15 18:58, Stefan Froehlich wrote:
> > On Tue, 23 Jun 2015 16:28:23 Thomas Mlynarczyk wrote:
> >> [...] it may be simpler for the engine not having to "remember"
> >> the original values [...]
> > It's the only reason I can think of. Otherwise all arguments would
> > have to be copied eventually - just in case.

> Not necessarily. PHP implements "copy on write". If the argument is not
> modified by the function, no copy will be made.

Yes, but *if* the argument is modified, you have to copy it "just
in case", because in all except a very few cases you won't need the
original value any more.

Bye,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Laune mit Stefan, überlegen und imposant!
(Sloganizer)

Thomas Mlynarczyk

unread,
Jun 24, 2015, 4:44:08 AM6/24/15
to
On 24/06/15 00:03, Christoph M. Becker wrote:
> I would not necessarily assume a stack trace to include any arguments at
> all.

I had always assumed that. But it seems I was wrong. I just checked how
Python does it -- no arguments there.

> However, it doesn't help to argue in this newsgroup about it.

Actually I wanted to know if I had missed something obvious here since
the cost (wrong stack trace) outweighs by far the benefit (no need to
remember the original values). But if having the arguments in the stack
trace is actually a luxury that not even Python provides, then I guess
I'll have to put up with it and remove all arguments from my stack trace
display. It's a huge loss of usefulness though...

> Go ahead and file a bug report at
> <https://bugs.php.net/>, if you consider the changed behavior a bug. :)

Since it was deliberate, it's not actually a bug, is it? Anyway, I don't
think they would listen to me.

Christoph M. Becker

unread,
Jun 24, 2015, 2:02:11 PM6/24/15
to
Thomas Mlynarczyk wrote:

> On 24/06/15 00:03, Christoph M. Becker wrote:

>> However, it doesn't help to argue in this newsgroup about it.
>
> Actually I wanted to know if I had missed something obvious here since
> the cost (wrong stack trace) outweighs by far the benefit (no need to
> remember the original values).

In my opinion it is good to consult others whether something is a bug or
not before filing a bug report. However, it seems to me that everything
has already been said, i.e. that the behavior is doubtful, but has been
deliberately done for performance reasons.

>> Go ahead and file a bug report at
>> <https://bugs.php.net/>, if you consider the changed behavior a bug. :)
>
> Since it was deliberate, it's not actually a bug, is it?

IMHO, it is not. Therefore I suggested to file a feature request.

> Anyway, I don't
> think they would listen to me.

You'll never *know* unless you try. :)

--
Christoph M. Becker

Matthew Carter

unread,
Jun 24, 2015, 2:09:41 PM6/24/15
to
Off the direct topic, but on general topic, other than performance
enhancements to attempt to outdo facebook's HHVM PHP implementation (or
whatever its called), what type of new features does PHP7 plan to
introduce?

--
Matthew Carter (m...@ahungry.com)
http://ahungry.com

Thomas Mlynarczyk

unread,
Jun 24, 2015, 2:50:45 PM6/24/15
to
On 24/06/15 20:09, Matthew Carter wrote:
> Off the direct topic, but on general topic, other than performance
> enhancements to attempt to outdo facebook's HHVM PHP implementation (or
> whatever its called), what type of new features does PHP7 plan to
> introduce?

Have a look here:
<http://www.slideshare.net/jpauli/php7-is-coming>

Among other things:
- Scalar type hints and return type hints
- Null coalesce operator (??)
- Spaceship operator (<=>)

Christoph M. Becker

unread,
Jun 24, 2015, 7:09:23 PM6/24/15
to
Thomas Mlynarczyk wrote:

> On 24/06/15 20:09, Matthew Carter wrote:
>
>> Off the direct topic, but on general topic, other than performance
>> enhancements to attempt to outdo facebook's HHVM PHP implementation (or
>> whatever its called), what type of new features does PHP7 plan to
>> introduce?
>
> Have a look here:
> <http://www.slideshare.net/jpauli/php7-is-coming>

Very nice! Haven't seen it before - thanks.

> Among other things:
> - Scalar type hints and return type hints
> - Null coalesce operator (??)
> - Spaceship operator (<=>)

See also <https://github.com/tpunt/PHP7-Reference>.

--
Christoph M. Becker

Matthew Carter

unread,
Jun 24, 2015, 10:12:53 PM6/24/15
to
"Christoph M. Becker" <cmbec...@arcor.de> writes:

Hm, so with new strict type scalar hints it is turned on via per file
(not global) basis, and whether something is strict or not is dependent
on the declare being in the file that makes calls to the functions, not
the file that defines the functions.

Doesn't that seem sort of weird?

If using a third party library that expects things to be on a strict
basis, the library can't enforce the strict behaviour, it requires the
user of the library to include a declare strict at the top of their file
that makes use of it.

What happens when two external libraries are being used, one which
assumes the user is operating in strict mode, while the other assumes
not?

Thomas Mlynarczyk

unread,
Jun 25, 2015, 4:02:07 AM6/25/15
to
On 25/06/15 04:12, Matthew Carter wrote:
> Hm, so with new strict type scalar hints it is turned on via per file
> (not global) basis, and whether something is strict or not is dependent
> on the declare being in the file that makes calls to the functions, not
> the file that defines the functions.
>
> Doesn't that seem sort of weird?

It's PHP, what did you expect? Instead of simply regarding

function foo ( int $arg ) { /* ... */ }

as the equivalent of

function foo ( $arg ) {
if ( !is_int( $arg ) throw new InvalidArgumentException( ... );
/* ... */
}

they had to invent problems where there are none. Yes, reasons are given
in the relevant RFC, but they are not at all convincing to me.

> If using a third party library that expects things to be on a strict
> basis, the library can't enforce the strict behaviour, it requires the
> user of the library to include a declare strict at the top of their file
> that makes use of it.
>
> What happens when two external libraries are being used, one which
> assumes the user is operating in strict mode, while the other assumes
> not?

It seems that your type-hinted function will receive its arguments
casted to the hinted type in any case. Of course, if a wrong type is
passed, it may silently be converted to the hinted type instead of
causing an error, but that -- together with the messed up stack trace --
will make debugging so much more fun, won't it?

Christoph M. Becker

unread,
Jun 25, 2015, 9:48:41 AM6/25/15
to
Thomas Mlynarczyk wrote:

> On 25/06/15 04:12, Matthew Carter wrote:
>> Hm, so with new strict type scalar hints it is turned on via per file
>> (not global) basis, and whether something is strict or not is dependent
>> on the declare being in the file that makes calls to the functions, not
>> the file that defines the functions.
>>
>> Doesn't that seem sort of weird?

It is the only way to let the user (opposed to library developers)
decide whether weak or strict type checking should be applied.

> It's PHP, what did you expect? Instead of simply regarding
>
> function foo ( int $arg ) { /* ... */ }
>
> as the equivalent of
>
> function foo ( $arg ) {
> if ( !is_int( $arg ) throw new InvalidArgumentException( ... );
> /* ... */
> }

Basically, that is how strict typing works.

> they had to invent problems where there are none. Yes, reasons are given
> in the relevant RFC, but they are not at all convincing to me.

Well, you'll want to consider the background. "Everybody" wanted scalar
type hints, but no broad consensus could be achieved regarding the
semantics (strict vs. weak). The Scalar Type Declarations RFC is a
compromise trying to satisfy most developers.

Apparently, you're in favor of strict type hints, but I am not, because
it would be a pretty big change in how PHP works, and also there are
some weird edge cases, e.g.:

<?php
declare(strict_types=1);
$num = 1 << 32;
$num *= $num;
echo dechex($num);

What's the output?

--
Christoph M. Becker

Thomas Mlynarczyk

unread,
Jun 25, 2015, 12:04:20 PM6/25/15
to
On 25/06/15 15:48, Christoph M. Becker wrote:

> It is the only way to let the user (opposed to library developers)
> decide whether weak or strict type checking should be applied.

Shouldn't the mere presence (or absence) of scalar type hints in a
function declaration serve this purpose? If you absolutely want weak
typing, you simply don't use type hints (nor strict libraries).

> Well, you'll want to consider the background. "Everybody" wanted scalar
> type hints, but no broad consensus could be achieved regarding the
> semantics (strict vs. weak).

To me, "type hint" implies "strict". If I say "int", I mean "int" and
not "string containing a representation of an integer".

> Apparently, you're in favor of strict type hints, but I am not, because
> it would be a pretty big change in how PHP works, and also there are
> some weird edge cases, e.g.:
>
> <?php
> declare(strict_types=1);
> $num = 1 << 32;
> $num *= $num;
> echo dechex($num);
>
> What's the output?

I consider PHP's built-in functions as being equivalent to functions
without type hinting. And thus the behaviour should not change from what
it currently is.

According to the manual, the signature is

string dechex( int $number )

In your example, $num would be a float, larger than the allowed range
for int. So, taking the manual literally, I would expect this to cause
an error. (The actual output is "0", by the way.) The current behaviour
would be better "hinted" with

string dechex( numeric $number )

which would describe the allowed values more accurately. (Assuming
numeric = bool | int | float | string containing a number.) I do not see
any "weird edge case" here. Currently, the manual is inaccurate by
specifying "int" whereas the function also accepts bools, floats and
numeric strings. The latter fact could even be regarded as an
undocumented feature, since the documentation clearly says "int".
Anyway, I wouldn't call this function with anything else but an integer
argument.

Christoph M. Becker

unread,
Jun 25, 2015, 1:14:55 PM6/25/15
to
Thomas Mlynarczyk wrote:

> On 25/06/15 15:48, Christoph M. Becker wrote:
>
>> It is the only way to let the user (opposed to library developers)
>> decide whether weak or strict type checking should be applied.
>
> Shouldn't the mere presence (or absence) of scalar type hints in a
> function declaration serve this purpose? If you absolutely want weak
> typing, you simply don't use type hints (nor strict libraries).

That would allow non-scalar values to be passed without an (immediate)
indication. A weak scalar type "hint" would still restrict to "fitting"
scalars.

> I consider PHP's built-in functions as being equivalent to functions
> without type hinting. And thus the behaviour should not change from what
> it currently is.

With `declare(strict_types=1)` built-in functions are supposed to be
strictly type "hinted", e.g.

<?php
declare(strict_types=1);
sin("1");

will fail:

PHP Fatal error: Uncaught TypeException: sin() expects parameter 1
to be float, string given in ...

> According to the manual, the signature is
>
> string dechex( int $number )
>
> In your example, $num would be a float, larger than the allowed range
> for int.

Indeed, if we're assuming a 64bit build. On a 128bit build, $num would
be int. There are no 128bit builds yet, but basically, the same applies
to 32bit vs. 64bit builds.

> So, taking the manual literally, I would expect this to cause
> an error. (The actual output is "0", by the way.) The current behaviour
> would be better "hinted" with
>
> string dechex( numeric $number )
>
> which would describe the allowed values more accurately. (Assuming
> numeric = bool | int | float | string containing a number.) I do not see
> any "weird edge case" here. Currently, the manual is inaccurate by
> specifying "int" whereas the function also accepts bools, floats and
> numeric strings. The latter fact could even be regarded as an
> undocumented feature, since the documentation clearly says "int".
> Anyway, I wouldn't call this function with anything else but an integer
> argument.

PHP is a loosely typed language, and wherever a value of an unexpected
type is given it is automatically juggled to the expected type, if
feasible. Most PHP programmers are likely to be accustomed to this
behavior, and make use of it, at least from time to time.

However, the following fails:

<?php
declare(strict_types=1);
class Foo {
function __toString() {
return 'foo';
}
}
function bar(string $string) {
echo $string;
}
bar(new Foo);

Most likely the way to workaround this to cast `new Foo` to string, i.e.
`(string) new Foo`. Fine. However, I don't see why the function should
be type-"hinted" in the first place, because a cast will accept almost
arbitrary values, so the advantage to be able to detect programming
errors is practically gone.

Anyhow, you may consider taking one of your programs, adding
declare(strict_types=1) at the beginning of every file, and running it
under PHP 7.0.0alpha2 (which has been released today, by the way).
There may be some surprises. :)

--
Christoph M. Becker
0 new messages