Permalink
Browse files

Provider fix. Separation of Reflection Cache and Services.

  • Loading branch information...
1 parent 93b4659 commit e9b98c56f2d6a798720287e9bc6d0fd148bfdeb6 @Evoke-PHP committed Apr 28, 2012
Showing with 347 additions and 71 deletions.
  1. +69 −0 php/src/Evoke/Cache.php
  2. +35 −0 php/src/Evoke/Iface/Cache.php
  3. +51 −0 php/src/Evoke/Iface/Services.php
  4. +63 −71 php/src/Evoke/Provider.php
  5. +129 −0 php/src/Evoke/Services.php
View
@@ -0,0 +1,69 @@
+<?php
+namespace Evoke;
+
+class Cache implements Iface\Cache
+{
+ /** @property $items
+ * @array Collection of cache entries.
+ */
+ protected $items = array();
+
+ /******************/
+ /* Public Methods */
+ /******************/
+
+ /** Clear an item from the cache.
+ * @param key @string The key from the cache to clear.
+ */
+ public function clear($key)
+ {
+ unset($this->items[$key]);
+ }
+
+ /// Clear all items from the cache.
+ public function clearAll()
+ {
+ $this->items = array();
+ }
+
+ /** Check whether the key exists in the cache (the value could still be
+ * NULL).
+ * @param key @string The key of the item to check for existance.
+ * @return @bool Whether the key exists in the cache.
+ */
+ public function exists($key)
+ {
+ // We can store NULL in the cache, so we must use array_key_exists.
+ return array_key_exists($key, $item);
+ }
+
+ /** Get an item from the cache (or if none has been defined return NULL).
+ * @param key @string The key for the item to get.
+ * @return @mixed The value of the item.
+ * @throws DomainException when the key does not exist.
+ */
+ public function get($key)
+ {
+ // We can store (and return) NULL using the cache so we must use
+ // array_key_exists.
+ if (!array_key_exists($key, $item))
+ {
+ throw new \DomainException(
+ __METHOD__ . ' key: ' . $key . ' does not exist. You must ' .
+ 'check for values before you get them.');
+ }
+
+ return $item[$key];
+ }
+
+ /** Set an item in the cache. If the value already exists it will be
+ * overwritten.
+ * @param key @string The key for the item to set.
+ * @param value @mixed The value to set in the cache.
+ */
+ public function set($key, $value)
+ {
+ $this->items[$key] = $value;
+ }
+}
+// EOF
@@ -0,0 +1,35 @@
+<?php
+namespace Evoke\Iface;
+
+interface Cache
+{
+ /** Clear an item from the cache.
+ * @param key @string The key from the cache to clear.
+ */
+ public function clear($key);
+
+ /// Clear all items from the cache.
+ public function clearAll();
+
+ /** Check whether the key exists in the cache (the value could still be
+ * NULL).
+ * @param key @string The key of the item to check for existance.
+ * @return @bool Whether the key exists in the cache.
+ */
+ public function exists($key);
+
+ /** Get an item from the cache (or if none has been defined return NULL).
+ * @param key @string The key for the item to get.
+ * @return @mixed The value of the item.
+ * @throws DomainException when the key does not exist.
+ */
+ public function get($key);
+
+ /** Set an item in the cache. If the value already exists it will be
+ * overwritten.
+ * @param key @string The key for the item to set.
+ * @param value @mixed The value to set in the cache.
+ */
+ public function set($key, $value);
+}
+// EOF
@@ -0,0 +1,51 @@
+<?php
+namespace Evoke\Iface;
+
+interface Services
+{
+ /** Check whether there is a service object cached for the specified class
+ * and parameter combination.
+ * @param name @string The name of the service (class or interface).
+ * @param params @array The construction parameters of the service.
+ * @returns @bool Whether the service object exists in the cache.
+ */
+ public function exists($name, Array $params);
+
+ /** Get the service object cached for the specified class and parameter
+ * combination. A check must already have been made using isService before
+ * calling this.
+ * @param name @string The name of the service (class or interface).
+ * @param params @array The construction parameters of the service.
+ * @returns @object The service object.
+ * @throws DomainException If the service cannot be retrieved.
+ */
+ public function get($name, $params);
+
+ /** Check whether the named service has been registered.
+ * @param name @string The name of the service (class or interface).
+ * @returns @bool Whether the name is registered as a service.
+ */
+ public function isService($name);
+
+ /** Register the name as a service.
+ * @param name @string The name of the service (class or interface).
+ */
+ public function register($name);
+
+ /** Set the service object for the named service with the specified
+ * parameters.
+ * @param name @string The name of the service (class or interface).
+ * @param params @array The construction parameters of the service.
+ * @param object @object The object to be set as the service instance.
+ * @throws DomainException If the named service is not a registered
+ * service.
+ * @throws OverflowException If the service has already been set.
+ */
+ public function set($name, Array $params, $object);
+
+ /** Unregister the name as a service (clearing all cached service objects
+ * for the named service.
+ */
+ public function unregister($name);
+}
+// EOF
View
@@ -113,31 +113,34 @@
class Provider implements Iface\Provider
{
/** @property $interfaceRouter
- * @object interfaceRouter
+ * @object Interface Router
*/
protected $interfaceRouter;
- /** @property $reflections
- * @array The cached class and parameter reflections for classes.
+ /** @property $reflectionCache
+ * @object Reflection Cache
*/
- protected static $reflections = array();
+ protected $reflectionCache;
- /** @property $shared
- * @array The store of shared classes (grouped by class and parameters).
- * If the same request to make an object of the class is received with the
- * same parameters then the stored shared class shall be returned.
+ /** @property $services
+ * @object Services
*/
- protected static $shared = array();
+ protected $services;
/** Construct a Provider object.
- * @param $interfaceRouter @object InterfaceRouter
+ * @param reflectionCache @object Reflection Cache
+ * @param interfaceRouter @object Interface Router
+ * @param services @object Services
*/
- public function __construct(Iface\Provider\Iface\Router $interfaceRouter)
+ public function __construct(Iface\Cache $reflectionCache,
+ Iface\Provider\Iface\Router $interfaceRouter,
+ Iface\Services $services)
{
$this->interfaceRouter = $interfaceRouter;
+ $this->reflectionCache = $reflectionCache;
+ $this->services = $services;
}
-
/******************/
/* Public Methods */
/******************/
@@ -149,7 +152,7 @@ public function __construct(Iface\Provider\Iface\Router $interfaceRouter)
* This makes it easy to test your code as it is not tightly bound to the
* objects that it depends on.
*
- * @param className @string Classname, including namespace.
+ * @param classname @string Classname, including namespace.
*
* @param params @array Construction parameters. Only the parameters
* that cannot be lazy loaded (scalars with no default or interfaces that
@@ -158,76 +161,45 @@ public function __construct(Iface\Provider\Iface\Router $interfaceRouter)
*
* @return The object that has been created.
*/
- public function make($className, Array $params=array())
+ public function make($classname, Array $params=array())
{
$passedParameters = $this->pascalToCamel($params);
-
- // Is this class a shared service.
- if (isset(self::$shared[$className]))
+ $isService = $this->services->isService($classname);
+
+ if ($isService &&
+ $this->services->exists($classname, $passedParameters))
{
- foreach (self::$shared[$className] as $sharedEntry)
- {
- if ($sharedEntry['Params'] === $params)
- {
- return $sharedEntry['Object'];
- }
- }
+ return $this->services->get($classname, $passedParameters);
}
-
- // Reflect the class if we haven't already done so.
- if (!isset(self::$reflections[$className]))
- {
- $this->reflect($className);
- }
+ $reflection = $this->getReflection($classname);
+
// If there is no possibility of passing parameters to the code then
// just instantiate the object and return it.
- if (!isset(self::$reflections[$className]['Params']))
+ if (!isset($reflection['Params']))
{
- return new $className;
+ return new $classname;
}
- // Use the passed parameters, falling back on the reflected parameters
- // for automatic lazy injection to create all of the dependencies.
+ // Calculate the dependencies for the object.
$deps = array();
- foreach (self::$reflections[$className]['Params'] as $reflectionParam)
+ foreach ($reflection['Params'] as $reflectionParam)
{
$deps[] = $this->getDependency($reflectionParam, $passedParameters);
}
- $obj = self::$reflections[$className]['Class']->newInstanceArgs($deps);
+ // Create the object.
+ $object = $reflection['Class']->newInstanceArgs($deps);
- // If this is a shared class then we are creating it for the first time.
- if (isset(self::$shared[$className]))
+ // If this is a service class, then we have just created it for the
+ // first time. Set it as the service instance.
+ if ($isService)
{
- self::$shared[$className][] = array('Object' => $obj,
- 'Params' => $passedParameters);
+ $this->services->set($classname, $passedParameters, $object);
}
- return $obj;
- }
-
- /** Set the specified class to be shared by the Provider. The make method
- * will return a shared object for this class while the class remains
- * shared.
- * @param className @string Classname (including namespace).
- */
- public function share($className)
- {
- if (!isset(self::$shared[$className]))
- {
- self::$shared[$className] = array();
- }
- }
-
- /** Stop the class from being shared by the Provider, forcing a new object
- * to be created for the class each time it is made using make.
- * @param className @string The classname to unshare.
- */
- public function unshare($className)
- {
- unset(self::$shared[$className]);
+ return $object;
}
/*********************/
@@ -259,6 +231,10 @@ protected function getDependency(\ReflectionParameter $reflectionParam,
if (!isset($depClass))
{
+ /** \todo Investiage, should an exception be throw here? It is
+ * likely to be an unset required scalar, so should we bail hard
+ * and early?
+ */
// Use NULL for something that we cannot reflect upon to get the
// required details to do an automatic injection.
return NULL;
@@ -292,18 +268,34 @@ protected function getDependency(\ReflectionParameter $reflectionParam,
return NULL;
}
- /** Reflect the class, storing it in the reflections array.
- * @param className @string The full classname (including the namespace).
+ /** Get the reflection for the class.
+ * @param classname @string The full classname (including the namespace).
+ * @return @mixed NULL if no reflection could be made, or an array of the
+ * format:
+ * @code
+ * array('Class' => $reflectionClass,
+ * 'Params' => $reflectionParams);
+ * @endcode
*/
- protected function reflect($className)
+ protected function getReflection($classname)
{
- $reflectionClass = new \ReflectionClass($className);
+ // Get the reflection using the cache if possible.
+ if ($this->reflectionCache->exists($classname))
+ {
+ return $this->reflectionCache->get($classname);
+ }
+
+ // Build the reflection.
+ $reflectionClass = new \ReflectionClass($classname);
$constructor = $reflectionClass->getConstructor();
- $reflectionParams =
- (isset($constructor) ? $constructor->getParameters() : NULL);
+ $reflectionParams = isset($constructor) ?
+ $constructor->getParameters() : NULL;
+ $reflection = array('Class' => $reflectionClass,
+ 'Params' => $reflectionParams);
- self::$reflections[$className] = array('Class' => $reflectionClass,
- 'Params' => $reflectionParams);
+ $this->reflectionCache->set($classname, $reflection);
+
+ return $reflection;
}
/*******************/
Oops, something went wrong.

0 comments on commit e9b98c5

Please sign in to comment.