This is exactly my code, I'm just stripping out everything that isn't needed:
First of all, I met this issue a lot of times and something tells me that it's something to do with PHP itself and "data structure hinting"?.
I have 3 objects, all of them respect the following interface:
interface OutputExportData { function output(); }
What this tells any object that implements it is that the object needs to output some kind of data. Let's assume that somewhere up the chain of execution, there's a foreach
that goes through these 3 objects and since they all 100% have the output
function, it gets called so I can get the data, my actual real usage is that I run an import of different steps that save / wrangle a lot of data and this output
function will give me a list of IDs / summary of what was imported / changed based on that object's implementation.
And there-in lies the issue. Every object is free to output
whatever they desire, or rather however they desire. Why is this a problem?
foreach( $objects as $object ) {
$export_data = $object->output();
saveToImportantList( $export_data['main_flags'] );
}
For every object, I look for a key main_flags
, since I'm the original developer I'll know that for all OutputExportData
objects, I need to implement this kind of array, but developers that will come and create their new objects to insert into my main flows will not know it, that's one big issue: the PHP script will break, since it can't find that key in the array.
Of course I can put it in the comments and documentation, but isn't there a way to type-hint what I expect PHP to ingest / output in the interface, such as:
interface OutputExportData {
public function output() : array<'main_flags' => array, 'name' => string> {}
}
So that I can force, through code that whoever's implementing my interface, to respect the same return schema, so issues like the one stated above won't happen?
Concerns:
- I understand that in my
foreach
I can just simply check for these keys and that the interface provides a contract for the top-level flow of the object (through naming of the interface's functions from where we can somewhat accurately tell what they are doing), but can't an interface also be concerned with how its implementing objects' functions output / move things? - This feels wrong to do: this
main_flags
key, I only need to use in this specific foreach and nowhere else, well, naturally as my system grows, I might use it in some other places, but for these other places, will it be relevant that my functions need to respect that structure? I'm not so sure. - What if my
output
function encounters a fatal error and cannot, under any circumstance, output my desired schema, can't I then do:
interface OutputExportData {
public function output() : boolean / array<'main_flags' => array, 'name' => string> {}
}
And in the end, the better question, on top of my original one is: should I even do this?
I read on interfaces a lot, for other languages too and here's a quote:
The functionality of a class does not depend on the interfaces it implements, the interface just provides a generic way of accessing the functionality.
Which is on-point, but if it was possible to do what I want, then the quote changes to:
The functionality of a class does not depend on the interfaces it implements, the interface provides a way of accessing the functionality and dictates what that functionality's end goal is.
Which doesn't sound like an interface.
output()
function to return the typeOutput
, which is a class that hasmain_flags
andname
as members.