Last active
September 29, 2018 18:25
-
-
Save decima/02020f7cf71e32da12def756ac1d6ab3 to your computer and use it in GitHub Desktop.
First try to create generic types in php :3
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 | |
// This is the hidden part ;-) | |
trait Generic | |
{ | |
private $_T = null; | |
private $__generic_nullable = false; | |
private $__originalObject = null; | |
/** | |
* @param $generic_types | |
* @return self | |
*/ | |
public function _T($generic_type, $nullable = false) | |
{ | |
$this->_T = $generic_type; | |
$this->__generic_nullable = $nullable; | |
return new class($this->_T, $this, $nullable) | |
{ | |
use Generic; | |
private $_childT; | |
private $_childObject; | |
private $_childNullable; | |
public function __construct($__type, &$__parent, $__nullable) | |
{ | |
$this->_childT = $__type; | |
$this->_childObject = $__parent; | |
$this->_childNullable = $__nullable; | |
} | |
public function __get($name) | |
{ | |
return $this->_childObject->$name; | |
} | |
public function __set($name, $value) | |
{ | |
return $this->_childObject->$name = $value; | |
} | |
public function __isset($name) | |
{ | |
return isset($this->_childObject->$name); | |
} | |
public function __unset($name) | |
{ | |
unset($this->_childObject->$name); | |
} | |
public function __toString() | |
{ | |
return (string)$this->_childObject; | |
} | |
public function __invoke(...$args) | |
{ | |
return $this->__call("__invoke", $args); | |
} | |
/** | |
* @param $name | |
* @param $arguments | |
* @return {$this->_childT}|null | |
*/ | |
public function __call($name, $arguments) | |
{ | |
$name = "T_" . $name; | |
$reflectionObject = new \ReflectionObject($this->_childObject); | |
$returnType = $reflectionObject->getMethod($name)->getReturnType(); | |
$classImplements = array_merge([$this->_childT], class_implements($this->_childT)); | |
$returnTypeClass = $returnType->getName(); | |
if (in_array($returnTypeClass, array_values($classImplements))) { | |
if (method_exists($this->_childObject, $name)) { | |
$response = call_user_func_array([$this->_childObject, $name], array_merge([$this->_childT], $arguments)); | |
if (is_object($response) && ($given = get_class($response)) !== ($expected = $this->_childT)) { | |
throw new \Exception("expected $expected as return, $given got."); | |
} | |
if ($response === null && !$this->_childNullable) { | |
throw new \Exception("unexpected null value."); | |
} | |
return $response; | |
} | |
} | |
throw new \Exception("return type invalid"); | |
} | |
}; | |
} | |
} | |
//this is an example of usage :-) | |
interface Food | |
{ | |
public static function requires(): array; | |
} | |
class Cookie implements Food | |
{ | |
public static function requires(): array | |
{ | |
return ["chocolate", "butter"]; | |
} | |
} | |
class CookedFish implements Food | |
{ | |
public static function requires(): array | |
{ | |
return ["rawfish"]; | |
} | |
} | |
class Cooker | |
{ | |
use Generic; | |
private $ingredients = ["rawfish" => 10, "chocolate" => 5, "butter" => 4]; | |
public function T_cook($T, $useHoven = false): ?Food | |
{ | |
foreach (($T)::requires() as $neededIngredient) { | |
if (!isset($this->ingredients[$neededIngredient]) || $this->ingredients[$neededIngredient] < 1) { | |
return null; | |
} else { | |
$this->ingredients[$neededIngredient]--; | |
} | |
} | |
return (new $T()); | |
} | |
public function getIngredients() | |
{ | |
return $this->ingredients; | |
} | |
} | |
$cooker = new Cooker(); | |
$cookie = $cooker->_T(Cookie::class, true)->cook(true); | |
$rawfish = $cooker->_T(CookedFish::class, true)->cook(true); | |
var_dump($cookie); | |
var_dump($rawfish); | |
var_dump($cooker->getIngredients()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment