Skip to content

Instantly share code, notes, and snippets.

@Danack
Last active August 29, 2015 14:25
Show Gist options
  • Save Danack/af51e941ce705f66d756 to your computer and use it in GitHub Desktop.
Save Danack/af51e941ce705f66d756 to your computer and use it in GitHub Desktop.
Variable dependencies

In response to services depending on 'variable dependencies' not being super awesome.

I don't really see this being a huge issue.....so long as no-once accidentally 'shares' (a.k.a. makes a 'singleton') out of something that depends on something that is unique per process. i.e. in the code:

function backupFile(ResponseFactory $responseFactory) {
    //
}

so long as the ResponseFactory isn't shared, it isn't a problem. The function is passed a fresh ResponseFactory each time. If the service we were calling needed to be an object, and we didn't want to create it each time then it wouldn't be a problem to separate the constant dependencies from the variable dependencies by having the constant ones set in the constructor, and the variable ones in the method call:

class Controller {
	function __construct(DBConnection $dbConnection) {
	    $this->dbConnection = dbConnection;
	}
	function foo(ResponseFactory $responseFactory) {
		//$data = get some data from $this->dbConnection.
		$responseFactory->create($data);
	}
}

I will look into making a dev mode in Auryn where if you do something like:

class Request {..}
class ResponseFactory {
    function __construct(Request $request) {... }
}

$injector->share('ResponseFactory');
$injector->make('ResponseFactory');
//This should throw an error - as the dependency is not shared, but class that needs it is...
//which leads to surprises.

From Igor's article:

As you can see, scopes lead to a lot of complexity. This is a mess.

I haven't looked at the way Symfony does scopes, I tend to get a headache whenever I read that code. I will take Igor at his word when he says:

However, in addition to construction, the container is also responsible for managing state and context transitions. There is no clear separation between construction time and runtime, which leads to a lot of complexity.

Yeah.......no. That would be bad. The container/injector shouldn't be managing that. Context/scope management is really a separate concern from just dependency injection.

I don't think scopes/contexts should be that complicated....so long as you do them programaticlly rather than trying to do them through configuration.

So for the example of having a long running task that can process multiple requests then I would do something like the following, if I wanted to be absolutely sure that I had isolated the things that are dependent on a variable dependency.


<?php
//BackgroundJobService


$injector = bootstrapApplication();

do {

    $request = readRequestFromGlobalVars();
    $contextObjects = [];
    $contextObjects['Request'] = $request;
    $injector = createContext($injector, $contextObjects);
    $callable = getNextThingToProcessThatNeedsRequest();
    $injector->execute($callable);
}


class ContextObject {
    /** The type name that this object should be used for */
	public $name;
	/** The instance of the object that should be used.
	public $instance;
}

function createContext(Injector $injector, $contextObjects) {

	$injector = clone $injector;
	foreach ($contextObjects as $contextObject) {
		$injector->alias($contextObject->name, get_class($contextObject->instance));
		$injector->share($contextObject->instance);
	}

	return $injector;
}

But as I said, I think that would only be a problem if someone had accidentally shared something that was dependent on a variable dependency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment