haijin/instantiator

A library to create and obtain instances of objects using a simple DSL.

This package's canonical repository appears to be gone and the package has been frozen as a result.

v2.0.0 2019-02-22 00:51 UTC

This package is auto-updated.

Last update: 2022-12-28 00:32:54 UTC


README

A library to create and obtain instances of objects using a simple DSL.

Latest Stable Version Latest Unstable Version Build Status License

Version 2.0.0

If you like it a lot you may contribute by financing its development.

Table of contents

  1. Installation
  2. Usage
    1. Creating new instances
    2. Accessing singletons
    3. Overriding instantiators in the current process
    4. Overriding new instantiator with a singleton
  3. Running the tests

Installation

Include this library in your project composer.json file:

{
    ...

    "require": {
        ...
        "haijin/instantiator": "^2.0",
        ...
    },

    ...
}

Usage

Creating new instances

Instead of using new to create objects use

use  Haijin\Instantiator\Create;

$object = Create::a( Sample_Class::class )->with( 1, 2, 3 );

It is also possible not to use the DSL and avoid an object creation and a few function calls with

use  Haijin\Instantiator\Create;

$object = Create::object( Sample_Class::class, 1, 2, 3 );

or even

use  Haijin\Instantiator\Create;

$object = Global_Factory::new( Sample_Class::class, 1, 2, 3 );

but we strongly encourage to always use DSLs. The philosophy behind using DSLs is to optimize expressiveness, readability and simplicity. Code optimization should always be done after benchmarking real applications in their actual contexts of use.

Accessing singletons

Create singleton instances of any class with

use  Haijin\Instantiator\Singleton;

Singleton::create( Sample::class, new Sample( 1, 2, 3 ) );

and access them with

use  Haijin\Instantiator\Singleton;

$object = Singleton::of( Sample::class );

// or

$object = Global_Factory::singleton_of( Sample::class );

Singletons can also be named

use  Haijin\Instantiator\Singleton;

Singleton::create( 's', new Sample( 1, 2, 3 ) );

$object = Singleton::of( 's' );

Overriding instantiators in the current process

It's possible to temporary change the class instantiators and singletons within the scope of a closure in the current process with

use  Haijin\Instantiator\Global_Factory;
use  Haijin\Instantiator\Create;
use  Haijin\Instantiator\Singleton;

$object = Create::a( Sample::class )->with( 1, 2, 3 );

( $object instanceof Sample ) === true;

Singleton::create( 's', new Sample( 1, 2, 3 ) );


$singleton = Singleton::of( 's' );

( $singleton instanceof Sample ) === true;

Global_Factory::with_factory_do( function($factory) 
{
    // change the instantiators within the scope of this closure

    $factory->set( Sample::class, Different_Sample::class );

    Singleton::create( 's', new Different_Sample( 1, 2, 3 ) );


    $object = Create::a( Sample::class )->with( 1, 2, 3 );

    ( $object instanceof Different_Sample ) === true;


    $singleton = Singleton::of( 's' );

    ( $singleton instanceof Different_Sample ) === true;

}, $this);


// restores the previous instantiators

( $object instanceof Sample ) === true;

Singleton::create( 's', new Sample( 1, 2, 3 ) );


$singleton = Singleton::of( 's' );

( $singleton instanceof Sample ) === true;

That means that two different processes may override instantiators and singletons to their convenience at the same time. For instance, the instantiator class Database may be overriden as MysqlDatabse in one process and as PostgresDatabase in another one, but the code that uses the class Database can be safely shared and instantiate database objects without being aware of it and with no need to pass around factory nor container objects.

use  Haijin\Instantiator\Global_Factory;
use  Haijin\Instantiator\Singleton;

public function access_data_in_mysql($connection_string)
{
    Global_Factory::with_factory_do( function($factory) use($connection_string)
    {
        Singleton::create( Database::class, new MysqlDatabase( $connection_string ) );

        $this->process_data();

    }, $this);
}

public function access_data_in_postgres($connection_string)
{
    Global_Factory::with_factory_do( function($factory) use($connection_string)
    {
        Singleton::create( Database::class, new PostgresDatabase( $connection_string ) );

        $this->process_data();

    }, $this);
}

public function process_data()
{
    $db = Singleton::of( Database::class );

    /// etc ...
}

Overriding new instantiator with a singleton

The previous example had the problem that the code using a database needed to know that it was a singleton. That is a big assumption to do for a library.

Instead, it could instantiate the database creating a new instance each time but the code using it may override the creation of a new instance with a singleton:

use  Haijin\Instantiator\Global_Factory;
use  Haijin\Instantiator\Singleton;

public function access_data_in_mysql($connection_string)
{
    Global_Factory::with_factory_do( function($factory) use($connection_string)
    {
        $factory->new_as_singleton( Database::class, new MysqlDatabase( $connection_string ) );

        $this->process_data();

    }, $this);    
}

public function access_data_in_postgres($connection_string)
{
    Global_Factory::with_factory_do( function($factory) use($connection_string)
    {
        $factory->new_as_singleton( Database::class, new PostgresDatabase( $connection_string ) );

        $this->process_data();

    }, $this);    
}

public function process_data()
{
    $db = Create::a( Database::class )->with();

    /// etc ...
}

Doing this allows the bottom code, such a third party library, not to assume that an object is a singleton and the top most code, such an application, to make it a singleton or not depending on its needs without the library being aware of it.

Overriding the creation of an instance by a singleton should be done only if it has no secondary effects. For instance if the singleton does not hold shared state from one function to another.

Running the tests

composer specs