Fun with destructors in PHP
-
This is one of those neat snippets of shit you shouldn't do in PHP, but completely can. Incidentally this comes indirectly from a presentation from the core PHP dev team as to why optimisation in PHP is hard.
<?php class destructor { function __destruct() { $GLOBALS['b'] = 2; } } $b = new destructor; $a = 1; $b = 1; echo $a + $b;
...outputs
3
.Trying to work out whether this is straight up or at least mildly cromulent with only a side order of .
It definitely needs some Jeff Goldblum.
-
Is that deterministic? I thought PHP was garbage collected...does it have deterministic destructors at the same time?
-
@LB_ yes, it is. https://3v4l.org/ZurZB demonstrates that it's consistent across 5.6, 7 and 7.1 versions.
Sequence point-wise, the assignment into $b occurs, this makes the ref count on the original zval 0, so it can be garbage collected, and it is at that point, that the destructor is called. (It may or may not be GC'd at that point but it has no more references so the destructor will be called anyway.)
-
I propose that the first line of every PHP program be changed to:
!?php
just so save timeโฆ
-
@dkf said in Fun with destructors in PHP:
I propose that the first line of every PHP program be changed to:
!?php
just so save timeโฆ
<div> !?php foreach ($array as $var) { ?! <div>!?=$var?!</div> </div>
Nah, never catch on.
-
@Arantor It'd depend on the exact garbage collection algorithm used, but if
__destruct()
is commonly used and expected to be called early, PHP's going to be stuck with reference counting for now as that's the only algorithm that does a good job of calling these things reliably early.
-
@dkf PHP has been ref-counting for a number of years now. I see no reason for it to change any time soon, especially as it doesn't exactly, say, enthusiastically call its GC. Most of the time the GC is only actually called at end of script, but destructors are called as soon as possible, at least in theory.
-
@Arantor said in Fun with destructors in PHP:
destructors
Formally they're a kind of finalization callback if there's actually garbage collection about, and the guarantee that GCs give is that the finalizer will be called before the object is collected, but they don't promise to actually collect the object at all.
-
@dkf so PHP does something that is almost but not quite what everyone else calls the same thing. Yuuuup, sounds like PHP.
-
@Arantor I don't think you can call this a PHP issue.
#!/usr/bin/python3 class destructor(): def __del__(self): global b b = 2 b = destructor() a = 1 b = 1 print(a+b)
This also prints 3.
-
@Dragnslcr I figured it applied in a lot of places but PHP. makes a good punching bag
-
@Arantor said in Fun with destructors in PHP:
<div> !?php foreach ($array as $var) { ?! <div>!?=$var?!</div> </div>
Still better than the "helpful" alternatives you could use before PHP 7.x:
<ul> <% foreach ($array as $var) { %> <!--โ ASP style tags work --> <li> <script language="php">echo $var;</script> <!--โ yes, this works too... --> </li> <% endforeach %> </ul>
-
@DCoder fuck ASP style tags in their percentage-holes.
-
@Arantor The script tag is definitely worse. It suggests that there's no big difference between server-side and client-side code; just like some idiot outsource developers seem to believe.
-
@Arantor said in Fun with destructors in PHP:
@Dragnslcr I figured it applied in a lot of places but PHP. makes a good punching bag
Can't argue with that.
-
@Arantor said in Fun with destructors in PHP:
This is one of those neat snippets of shit you shouldn't do in PHP, but completely can. Incidentally this comes indirectly from a presentation from the core PHP dev team as to why optimisation in PHP is hard.
<?php class destructor { function __destruct() { $GLOBALS['b'] = 2; } } $b = new destructor; $a = 1; $b = 1; echo $a + $b;
...outputs 3.
Trying to work out whether this is straight up or at least mildly cromulent with only a side order of .Well
- The existence of destructors (or finalizers or whatever-you-want-to-call-them) is OK and often necessary.
- The fact destructors can access outside environment is also OK and sometimes necessary, e.g. to create a scope guard.
- The snippet demonstrates a pathological case, because while real projects shouldn't do it, the interpreter is still expected to preserve correctness in them.
Using destructor that accesses
$GLOBALS
, in a real project, is probably a . But the features that allow it, by themselves, are not and in the context the snippet is not either.
-
@Bulb using a destructor to modify the variable literally just destructed seems like a .
-
@Arantor If you had that in production code, it would be. But in this case it is a pathological case on which an optimizer must not go haywire. And as such it is simply valid test case.
-
@Arantor said in Fun with destructors in PHP:
@Bulb using a destructor to modify the variable literally just destructed seems like a .
I do this sometimes in C++ or C# (with
using
blocks in the latter) to save and restore a variable's contents, such as an "ignore all user input but Cancel" flag in a windowed program that's performing a long operation while still pumping messages.
-
@Dragnslcr said in Fun with destructors in PHP:
I don't think you can call this a PHP issue.
Quick, kill it with fire before it reproduces again!!!
-
@Arantor said in Fun with destructors in PHP:
using a destructor to modify the variable literally just destructed seems like a .
it's a wtf i can get on board with though.
i apporve this message.
-
@Bulb said in Fun with destructors in PHP:
The snippet demonstrates a pathological case, because while real projects shouldn't do it, the interpreter is still expected to preserve correctness in them.
Is it preserving correctness though? The way I would interpret it (I'm not familiar with PHP), is that you first create a destructor object in $b, and then when you assign 1 to $b I imagine the destructor object runs out of references, executes its __destruct function, and then the assignment should complete. Having it work the other way around (first the assignment completes, then the destructor runs) seems to be the wrong way to go about it. You're having the dead object change things after the operation that killed it completed.
@Arantor said in Fun with destructors in PHP:
@Bulb using a destructor to modify the variable literally just destructed seems like a .
Eh, that's fine in itself. In C++, for example, the object isn't gone until the destructor finishes. So you can call your own methods and modify your own members and do anything you need to do inside it. Which you'd normally use to clean up resources, close handles and such.
-
@Arantor said in Fun with destructors in PHP:
@Bulb using a destructor to modify the variable literally just destructed seems like a .
The variable was not destructed! Only its value!
-
@Kian said in Fun with destructors in PHP:
then when you assign 1 to $b I imagine the destructor object runs out of references, executes its __destruct function, and then the assignment should complete. Having it work the other way around (first the assignment completes, then the destructor runs) seems to be the wrong way to go about it.
The assignment completes then the destructor runs because the assignment is completed and then it cleans up references as far as I can tell, which is when it runs the destructor.
-
@Kian said in Fun with destructors in PHP:
The way I would interpret it (I'm not familiar with PHP), is that you first create a destructor object in $b, and then when you assign 1 to $b I imagine the destructor object runs out of references, executes its __destruct function, and then the assignment should complete.
Remember, PHP, like most dynamically typed languages (and Java), has fully referential semantics. So think of
$b
asstd::shared_ptr<Anything>
.@Kian said in Fun with destructors in PHP:
Having it work the other way around (first the assignment completes, then the destructor runs) seems to be the wrong way to go about it. You're having the dead object change things after the operation that killed it completed.
Guess what,
std::shared_ptr
will do it just like that. While the actual implementation might differ between the standard libraries, consider this implementation from libc++:template<class _Tp> inline shared_ptr<_Tp>& shared_ptr<_Tp>::operator=(const shared_ptr& __r) _NOEXCEPT { shared_ptr(__r).swap(*this); return *this; }
The
shared_ptr(__r)
first creates a temporary copy of__r
. Then the pointers are swapped, so nowthis
already points to the new value and the old value is still held in the temporary. And only then, at the end of the statement, is the temporary destroyed, which destroys the pointee if its reference count dropped to zero.It is done this way to provide some exception guarantees: if the destructor of the pointee throws and exception, the pointer is not left pointing to the (possibly partially) destroyed object, but already safely points to the new target. It also properly handles self-assignment without need for special case as the new target is referenced before the old is released, and it is generally more DRY as it reuses the copy constructor and destructor. The last point may not extend to the PHP VM, but the first two do.
-
@asdf said in Fun with destructors in PHP:
@Arantor The script tag is definitely worse. It suggests that there's no big difference between server-side and client-side code; just like some idiot outsource developers seem to believe.
That concept... took an embarrassingly long time for me to understand when I first got into web stuff.
I can't understand how you could ever do anything without understanding it though - that first week or so was confusing as hell
-
@sloosecannon said in Fun with destructors in PHP:
That concept... took an embarrassingly long time for me to understand when I first got into web stuff.
I'm pretty sure everyone who started with web development felt that way in the beginning. The sheer number of languages/technologies involved makes web development extremely hard for novice programmers.
-
This post is deleted!
-
@Bulb said in Fun with destructors in PHP:
self-assignment
That's the critical requirement in my experience, as it is fairly easy to end up with situations where that is happening but it is difficult to prove that it is happening during compilation. Especially in production code.
-
@asdf Webdev is easy to pick up for beginners because you already have the tools you need; you can just write a html file in Notepad and open it with IE and see how everything works. You can also, for the most part, examine the source of any working application and find out how it is pieced together as a reference.
Webdev is hard to pick up for beginners because instead of starting with a simple "hello world", you're thrown headfirst into a network situation with a client-server architecture, a sandbox environment, new techniques coming out practically every week, and all the quirks and foibles of CORS and XSS protection to worry about.
-
@Yamikuronue Also, the standard situation is less than stellar. In theory, everything is standardized, while in practice, cross-browser compatibility can still be a nightmare depending on what you're trying to achieve.
-
@Yamikuronue I remember when I started with web dev, when Netscape was in its dying throes and all the browsers seemed to be IE or IE-embedded-in-a-custom-UI, so there was really only IE.
Plus everyone was cool with tables and images for perfection in layout.
-
@Arantor
And now we've gone full circle through "TABLES ARE TEH EVILZ", "well, technically, they're all table-formatted divs, so it's not a table", to "bootstrap all the things and let it deal with the table layout!"
-
@izzion we've gone further than that by fart-arsing around with divs that have display: table on them to achieve whatever fuckassery we would just have used images for back in the day.
-
@izzion said in Fun with destructors in PHP:
"bootstrap all the things and let it deal with the table layout!"
To be fair to Bootstrap, it achieves the layout by using
float
s
-
@izzion said in Fun with destructors in PHP:
@Arantor
And now we've gone full circle through "TABLES ARE TEH EVILZ", "well, technically, they're all table-formatted divs, so it's not a table", to "bootstrap all the things and let it deal with the table layout!"tables are for laying out tables of data. that's still fine, but for site layout using tables....... icky!
-
@accalia so a site using divs for everything and jamming layout with
display: table
is also OK?
-
@Arantor I'd call that a sign of the developer not understanding HTML and CSS sufficiently to do the job properly.
-
@Arantor said in Fun with destructors in PHP:
@accalia so a site using divs for everything and jamming layout with
display: table
is also OK?depends on how much the developer likes their ankles i guess.
/me smiles showing many many teeth
-
@RaceProUK said in Fun with destructors in PHP:
I'd call that a sign of the developer not understanding HTML and CSS sufficiently to do the job properly.
If you give a shit about accessibility, you don't use a
<table>
unless you're actually displaying tabular data. There are perfectly cromulent use cases fordisplay: table
.
-
@asdf said in Fun with destructors in PHP:
If you give a shit about accessibility, you don't use a
<table>
unless you're actually displaying tabular data.Agreed.
@asdf said in Fun with destructors in PHP:
There are perfectly cromulent use cases for
display: table
.Eh... I could probably find an alternative that doesn't use
display: table
.
-
@RaceProUK said in Fun with destructors in PHP:
Eh... I could probably find an alternative that doesn't use display: table.
Maybe, but it might require more markup. Sometimes, you really want to use the table layout algorithm. Why should abusing the hell out of
display: float
be any less evil thandisplay: table
?
-
@asdf said in Fun with destructors in PHP:
Sometimes, you really want to use the table layout algorithm
so use a table
-
@asdf said in Fun with destructors in PHP:
Why should abusing the hell out of
display: float
be any less evil thandisplay: table
?Done right,
float: left
/float: right
is more flexible and adaptable thandisplay: table
-
@RaceProUK said in Fun with destructors in PHP:
To be fair to Bootstrap, it achieves the layout by using
float
sThe
col-xs-
n and related classes are a fragile pile of poo. The thing relies on exactly 12 units fitting a row and there are negative paddings to cancel with other paddings, so when you make a small mistake, the layout blows up completely.
-
@Bulb Then don't make a mistake
-
@Bulb said in Fun with destructors in PHP:
when you make a small mistake, the layout blows up completely
Doesn't that claim apply to all CSS, with or without a framework around it?
-
@Bulb I routinely put more than 12 in a row so it can stack or float as appropriate.
-
@Arantor Hm, TR is that the app I work on uses it for things that actually should have been tables, because they actually are tables (multi-column lists, to be most precise).
-
@Bulb that is definitely a .