简体   繁体   中英

Passing out of scope data to a service container

I wish to create a new instance of SomeService which must be injected with some data which is not known when defining the service in Pimple. The following technically works, but surely cannot be the proper way to do so. How should this be accomplished?

<?php
use Pimple\Container;

class SomeService
{
    private $theNewData;
    public function __construct(MyPdo $pdo, array $theNewData){
        $this->theNewData=$theNewData;
    }
    public function getJsonString():string{
        return json_encode($this->theNewData);
    }
}
class MyPdo{}

function getServiceSometimeInTheFuture(Container $container):SomeService {
    $someFutureData= ['a'=>1,'b'=>2,'c'=>3,];

    /*
    How do I inject this content into SomeService other than using Pimple as a temporary transport?
    */
    $container['temp']=$someFutureData;
    $newInstance=$container['someService'];
    unset($container['temp']);
    return $newInstance;
}

require '../vendor/autoload.php';

$container=new Container();

$container['myPdo'] = function ($c) {
    return new MyPdo();
};

$container['someService'] = $container->factory(function ($c) {
    return new SomeService($c['myPdo'], $c['temp']);
});

$service = getServiceSometimeInTheFuture($container);
echo($service->getJsonString());

How do I inject this content into SomeService other than using Pimple as a temporary transport?

I think the root cause for this problem is that future data is actually data as its name suggests, and it is going to change multiple times over the time, so it should not be passed to the service at the time of service definition, but every time the consumer method in the service ( getJsonString here) needs that data.

... which is not known when defining the service

The data is not known, but how about the source of the data? Could you write a new service to act as data provider, so the original service can get required data when required in the future ?

I can suggest two solutions.
(I intentionally removed MyPdo from everywhere because it was not used at all, even in the constructor)

If you really need the data to be passed to the service at creation time:

<?php
require __DIR__ . '/../vendor/autoload.php';
use Pimple\Container;

class SomeService
{
    private $theNewData;
    public function __construct(array $theNewData){
        $this->theNewData=$theNewData;
    }
    public function getJsonString():string{
        return json_encode($this->theNewData);
    }
}

$container=new Container();

// Use pimple factory method to create new service instance, instead of creatng a custom function
$container['getServiceSometimeInTheFuture'] = $container->factory(function (Container $c):SomeService {
    return new SomeService($c['futureData']);
});

// `futureData` returns new data every time
$container['futureData'] = $container->factory(function(){
    return ['a' => rand(1, 10), 'b' => rand(1, 10), 'c' => rand(1, 10), ];
});

$service1 = $container['getServiceSometimeInTheFuture'];
$service2 = $container['getServiceSometimeInTheFuture'];

// Demonstrate how two different instances have different, persistent data
echo("\nservice 1:" . $service1->getJsonString());
echo("\nservice 2:" . $service2->getJsonString());
echo("\nservice 1:" . $service1->getJsonString());
echo("\nservice 2:" . $service2->getJsonString());

If you can postpone providing data to the service at the time it needs that data in the future:

<?php
require __DIR__ . '/../vendor/autoload.php';
use Pimple\Container;

// DataProvider decides what data should be provided to the service
class DataProvider {
    public function getData(){
        return ['a' => rand(1, 10), 'b' => rand(1, 10), 'c' => rand(1, 10), ];
    }
}

class SomeService
{
    private $dataProvider;
    public function __construct(DataProvider $dataProvider){
        $this->dataProvider=$dataProvider;
    }
    public function getJsonString():string{
        return json_encode($this->dataProvider->getData());
    }
}

$container=new Container();

// Use pimple factory method to create new service instance, instead of creatng a custom function
$container['getServiceSometimeInTheFuture'] = $container->factory(function (Container $c):SomeService {
    return new SomeService($c['dataProvider']);
});

$container['dataProvider'] = function() {
    return new DataProvider;
};

$service = $container['getServiceSometimeInTheFuture'];

// Demonstrate how THE SAME INSTANCE will have different data every time
echo("\n" . $service->getJsonString());
echo("\n" . $service->getJsonString());
echo("\n" . $service->getJsonString());
echo("\n" . $service->getJsonString());

Use Pimple's raw() method.

$container->raw('someService')($container, $someFutureData);

The service will need to be set up to accept the new data.

$container['someService'] = $container->factory(function ($c, $data) {
    return new SomeService($c['myPdo'], $data);
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM