Skip to content

Commit

Permalink
Drop Nette/DI dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
Spamercz committed Feb 27, 2019
1 parent 4b33012 commit 44a9d8a
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 41 deletions.
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
}
],
"require": {
"mockery/mockery": "^1.0",
"nette/di": "2.3.*"
"mockery/mockery": "^1.0"
},
"require-dev": {
"nette/tester": "2.1.0"
Expand Down
84 changes: 45 additions & 39 deletions src/Spamer/DependencyMocker/Mocker.php
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
<?php
<?php declare(strict_types = 1);

namespace Spamer\DependencyMocker;

use Nette;
use Mockery;

class Mocker
{
/** @var array */
/**
* @var array
*/
public static $bannedClasses = [];

/** @var \ReflectionClass */
/**
* @var \ReflectionClass
*/
private static $reflectedClass;

/** @var Mockery\Mock */
private static $mockedClass;

/**
* @param string $className
* @return \ReflectionClass|\stdClass
* @var \Mockery\Mock
*/
public static function mockClassDependencies($className)
private static $mockedClass;


public static function mockClassDependencies(
string $className
) : \Mockery\MockInterface
{
self::$reflectedClass = new \ReflectionClass($className);
self::$mockedClass = Mockery::mock($className);
self::$mockedClass = \Mockery::mock($className);

self::mockInjectedMethods($className);
self::mockInjectedProperties();
Expand All @@ -33,32 +36,31 @@ public static function mockClassDependencies($className)
}


/**
* @param string $className
*/
private static function mockInjectedMethods($className)
private static function mockInjectedMethods(
string $className
) : void
{
foreach (self::$reflectedClass->getMethods() as $method) {
if (substr($method->getName(), 0, 6) === 'inject') {
if (\strpos($method->getName(), 'inject') === 0) {
self::mockDependenciesFromMethod($className, $method->getName());
}
}
}


private static function mockInjectedProperties()
private static function mockInjectedProperties() : void
{
/** @var \ReflectionProperty $property */
foreach (self::$reflectedClass->getProperties() as $property) {
if (
Nette\DI\PhpReflection::parseAnnotation($property, 'inject') !== NULL
ReflectionHelper::parseAnnotation($property, 'inject') !== NULL
||
Nette\DI\PhpReflection::parseAnnotation($property, 'autowire') !== NULL
ReflectionHelper::parseAnnotation($property, 'autowire') !== NULL
) {
if ($mockedParameterClass = Nette\DI\PhpReflection::parseAnnotation($property, 'var')) {
$mockedParameterClass = Nette\DI\PhpReflection::expandClassName(
if ($mockedParameterClass = ReflectionHelper::parseAnnotation($property, 'var')) {
$mockedParameterClass = ReflectionHelper::expandClassName(
$mockedParameterClass,
Nette\DI\PhpReflection::getDeclaringClass($property)
ReflectionHelper::getDeclaringClass($property)
);
}
self::setProperty($mockedParameterClass, $property);
Expand All @@ -67,22 +69,18 @@ private static function mockInjectedProperties()
}


/**
* @param string $className
*/
private static function mockConstructorDependencies($className)
private static function mockConstructorDependencies(string $className) : void
{
if (method_exists($className, '__construct')) {
self::mockDependenciesFromMethod($className, '__construct');
}
}


/**
* @param string $className
* @param string $methodName
*/
private static function mockDependenciesFromMethod($className, $methodName)
private static function mockDependenciesFromMethod(
string $className,
string $methodName
) : void
{
$reflectionMethod = new \ReflectionMethod($className, $methodName);
$parameters = $reflectionMethod->getParameters();
Expand All @@ -97,10 +95,9 @@ private static function mockDependenciesFromMethod($className, $methodName)
}


/**
* @param array $bannedClasses
*/
public static function setBannedClasses($bannedClasses)
public static function setBannedClasses(
array $bannedClasses
) : void
{
self::$bannedClasses = $bannedClasses;
}
Expand All @@ -109,11 +106,18 @@ public static function setBannedClasses($bannedClasses)
/**
* @param string $className
* @param \ReflectionParameter|\ReflectionProperty $class
* @throws \ReflectionException
*/
private static function setProperty($className, $class)
private static function setProperty(
string $className,
$class
) : void
{
if ( ! in_array($className, self::$bannedClasses) && $class->getDeclaringClass()->hasProperty($class->getName())) {
$mockedParameter = Mockery::mock($className);
if (
! in_array($className, self::$bannedClasses, TRUE)
&& $class->getDeclaringClass()->hasProperty($class->getName())
) {
$mockedParameter = \Mockery::mock($className);
$property = new \ReflectionProperty($class->getDeclaringClass()->getName(), $class->getName());
$property->setAccessible(TRUE);
$property->setValue(self::$mockedClass, $mockedParameter);
Expand All @@ -126,6 +130,7 @@ private static function setProperty($className, $class)
* @param string $property
* @param object $object
* @return mixed
* @throws \ReflectionException
*/
public static function getProperty($class, $property, $object)
{
Expand All @@ -142,6 +147,7 @@ public static function getProperty($class, $property, $object)
* @param string $method
* @param array $arguments
* @return mixed
* @throws \ReflectionException
*/
public static function callPrivateFunction($object, $method, $arguments = [])
{
Expand Down
183 changes: 183 additions & 0 deletions src/Spamer/DependencyMocker/ReflectionHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php declare(strict_types = 1);

namespace Spamer\DependencyMocker;


class ReflectionHelper
{

/**
* @var array
*/
private static $cache;

/**
* @var array
*/
private static $builtinTypes = [
'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1,
'callable' => 1, 'iterable' => 1, 'void' => 1,
];


public static function parseAnnotation(
\ReflectionProperty $ref,
string $name
) : string
{
if (preg_match("#[\\s*]@$name(?:\\s++([^@]\\S*)?|$)#", trim($ref->getDocComment(), '/*'), $m)) {
return $m[1] ?? '';
}

return '';
}



public static function getDeclaringClass(
\ReflectionProperty $prop
) : \ReflectionClass
{
foreach ($prop->getDeclaringClass()->getTraits() as $trait) {
if ($trait->hasProperty($prop->getName())) {
return self::getDeclaringClass($trait->getProperty($prop->getName()));
}
}

return $prop->getDeclaringClass();
}


public static function expandClassName(
string $name,
\ReflectionClass $rc
) : string
{
$lower = strtolower($name);

if (self::isBuiltinType($lower)) {
return $lower;

} elseif ($lower === 'self' || $lower === 'static' || $lower === '$this') {
return $rc->getName();

} elseif ($name[0] === '\\') { // fully qualified name
return ltrim($name, '\\');
}

$uses = & self::$cache[$rc->getName()];
if ($uses === NULL) {
self::$cache = self::parseUseStatements(file_get_contents($rc->getFileName()), $rc->getName()) + self::$cache;
$uses = & self::$cache[$rc->getName()];
}
$parts = explode('\\', $name, 2);
if (isset($uses[$parts[0]])) {
$parts[0] = $uses[$parts[0]];
return implode('\\', $parts);

} elseif ($rc->inNamespace()) {
return $rc->getNamespaceName() . '\\' . $name;

} else {
return $name;
}
}


public static function isBuiltinType(
string $type
) : bool
{
return isset(self::$builtinTypes[strtolower($type)]);
}


private static function parseUseStatements($code, $forClass = null)
{
$tokens = PHP_VERSION_ID >= 70000 ? token_get_all($code, TOKEN_PARSE) : token_get_all($code);
$namespace = $class = $classLevel = $level = null;
$res = $uses = [];

while ($token = current($tokens)) {
next($tokens);
switch (is_array($token) ? $token[0] : $token) {
case T_NAMESPACE:
$namespace = ltrim(self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]) . '\\', '\\');
$uses = [];
break;

case T_CLASS:
case T_INTERFACE:
case T_TRAIT:
if ($name = self::fetch($tokens, T_STRING)) {
$class = $namespace . $name;
$classLevel = $level + 1;
$res[$class] = $uses;
if ($class === $forClass) {
return $res;
}
}
break;

case T_USE:
while (!$class && ($name = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]))) {
$name = ltrim($name, '\\');
if (self::fetch($tokens, '{')) {
while ($suffix = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR])) {
if (self::fetch($tokens, T_AS)) {
$uses[self::fetch($tokens, T_STRING)] = $name . $suffix;
} else {
$tmp = explode('\\', $suffix);
$uses[end($tmp)] = $name . $suffix;
}
if (!self::fetch($tokens, ',')) {
break;
}
}

} elseif (self::fetch($tokens, T_AS)) {
$uses[self::fetch($tokens, T_STRING)] = $name;

} else {
$tmp = explode('\\', $name);
$uses[end($tmp)] = $name;
}
if (!self::fetch($tokens, ',')) {
break;
}
}
break;

case T_CURLY_OPEN:
case T_DOLLAR_OPEN_CURLY_BRACES:
case '{':
$level++;
break;

case '}':
if ($level === $classLevel) {
$class = $classLevel = null;
}
$level--;
}
}

return $res;
}


private static function fetch(&$tokens, $take)
{
$res = null;
while ($token = current($tokens)) {
[$token, $s] = is_array($token) ? $token : [$token, $token];
if (in_array($token, (array) $take, true)) {
$res .= $s;
} elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], true)) {
break;
}
next($tokens);
}
return $res;
}
}

0 comments on commit 44a9d8a

Please sign in to comment.