Created
December 16, 2010 16:25
-
-
Save mvaled/743599 to your computer and use it in GitHub Desktop.
A basic PHP IoC implementation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class ClaseX { | |
private static $info; | |
public static function RegistrarInformacion($info) { | |
echo "Me estan poniendo informacion $info \n"; | |
self::$info = $info; | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* container.php | |
* | |
* (c) 2010 Manuel Vázquez. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
*/ | |
// If this constant is false, the Cointaner will try to emulate | |
// the behavior of an unimplemented (but defined) contract. | |
// | |
// It is useful when developing. Set it to true when release-testing and | |
// in production. | |
if (!defined('CONTAINER_FORCE_METHODS')) | |
define('CONTAINER_FORCE_METHODS', false); | |
function getAttribute($node, $name) { | |
$attrs = $node->attributes(); | |
return (string)$attrs[$name]; | |
} | |
/** | |
* Returns the real path of file/dir given a context. | |
* | |
* @param string $path The path to be resolved. | |
* @param string $context A context to be prefixed to path before resolving. | |
*/ | |
function resolvepath($path, $context = '.') { | |
$context = trim($context); | |
if ($context === '.' or $context === '') { | |
return realpath($path); | |
} | |
else { | |
if ($context[strlen($context)-1] === '/') { | |
return realpath($context.$path); | |
} | |
else { | |
return realpath($context.'/'.$path); | |
} | |
} | |
} | |
class ContractViolation extends Exception { | |
} | |
class UnknownContractName extends Exception { | |
} | |
/** | |
* The IoC container. | |
* | |
* @see test2.php for usage example. | |
*/ | |
class Container { | |
public function __construct($filename, $context = '.') { | |
$contents = simplexml_load_file(resolvepath($filename, $context)); | |
foreach($contents->modulo as $which) { | |
$proxy = new ModuleProxy(realpath(getAttribute($which, 'src'))); | |
if(!isset($this->{$proxy->getname()})) { | |
$this->{$proxy->getname()} = $proxy; | |
} | |
else { | |
$this->{$proxy->getname()} = ModuleProxy::MergeProxies($this->{$proxy->getname()}, $proxy); | |
} | |
} | |
} | |
} | |
/** | |
* Represents the signature of a method in a contract | |
*/ | |
class Prototype { | |
public $params; | |
public $result; | |
public function __construct($params, $result) { | |
/* Los parámetros son un arreglo de tuplas: llave, valor | |
* | |
* XXX No usar arreglos asociativos, pues nosotros necesitamos | |
* el orden de los parámetros. | |
*/ | |
$this->params = $params; | |
$this->result = $result; | |
} | |
} | |
/** | |
* An unbounded method proxy. | |
* | |
* Represents an indirection to a method (implementation) within a | |
* contract. | |
* | |
* The contructor takes four arguments: | |
* | |
* $desc A string describing the method. TODO: PHPDoc? | |
* $controller The name of the class implementing the method. It | |
* should be a public static method. | |
* $action The name of the method. | |
* $prototype A Prototype instance describing the signature of the method. | |
* | |
*/ | |
class UnboundMethodProxy { | |
private $description = ''; | |
private $controller = ''; | |
private $action = ''; | |
private $prototype; | |
private $method = null; | |
public function __construct($desc, $controller, $action, $prototype) { | |
$this->description = $desc; | |
$this->controller = $controller; | |
$this->action = $action; | |
$this->prototype = $prototype; | |
} | |
private function InitMethod() { | |
$class = new ReflectionClass($this->controller); | |
try { | |
$this->method = $class->getMethod($this->action); | |
} catch (ReflectionException $exc) { | |
// El método aun no está implementado. | |
// Verificamos si debemos simular el método o relanzar la excepción | |
if (!CONTAINER_FORCE_METHODS) { | |
$type = ucfirst($this->prototype->result); | |
try { | |
$factory = new ReflectionClass("{$type}Factory"); | |
} catch(Exception $devnull) { | |
$factory = new ReflectionClass("ObjectFactory"); | |
} | |
$this->method = $factory->getMethod('getDefault'); | |
} | |
else | |
throw $exc; | |
} | |
} | |
/* | |
private function CheckArguments($arguments) { | |
if (!isset($this->method)) { | |
$this->InitMethod(); | |
} | |
$result = true; | |
$i = 0; | |
while($result and $which = each($arguments)) { | |
$typeincontract = $this->prototype->params[$i][1]; | |
// TODO: PHP es tan malito que no puedo obtener fiablemente el tipo | |
$i++; | |
} | |
return $result; | |
} | |
*/ | |
public function execute() { | |
$arguments = func_get_args(); | |
// TODO: CheckArguments? | |
if (!isset($this->method)) | |
$this->InitMethod(); | |
/* | |
eval("\$controller = new {$this->controller}();"); | |
*/ | |
return $this->method->invokeArgs(null, $arguments); | |
} | |
} | |
class VoidFactory { | |
public static function getDefault() { | |
return null; | |
} | |
} | |
class IntegerFactory { | |
public static function getDefault() { | |
return 0; | |
} | |
} | |
class StringFactory { | |
public static function getDefault() { | |
return ''; | |
} | |
} | |
class BooleanFactory { | |
public static function getDefault() { | |
return false; | |
} | |
} | |
class DoubleFactory { | |
public static function getDefault() { | |
return 0.0; | |
} | |
} | |
class ArrayFactory { | |
public static function getDefault() { | |
return array(); | |
} | |
} | |
class ObjectFactory { | |
public static function getDefault() { | |
return null; | |
} | |
} | |
/** | |
* Represents a module which. A module is basically a namespace | |
* for methods within a Container. | |
* | |
* You can define a module in several XML files and they will be merged. | |
*/ | |
class ModuleProxy { | |
private $name; | |
private $methods = array(); | |
public function getname() { | |
return $this->name; | |
} | |
public static function MergeProxies($pivot, $added) { | |
assert($pivot->name == $added->name); | |
$result = clone $pivot; | |
foreach($added->methods as $which => $what) { | |
$result->methods[$which] = $what; | |
} | |
return $result; | |
} | |
private static function ProcessPrototype($which) { | |
$params = array(); | |
foreach($which->parametro as $paramdef) { | |
$name = getAttribute($paramdef, 'nombre'); | |
$type = getAttribute($paramdef, 'tipo'); | |
$params[] = array($name, $type); | |
} | |
$resultype = getAttribute($which->resultado[0], 'tipo'); | |
$prototype = new Prototype($params, $resultype); | |
return $prototype; | |
} | |
public function __construct($filename, $context = '.') { | |
$contents = simplexml_load_file(resolvepath($filename, $context)); | |
$this->name = getAttribute($contents, 'nombre'); | |
foreach($contents->contrato as $which) | |
{ | |
$prototype = self::ProcessPrototype($which->prototipo[0]); | |
$desc = getAttribute($which->inyector[0], 'nombre'); | |
$controller = getAttribute($which->inyector[0], 'controlador'); | |
$action = getAttribute($which->inyector[0], 'accion'); | |
$methodproxy = new UnboundMethodProxy($desc, $controller, $action, $prototype); | |
$this->methods[getAttribute($which, 'nombre')] = $methodproxy; | |
} | |
} | |
private function __call($action, $args) { | |
if (isset($this->methods[$action])) { | |
$result = call_user_method_array("execute", $this->methods[$action], $args); | |
return $result; | |
} | |
else { | |
throw new UnknownContractName(sprintf('Contrato %s indefinido para el módulo %s', $action, $this->name)); | |
} | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<container> | |
<modulo src="moduloX.xml" /> | |
</container> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<modulo nombre="ModuloX"> | |
<contrato nombre="Uno"> | |
<inyector nombre="Gestor de Informacion" controlador="ClaseX" accion="RegistrarInformacion" /> | |
<prototipo> | |
<parametro nombre="Informacion" tipo="integer" /> | |
<resultado tipo="void" /> | |
</prototipo> | |
</contrato> | |
<contrato nombre="Unimpl"> | |
<inyector nombre="Gestor de Informacion" controlador="ClaseX" accion="Unimpl" /> | |
<prototipo> | |
<resultado tipo="integer" /> | |
</prototipo> | |
</contrato> | |
</modulo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require_once 'container.php'; | |
// So the IoC could find the classes | |
function __autoload($class_name) { | |
@include_once($class_name.".php"); | |
} | |
$container = new Container('container.xml'); | |
$container->ModuloX->Uno(55); | |
// The following sentence either return the default value for the result's | |
// type; or raises a ReflectionException. Check the CONTAINER_FORCE_METHODS constant. | |
echo $container->ModuloX->Unimpl(); | |
// The following sentence raises an UnknownContractName. Uncomment at will. | |
//$container->ModuloX->Yme(55); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment