Hacking it Out: Creative Uses of Object Destructors
Take a look at this code snippet from a hypothetical PHP framework (Framework A). It sends a HTTP response to the client.
respond($result);
And then this. Same as above, but with the Content-Type
set to application/json
:
respond($result)->asJson();
One more. Same as above, but with the status code explicitly set as 500:
respond($result)->asJson()->withStatus(500);
// or
respond($result)->withStatus(500)->asJson();
Basically, you can send a generic response by calling respond
alone, or you can customise your response by chaining additional methods on the result of respond
.
There’s something a bit suspicious about the snippets above, though. If you can’t see it yet, compare those snippets above with this, from Framework B:
respond($result)->send();
respond($result)->asJson()->send();
respond($result)->asJson()->withStatus(500)->send();
respond($result)->withStatus(500)->asJson()->send();
When is the response actually sent?
With Framework B, the flow is obvious. You create some sort of Response
object, call methods to customise it, and then call send
to send it. But what about Framework A? How does the response know when to be sent? If the response is sent as soon as we call respond
, then calling extra methods on the result wouldn’t have any effect. But they do.
Which means there must be some way the Response
object can tell what methods you call on it and wait for you to finish calling them, before it sends. Actually, there is something like that: the destructor.
__destruct
The constructor is a fairly popular guy. The destructor? Not so much. In case you’re not familiar with the destructor, it’s a method that is called before an object is “destroyed”, just like the constructor is called after an object is created. The idea behind the destructor is to free up any resources the object may have acquired during its lifetime, such as a database connection.
Destructors exist in several OOP languages, including C++, Python, Swift and PHP. (Sorry, JavaScript not included.) Typically, in a language with garbage collection like PHP, you shouldn’t call the object destructor explicitly; it is called when there are no more references to an object or the object goes out of scope.
Let’s do a basic implementation of the respond
flow in both froms:
function respond($response) {
return new Response($response);
}
Calling send
explicitly:
class Response
{
public function asJson()
{
$this->contentType = "application/json";
return $this;
}
public function withStatus($code)
{
$this->statusCode = $code;
return $this;
}
public function send()
{
header("Content-Type: $this->contentType", true, $this->statusCode);
echo $result;
}
}
And using a destructor:
class Response
{
public function asJson()
{
$this->contentType = "application/json";
return $this;
}
public function withStatus($code)
{
$this->statusCode = $code;
return $this;
}
public function __destruct()
{
header("Content-Type: $this->contentType", true, $this->statusCode);
echo $result;
}
}
So basically, we moved the send
functionality into the destructor. Pretty, cool, eh?
And, no, it’s absolutely not necessary. We could have gone with the explicit send
approach. Or both. It’s purely a matter of preference.
Here’s an example of how the destructor is used creatively: Laravel’s events system allows you to broadcast an event by calling broadcast($event)
. If you want to exclude the sender from also receiving the event, just do broadcast($event)->toOthers()
. The event is actually broadcast in the destructor. See for yourself.
Thanks for reading. Why not clap for yourself for learning something new (or something you already knew)?
I write about my software engineering learnings and experiments. Stay updated with Tentacle: tntcl.app/blog.shalvah.me.