<?php
namespace Injector;
use Minime\Annotations\Facade;
/**
* Stores a class profile
* @author emaphp
* @package Injector
*/
class ClassProfile {
/**
* Default annotation namespace
* @var string
*/
const NS = 'inject';
/**
* Reflection class
* @var \ReflectionClass
*/
public $class;
/**
* Class constructor
* @var \ReflectionMethod
*/
public $constructor;
/**
* Class name
* @var string
*/
public $className;
/**
* Service providers
* @var array
*/
public $providers;
/**
* Constructor injection values
* @var array
*/
public $constructorParams = array();
/**
* Injection properties
* @var array
*/
public $properties = array();
/**
* Reflection properties array
* @var array
*/
public $reflectionProperties = array();
/**
* Indicates if all dependecies must be fullfiled
* When true, a RuntimeException is thrown if a dependency is not declared within the container
* @var boolean
*/
public $isStrict = false;
/**
* Creates a new class profile
* @param string $className
* @throws \RuntimeException
*/
public function __construct($className) {
$this->className = $className;
$this->initialize();
}
/**
* Obtains all providers for a given class
* @param \ReflectionClass $class
* @return array
*/
protected function getClassProviders(\ReflectionClass $class) {
$annotations = Facade::getAnnotations($class);
$values = $annotations->useNamespace(self::NS)->export();
if (array_key_exists('provider', $values)) {
return is_array($values['provider']) ? $values['provider'] : [$values['provider']];
}
else {
//try getting providers from parent class
$extend = array_key_exists('extend', $values) ? (bool) $values['extend'] : true;
if ($extend) {
$parent = $class->getParentClass();
return $parent instanceof \ReflectionClass ? $this->getClassProviders($parent) : [];
}
}
}
/**
* Initializes a class profile instance
* @throws \RuntimeException
*/
protected function initialize() {
$this->class = new \ReflectionClass($this->className);
//parse class annotations
$annotations = Facade::getAnnotations($this->class);
$values = $annotations->useNamespace(self::NS)->export();
//get default providers
$this->providers = $this->getClassProviders($this->class);
//is strict?
if (array_key_exists('strict', $values)) {
$this->isStrict = is_array($values['strict']) ? array_shift($values['strict']) : (bool) $values['strict'];
}
//parse properties
$properties = $this->class->getProperties();
foreach ($properties as $property) {
$annotations = Facade::getAnnotations($property);
$values = $annotations->useNamespace(self::NS)->export();
if (array_key_exists('service', $values)) {
//store service id
$propertyName = $property->getName();
$this->properties[$propertyName] = is_array($values['service']) ? array_shift($values['service']) : $values['service'];
//store ReflectionProperty instance and make it accesible
$this->reflectionProperties[$propertyName] = $property;
$this->reflectionProperties[$propertyName]->setAccessible(true);
}
}
//parse constructor
$this->constructor = $this->class->getConstructor();
if (!is_null($this->constructor)) {
$annotations = Facade::getAnnotations($this->constructor);
$values = $annotations->useNamespace(self::NS)->export();
if (array_key_exists('param', $values)) {
if (is_array($values['param'])) {
foreach ($values['param'] as $arg) {
list($argname, $argid) = $this->parseParameter($arg);
if ($argname) {
$this->constructorParams[$argname] = $argid;
}
}
}
else {
list($argname, $argid) = $this->parseParameter($values['param']);
if ($argname) {
$this->constructorParams[$argname] = $argid;
}
}
}
}
}
/**
* Parses an injected argument expression
* @param string $str
* @return array
* @throws \RuntimeException
*/
protected function parseParameter($str) {
$regex = '/(?:\s*)?\$?(\w+)(?:\s+)(\w+)(?:\s*)?$/';
if (preg_match($regex, $str, $matches)) {
return [$matches[1], $matches[2]];
}
throw new \RuntimeException(sprintf("Invalid expression found in %s __construct method annotation", $this->className));
}
}
|