I was wondering, if json_decode serializes a multidimensional JSON into a stdClass, why can’t it serialize into a specified custom Class?
Why can’t i pass in a new Homullus() and let it do some kind of black box magic? Afterall, as ew have often seen, ORMs like Doctrine and Eloquent do this job for us — they are given a JSON (via DB or file), or even a mysql row, and somehow, they are able to discern what to do with it, and how to unserialize it onto a living and breathing object.
How i got to thinking
I was working on a cute little project pinkosensitivism.com which uses the public guest Instagram API to showcase all posts from the client’s page. The system was really simple as you might guess, but since calls to servers are expensive in the case of Instagram API, i created a basic local cache. The project was OO based, with an MVC model similar to an extremely simplified Symfony structure. 4 Services, 1 Provider, 1 transformer and 2 endpoints… Let the games begin.
A hour into it, i realized that i have 4 transformers, which are used basically all over the place, and that most of my code is not the logic behind caching anymore, but a series of silly transformations, and guesses whether something is an object or an array, or in some instances a stdClass. I decided i am not smart enough to solve this mess i made on my own, which got me to thinking about how this is handled on some large-scale projects i have worked on. While no project is the same, most of them did solve this in a similar manner — and suddenly it made perfect sense:
Transform everything on input and output only, and always work with valid state objects, never with anonymous arrays.
While we do manually transform data received from the client via form submit or API, we do it for validation and verification purposes, on the other hand unserializing data received from the DB is usually performed by the ORM itself, behind closed curtains.
The problem with StackOverflow
So as always the story continues on Google and S/O. It was 23:30 so i got to bed, took my iPhone and did some basic wide-scale research for about 30 min. People suggested a wide array of hacks, but no one had any actual idea of how this is done.
For example, a bunch of people were willing to create a stdClass out of the JSON, and then serialize this stdClass into plain text while performing a brute-force regex replacement of Class name. After a unserialize, you have your Object. Many smart, Much Cool.
Yes i am aware that sometimes it is necessary to play dirty like that, but come on, if you are willing to write a 1000 character answer, why not use the same amount of time to find a better way of doing it?
The last time i felt this hopeless and angry at myself and Stack Overflow was a year ago when i researched for two days on how exactly iOS native scroll implemented gravity under the hood.
I am eternally grateful to all the people who wrote on Stack Overflow and various forums, but i cannot make peace with the obvious fact that as soon as you get interested in how something works under the hood, there is no one around. It seems that we are able to create silly (or awful) hacks around any possible problem, but when someone is searching for a genuinely smart and advanced way to solve a problem, S/O is dead.
That kind of information can only be found on author blogposts, or by asking someone smarter than yourself. Which i immediately did.
Enter Reflection
My senior colleague Boris Ćeranić told me that he is not 101% certain of the details, but had explained to me in brief what PHP Reflection API is, and how vendors implement it under the hood in order to seem smarter than they are.
I will not delve into all-out explaining what Reflection in programming is, but in simple terms, it is a set of built in methods that allow a programmer to find out details about the structure of a Class. More information equals more power, and if you are a spare amount of time, like vendor libraries do, you can definitely cover all cases and create an almost sentient ORM, like they did :)
Philip did an awesome thing by explaining this in detail and with examples here:
So, a quick analysis for those who did not open the link. If you are able to ask an Object what Class it is, and are able to ask that Class what methods are supported in which states, or what private variables it has, if you are able to find out that Class extends another class or that there are properly named getters and setters. You can do magical stuff like create a magic getter which in a way changes the structure of PHP others will write after you. A marvelous text, again, i highly recommend reading it.
Why not go a step further? Inspect a Class’s constructor for input parameters. Now inspect their strongly typed arguments. Now you know that they require certain dependencies, and you can provide them. Yes, you have just created a Dependency Injection container… Oh, Philip also did a text on that…
Back to the topic at hand
Ok, so i am an ORM and know all this stuff about the Class i want to create out of this data that i have, but how do i do that?
Boris’s suggestion seems to have been that like Philip created a magic getter, we can conclude if conditions are met within the class and create a magic setter. Now just do a simple automated transformation based on the column names (or properties in case of JSON).
But wait, what do we do if the class does not have properly named setters? Also what if setters perform some irreversible transformations to the Object? What if the properties are private? It dawned on me that there must be a way to overload the access level and set these on creation. And there is. Again, Reflection API.
So there you have it, from my research it seems that they just do a bunch of tests and probes, trying to find out what is the best way to approach object creation for that particular Class. If everything else fails, you can always loop over the column names and forcibly set the parameters…