I've been working on my own PHP framework for my projects. I just finished coding my DI container. There is always room for improvement. Any input be it good or bad on my code would be appreciated.
Also, would using exceptions instead of die()
be a better approach?
Should I move the higher API functions (register()
and addParameter()
) to a separate class?
<?php
class Reference
{
private $id;
public function __construct($id) {
$this->id = strtolower($id);
}
public function __toString() {
return (string) $this->id;
}
}
class ServiceContainer
{
private $services;
private $lastRegistered;
/**
* Register a service with the DI container
*
* @param string $id Name of the service
*
* @param string $class Class to attach to service
*
* @return object $this
*/
public function register($id, $class) {
$this->addService($id, $class);
return $this;
}
/**
* Add a parameter to a service
*
* @param mixed $parameter Parameter to add
*
* @param string $id Name of the service
*
* @return object $this
*/
public function addParameter($parameter, $id = null) {
$id = isset($id) ? $id : $this->lastRegistered;
$this->addService($id, null, array($parameter));
return $this;
}
/**
* Check if a service is registered
*
* @param string $id Name of the service
*
* @return bool
*/
public function isRegistered($id) {
$id = strtolower($id);
return array_key_exists($id, $this->services);
}
/**
* Add a service to the DI container
*
* @param string $id Name of the service
*
* @param string $class Class to attach to service
*
* @param array $parameters Array of parameters
*/
public function addService($id, $class, array $parameters = array()) {
$id = strtolower($id);
// Initialize the parameter array
if (! isset($this->services[$id]['parameters'])) $this->services[$id]['parameters'] = array();
foreach ($parameters as $parameter) {
$this->services[$id]['parameters'][] = $parameter;
}
if (isset($class)) $this->services[$id]['class'] = $class;
$this->lastRegistered = $id;
}
/**
* Get a service
*
* @param string $id Name of the service
*
* @return object Return the service
*/
public function getService($id) {
$id = strtolower($id);
if ($this->isRegistered($id)) {
// Build only when needed
return (is_object($this->services[$id])) ? $this->services[$id] : $this->buildService($id);
} else {
throw new \InvalidArgumentException(sprintf('Service "%s" does not exist.', $id));
}
}
/**
* Build the service
*
* This a low level function with all the DI container magic
*
* @param string $id Name of the service
*
* @return object Return the service
*/
private function buildService($id) {
if (! $this->isRegistered($id)) {
throw new \InvalidArgumentException(sprintf('Service "%s" does not exist.', $id));
}
if (! isset($this->services[$id]['class'])) {
throw new \RuntimeException(sprintf('Service "%s" has no class to construct.', $id));
}
$reflection = new \ReflectionClass($this->services[$id]['class']);
// If the paramters is a reference replace the parameter with the referenced service object
foreach ($this->services[$id]['parameters'] as &$parameter) {
if ($parameter instanceof Reference) {
$parameter = (string) $parameter;
$parameter = $this->buildService($parameter);
}
}
// Replace the whole array with the service object, this is destructive for the parameters
// but they are no longer needed
// If the constructor is null pass no arguments to the class
$this->services[$id] = (null === $reflection->getConstructor()) ? $reflection->newInstance() : $reflection->newInstanceArgs($this->services[$id]['parameters']);
return $this->services[$id];
}
}