Skip to content

Instantly share code, notes, and snippets.

@mattsah
Last active December 10, 2015 22:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattsah/4502774 to your computer and use it in GitHub Desktop.
Save mattsah/4502774 to your computer and use it in GitHub Desktop.
<?php namespace Dotink\Jest {
class Mime extends Jest
{
/**
*
*/
static private $parents = array();
/**
*
*/
static private $interfaces = array();
/**
*
*/
static private $traits = array();
/**
*
*/
private $class = NULL;
/**
*
*/
private $object = NULL;
/**
*
*/
static public function define($class)
{
return new self($class);
}
/**
*
*/
static public function create($class)
{
if (!class_exists($class)) {
self::make($class);
}
$class = '\\' . $class;
return new self(new $class());
}
/**
*
*/
static private function make($class)
{
$parent = isset(self::$parents[$class])
? self::$parents[$class]
: __NAMESPACE__ . '\Jest';
if ($parent && !class_exists($parent)) {
self::make($parent);
}
$interfaces = isset(self::$interfaces[$class])
? self::$interfaces[$class]
: array();
$traits = isset(self::$traits[$class])
? self::$traits[$class]
: array();
$ns_parts = explode('\\', $class);
$class = array_pop($ns_parts);
$ns = implode('\\', $ns_parts);
$parent = '\\' . $parent;
eval(call_user_func(function() use ($ns, $class, $parent, $interfaces, $traits) {
ob_start() ?>
namespace <?= $ns ?>
{
class <?= $class ?>
<?php if ($parent) { ?>
extends <?= $parent ?>
<?php } ?>
<?php if (count($interfaces)) { ?>
implements <?= implode(', ', $interfaces) ?>
<?php } ?>
{
}
}
<?php return ob_get_clean();
}));
}
/**
*
*/
public function __construct($target = NULL)
{
if (get_class($this) != __CLASS__) {
return;
}
if (is_object($target)) {
$this->class = get_class($target);
$this->object = $target;
self::$objects[$this->class] = $target;
} else {
$this->class = $target;
}
}
/**
*
*/
public function resolve()
{
return $this->object;
}
/**
*
*/
public function extending($parent_class)
{
self::$parents[$this->class] = $parent_class;
return self::define($parent_class);
}
/**
*
*/
public function implementing($interface)
{
if (class_exists($this->class)) {
throw new \Exception (
'Cannot redefine interfaces for existing class %s',
$this->class
);
}
foreach (func_get_args() as $interface) {
if (!interface_exists($interface)) {
eval(call_user_func(function() use ($interface) {
ob_start() ?>
Interface <?= $interface ?> {}
<?php return ob_get_clean();
}));
}
self::$interfaces[$this->class][] = '\\' . $interface;
}
return $this;
}
/**
*
*/
public function using($trait)
{
if (class_exists($this->class)) {
throw new \Exception (sprintf(
'Cannot redefine traits for existing class %s',
$this->class
));
}
foreach (func_get_args() as $trait) {
self::$traits[$this->class][] = $trait;
}
return $this;
}
/**
*
*/
public function onCall($method)
{
if ($this->object && ($this->object->openMethod || $this->object->openProperty)) {
throw new \Exception(sprintf(
'Cannot mimick call %s without first mimicking return for %s',
$method,
$this->object->openMethod ?: $this->object->openProperty
));
}
$this->object->methods[$method] = array();
$this->object->openMethod = $method;
return $this;
}
/**
*
*/
public function onGet($property)
{
if ($this->object && ($this->object->openMethod || $this->object->openProperty)) {
throw new \Exception(sprintf(
'Cannot mimick property %s without first mimicking return for %s',
$property,
$this->object->openMethod ?: $this->object->openProperty
));
}
$this->object->openProperty = $property;
return $this;
}
/**
*
*/
public function onNew()
{
$expectation = array_slice(func_get_args(), 0, -1);
$factory = array_slice(func_get_args(), -1)[0];
if (!isset(self::$factories[$this->class])) {
self::$factories[$this->class] = array();
}
self::$factories[$this->class][] = [
'expectation' => $expectation,
'factory' => $factory
];
return $this;
}
/**
*
*/
public function expect()
{
if (!$this->object->openMethod) {
throw new \Exception(sprintf(
'Cannot set argument expectations without first opening a call'
));
}
$this->object->expectation = func_get_args();
return $this;
}
/**
*
*/
public function give($value = NULL)
{
if (is_callable($value)) {
$value = call_user_func($value);
}
if ($this->object->openMethod) {
$this->object->methods[$this->object->openMethod][] = [
'expectation' => $this->object->expectation,
'value' => $value
];
$this->object->openMethod = FALSE;
$this->object->expectation = array();
} elseif ($this->object->openProperty) {
$this->object->properties[$this->object->openProperty] = $value;
$this->object->openProperty = FALSE;
}
return $this;
}
}
/**
*
*/
class Jest {
static protected $objects = array();
static protected $factories = array();
protected $expectation = array();
protected $methods = array();
protected $properties = array();
protected $openMethod = FALSE;
protected $openProperty = FALSE;
/**
*
*/
public function __construct()
{
$class = get_class($this);
$args = func_get_args();
if (isset(self::$factories[$class])) {
foreach (self::$factories[$class] as $jest) {
if ($args == $jest['expectation']) {
return call_user_func($jest['factory'], new Mime($this));
}
}
}
}
/**
*
*/
public function __call($method, $args)
{
if (isset($this->methods[$method])) {
foreach ($this->methods[$method] as $jest) {
if ($args == $jest['expectation']) {
return $jest['value'];
}
}
} else {
throw new \Exception(sprintf(
'The method %s was never mimicked',
$method
));
}
}
/**
*
*/
static public function __callStatic($method, $args)
{
$called_class = get_called_class();
$object = self::$objects[$called_class];
return call_user_func_array([$object, $method], $args);
}
/**
*
*/
public function __get($property)
{
return $this->properties[$property];
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment