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/

Reply via email to