<?php
/*
* Created on Mar 7, 2009
*
* author: David Palmer <hide@address.com>
* primary contributor: Craig Andrews
*
* This class basically represents a very light-weight inversion-of-control
* container so that we can easily map our actions to service classes, or
* using Spring's "bean" paradigm. This is a first pass, and does not support
* all of Spring's functionality
*/
class Container {
private $beans;
public $BEAN_CONTAINER = null;
public $context;
private $wqueue = array(); //wait/retry queue
function __construct() {
// lets invoke our beans so we have nice little object references
//Tools::flushCache();
$this->BEAN_CONTAINER = Tools::getCachedObject("bean-container");
if (! $this->BEAN_CONTAINER) {
$context = new SimpleXmlElement(file_get_contents(CONTEXT_XML));
$imported_contexts = array();
// lets see if we need to import anything else before we really get going
foreach ($context->import as $import) {
if (file_exists($import['resource'])) {
$icontext = new SimpleXmlElement(file_get_contents($import['resource']));
array_push($imported_contexts,$icontext);
} else {
throw new Exception ($import['resource'] . " was not found on import");
}
}
$imported_xml="";
foreach($imported_contexts as $ctx) {
foreach($ctx->bean as $lb) {
$xml = $lb->asXML();
$imported_xml .= "\n" . $xml;
}
}
$parent = $context->xpath('bean');
$parent_xml="";
foreach($parent as $bean) {
$parent_xml .= $bean->asXML();
}
$full_xml = $parent_xml . "\n" . $imported_xml;
$fcontext = simplexml_load_string("<beans>" . $full_xml . "</beans>");
foreach ($fcontext->bean as $fbean) {
$this->_processBean($fcontext,$fbean);
}
//TODO: uncomment this line - it's nice to not have caching for development
Tools::setCachedObject("bean-container",$this->BEAN_CONTAINER);
}
}
function getBean ($name) {
if (array_key_exists($name,$this->BEAN_CONTAINER)) {
$b = $this->BEAN_CONTAINER->$name;
if ($b instanceof ProxyFactory) {
// we need to do some executing before or after!!! we don't
// care about returning anything else just the instance
$interceptor = $b->getInterceptor();
try {
$interceptor->before($b->getTarget(),$b->getMethod(),null);
} catch (Exception $ex) {
throw $ex;
}
return $b->getTarget();
}
return $this->BEAN_CONTAINER->$name;
} else {
return null;
}
}
private function _processBean($context,$bean) {
$obj = "Initializing...";
if($bean['id'] && $this->BEAN_CONTAINER->$bean['id']){
return $this->BEAN_CONTAINER->$bean['id'];
}else{
// if we don't have an ID, lets make one
if (!$bean['id']) {
$bean['id'] = Tools::uuidSecure();
}
if($bean['id']){
$this->BEAN_CONTAINER->$bean['id'] = &$obj;
}
$class = $bean["class"];
if (!$class) {
throw new Exception ("bean ID: " . $bean['id'] . " seems to not exist");
}
$class_path = str_replace(".", "/",$class) . ".php";
$class_name = array_pop(explode("/",$class_path));
$class_name = array_shift(explode(".",$class_name));
/**
* TODO: This needs to be cleaned up and should probably use the classloader
* function in: libs/c_tools.php because if we ever add another classpath
* this would be quite broken
*/
if (! @include_once(CLASSPATH . "/" . $class_path)) {
include_once($class_path);
}
if ($bean['constructor']) {
$constructor = (string)$bean['constructor'];
// @TODO: This isn't done yet. This is to handle constructor args
if ($bean->constructor_arg) {
foreach($bean->constructor_arg as $arg) {
}
} else {
$obj = new $constructor();
}
} else {
$obj = new $class_name();
}
// now lets see if we have properties for this bean
if (isset($bean->property)) {
$obj = $this->_processProperties($context,$obj,$bean->property);
}
if(method_exists($obj,"afterPropertiesSet")){
$obj->afterPropertiesSet();
}
return $obj;
}
}
private function _processProperties ($context,$obj, $properties) {
$ref_obj = new ReflectionObject($obj);
foreach ($properties as $property) {
$prop_name = $property['name'];
$method_name = "set" . ucfirst($prop_name);
if (! $ref_obj->hasMethod($method_name)) {
throw new Exception("object does not have setter: $method_name");
}
$method = $ref_obj->getMethod($method_name);
$children = $property->children();
if(count($children)!=1){
throw new Exception("A Property tag must have exactly one element within it");
}
$value = $this->_materializeFromTag($context,$children[0]);
if ($value != "__queued__") {
$method->invoke($obj,$value);
}
}
return $obj;
}
private function _materializeFromTag($context,$tag){
$tag_name = $tag->getName();
if ($tag_name == "ref") {
if ($tag['bean']) {
$l_bean = $tag['bean'];
$id_ref = $context->xpath("//bean[@id='".$l_bean."']");
if (!empty($id_ref)) {
$bean_ref = $context->$l_bean;
$this->BEAN_CONTAINER->$l_bean = $this->_processBean($context,$id_ref[0]);
$value = $this->BEAN_CONTAINER->$l_bean;
} else {
throw new Exception("Referenced bean $l_bean not defined in this context.");
}
} else if ($tag['local']) {
// this property has a local bean reference
$l_bean = $tag['local'];
$id_ref = $context->xpath("//bean[@id='".$l_bean."']");
if (isset($id_ref)) {
$bean_ref = $context->$l_bean;
$this->BEAN_CONTAINER->$l_bean = $this->_processBean($context,$id_ref[0]);
$value = $this->BEAN_CONTAINER->$l_bean;
} else {
throw new Exception("Referenced bean $l_bean not defined in this context.");
}
}
} else if ($tag_name == "map") {
$value = array();
// @TODO: this needs to allow for beans to be values not just simple string values
foreach ($tag->entry as $entry) {
//$value[(string)$entry['key']] = $this->_materializeValue((string)$entry->value);
$value[(string)$entry['key']] = $this->_materializeFromTag($context,$entry);
}
} else if ($tag_name == "list") {
$value = array();
$count = 0;
foreach($tag->xpath("*") as $listItem) {
$count++;
$value[$count] = $this->_materializeFromTag($context,$listItem);
}
} else if ($tag_name == "bean") {
$value = $this->_processBean($context,$tag);
} else if ($tag_name == "value") {
$value = $this->_materializeValue((string) $tag);
}else{
throw new Exception("Unknown tag: $tag_name");
}
return $value;
}
private function _materializeValue($value) {
$placeholders = array();
foreach ($this->BEAN_CONTAINER as $bean) {
if ($bean instanceof PropertyPlaceholderConfigurer) {
$placeholders = $bean->params;
}
}
$pattern = '/\$\{(.*)\}/';
if (preg_match($pattern,$value,$matches)) {
$m_count = count($matches);
for ($i=1; $i<$m_count; $i++) {
$key = $matches[$i];
if ($placeholders[$key]) {
$value = preg_replace($pattern,$placeholders[$key],$value);
}
}
return $value;
} else {
return $value;
}
}
}
?>