On Fri, Nov 24, 2023 at 5:06 AM Mike Schinkel <m...@newclarity.net> wrote:
>
>
>
> 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
>
> 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
>
> And if you are dealing with 3rd party classes, you'll need to write wrapper 
> classes.
>
> Hope this helps.
>
> -Mike
>

Hey Mike,

Using a static method to enforce LSP when it should have been enforced
in the first place, merely proves my point that it violates LSP. I
don't know how else to spell it out, I guess we can try this one next:

class A {
    final public static function foo() {
        return new static();
    }
}

class B extends A {
    public function __construct(private string $name) {}
}

$a = B::foo();

It just plainly violates LSP and you can't write safe code that
touches constructors. I don't know how else to spell this out.

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to