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