> On Nov 23, 2023, at 4:50 PM, Robert Landers <landers.rob...@gmail.com> wrote:
> 
> On Thu, Nov 23, 2023 at 10:30 PM Deleu <deleu...@gmail.com> wrote:
>> 
>> Constructors are an implementation detail of a specialized class and as such 
>> they're not subject to LSP because the goal of LSP is to be able to make 
>> sure that any object of a given type hierarchy can be used to accomplish a 
>> certain behavior. If you take a step back from PHP's dynamic nature and 
>> think about LSP from a more pure type system, the fact you're expecting an 
>> object of type C, but then you completely disregard everything about the 
>> object itself and dive into it's metadata to build another object, that's 
>> the moment you're no longer playing by the rules of OOP. It's like those 
>> mathematical equations that prove that 1 = 2, they all have one thing in 
>> common: they end up dividing by 0 at some point.
>> 
>> OOP here dictates that you should reach for patterns like Builder, Abstract 
>> Factory or similar. That way you constraint yourself to the rules of OOP and 
>> you won't get weird outcomes.
>> 
>> From another point of view, when a type is expected by a function or method, 
>> all we can expect from it is whatever was defined as the blueprint 
>> (class/interface) of that object and the __construct() is a special method 
>> that is not assumed to be part of that blueprint because it's not reasonable 
>> to do `$object->__construct();` after receiving an object. As such, a 
>> constructor cannot break LSP because the constructor is not part of the 
>> object's API from a "receptor" point of view.
>> 
>> I don't have a vote so take my opinion with a bucket of salt, but if I could 
>> I would definitely vote against such RFC.
>> 
>> 
>> --
>> Marco Deleu
> 
> Thanks Marco,
> 
> That's an interesting perspective and one I would agree with for the
> most part, especially if you take my illustration at face value. Where
> it gets weird/breaks down is when you have a class-string, that you
> assert is the correct type, and then try to instantiate it:
> 
> // from somewhere
> $class = "C";
> 
> if(is_subclass_of($class, P::class)) {
>    $example = new $class("world");
> }
> 
> If PHP didn't offer these built-in methods, then I would fully agree
> with you, but it does, which puts it into a weird position where
> sometimes a class is substitutable, and in this one special case, it
> is not.

What Marco said pretty much mirrors the answer in Software Engineering SE:

https://softwareengineering.stackexchange.com/a/270738/9114 
<https://softwareengineering.stackexchange.com/a/270738/9114>

As if probably obvious now, when you are using a variable containing a class 
name to instantiate a class you are actually not using OOP, you are using the 
text expansion capabilities of PHP. The fact that it appears to be similar to a 
method call is just likely coincidence,

Now if classes were first class objects in PHP, then there might be an 
argument, but alas, they are not.

Anyway, I always addressed the problem you are running into by defining a 
`make_new()` static method.  Here's what your example might look after an 
initial refactor:

class P {
    public $msg;
    function __construct($msg){
        $this->msg = $msg;
    }
}
class C extends P {
    static function make_new($props){
        return new C($props["msg"]);
    }
}

$class = "C";

if(is_subclass_of($class, P::class)) {
   $example = $class::make_new( ["msg"=>"world"] );
   print_r($example);
}

Of course, getting it to production code will take a lot more, like you can see 
here (although this code has now been update for probably five years since I am 
no longer coding in PHP professionally):

https://github.com/search?q=repo%3Awplib%2Fwplib%20make_new&type=code 
<https://github.com/search?q=repo:wplib/wplib%20make_new&type=code>

And if you are dealing with 3rd party classes, you'll need to write wrapper 
classes.

Hope this helps.

-Mike

Reply via email to