Hey Richard, On Thu, Jan 12, 2017 at 9:58 PM, Fleshgrinder <p...@fleshgrinder.com> wrote:
> On 1/12/2017 9:35 PM, Marco Pivetta wrote: > > Heya, > > > > While I agree that it is weird to be able to call constructors more than > > once, this is generally used for: > > > > * lazy loading > > * resource reset > > > > Specifically, what is going on is something like following:<?php > > > > final class DbConnection > > { > > private $dsn; > > private $initializer; > > public function __construct(string $dsn) > > { > > $this->dsn = $dsn; > > // socket stuff happens here, much like with PDO > > } > > > > public function query(string $queryString) : array > > { > > ($this->initializer)(); > > // irrelevant from here on > > return ['query' => $queryString, 'dsn' => $this->dsn]; > > } > > > > public static function lazyInstance(string $dsn) : self > > { > > $instance = (new > > ReflectionClass(self::class))->newInstanceWithoutConstructor(); > > $instance->initializer = function () use ($dsn, $instance) { > > $instance->__construct($dsn); > > $instance->initializer = function () { > > }; > > }; > > return $instance; > > } > > } > > > > $instance = DbConnection::lazyInstance('mysql://something'); > > > > var_dump($instance); > > > > var_dump($instance->query('SELECT * FROM foo')); > > var_dump($instance->query('SELECT * FROM bar')); > > > > Here's an example of it at work: https://3v4l.org/Y0eoL > > > > The pattern is simple: > > > > * intercept constructor call > > * capture constructor parameters > > * instantiate without constructor > > * defer constructor call for later > > > > The same can be used in a myriad of different ways, but this is a legit > > use-cases that generally don't involve coding everything into the same > > class (and I generally advise against doing that anyway). > > > > Therefore I don't see a reason to drop manual constructor calls, unless > > there is a strong necessity to get rid of 'em. > > > > > > > > Marco Pivetta > > > > Very creative but why not the following? > > ``` > <?php > > final class DbConnection { > > private $dsn; > > private $initialized = false; > > public function __construct($dsn) { > $this->dsn = $dsn; > } > > private function init() { > $this->initialized = true; > > echo $this->dsn , "\n"; > } > > public function query($queryString) { > // this is actually cheaper than calling the closure > $this->initialized || $this->init(); > > return ['query' => $queryString, 'dsn' => $this->dsn]; > } > > } > > $db_conn = new DbConnection('mysql://something'); > > var_dump( > $db_conn, > $db_conn->query('SELECT * FROM foo'), > $db_conn->query('SELECT * FROM bar') > ); > ``` > > https://3v4l.org/PaqqZ > > Works equally well in PHP and HHVM and achieves your goal without using > undocumented side effects and reflection. > > Adding the ability of a non-lazy and lazy construction is easily added > too if desired. > > -- > Richard "Fleshgrinder" Fussenegger > I made an example where everything was in a single class, but most scenarios involve a lazy-loading wrapper that has no knowledge of the original class besides its constructor. I use this approach to generate proxy classes that are "safe" to use with fluent interfaces, for example (because fluent interfaces are really a mess to work with). The alternative (for me, specifically) is to copy the constructor AST into a closure, then store the closure somewhere. Still, if that closure also calls the private constructor, I have to do it recursively, and so on until I just give up :-P Also, this assumes codegen. Large pieces of code will need rewriting, whereas I don't see strong reasoning for dropping a feature that, while weird, is actually useful. Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/