Skip to content

Commit

Permalink
Add support for PHP 8.0
Browse files Browse the repository at this point in the history
Set min PHP version to 7.4
  • Loading branch information
mdio committed May 10, 2022
1 parent 9a386c0 commit 2e4cb81
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 189 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
tests:
strategy:
matrix:
php-versions: ['7.3', '7.4']
php-versions: ['7.4', '8.0']

runs-on: ubuntu-latest

Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,20 @@ class GenerateDependencyInjectionFactories extends \Symfony\Component\Console\Co

require_once $fullpath;

$astLocator = (new \Roave\BetterReflection\BetterReflection())->astLocator();

// for roave/better-reflection 4.x (PHP 7.4)
$reflector = new \Roave\BetterReflection\Reflector\ClassReflector(new Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator($fileName, $astLocator));
$classes = $reflector->getAllClasses();

// for roave/better-reflection 5.x (PHP 8.0)
$reflector = new \Roave\BetterReflection\Reflector\DefaultReflector(new Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator($fileName, $astLocator));
$classes = $reflector->reflectAllClasses();

foreach ($classes as $class) {
$generator->processFileForClass($class->getName());
}

$fileReflection = new \Laminas\Code\Reflection\FileReflection($fullpath);
$classes = $fileReflection->getClasses();
foreach ($classes as $class) {
Expand Down
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
}
],
"require": {
"php": "^7.2",
"laminas/laminas-code": "^3.0.0",
"php": "^7.4 || ^8.0",
"doctrine/annotations": "^1.0.0",
"laminas/laminas-code": "^4.0.0",
"psr/log": "^1.0.0"
},
"require-dev": {
"composer/package-versions-deprecated": "^1.0",
"ocramius/proxy-manager": "^2.1.0",
"phpunit/phpunit": "^9.0.0",
"ocramius/proxy-manager": "^2.1.0"
"roave/better-reflection": "^4.0.0 || ^5.0.0"
},
"suggest": {
"ocramius/proxy-manager": "For lazy loading"
Expand Down
120 changes: 44 additions & 76 deletions src/rg/injektor/DependencyInjectionContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@
use ProxyManager\Proxy\LazyLoadingInterface;
use Psr\Log\LoggerInterface;
use ReflectionNamedType;
use ReflectionParameter;
use ReflectionProperty;
use rg\injektor\annotations\Named;
use UnexpectedValueException;
use function get_class;
use function method_exists;
use const PHP_VERSION_ID;

/**
Expand All @@ -26,61 +30,33 @@
*/
class DependencyInjectionContainer {

public static $CLASS = __CLASS__;
public static string $CLASS = __CLASS__;

/**
* @var Configuration
*/
protected $config;
protected Configuration $config;

/**
* @var array
*/
private $instances = [];
private array $instances = [];

/**
* @var DependencyInjectionContainer
*/
private static $defaultInstance;
private static ?DependencyInjectionContainer $defaultInstance = null;

/**
* @var SimpleAnnotationReader
*/
private $annotationReader;
private SimpleAnnotationReader $annotationReader;

/**
* iteration depth used for intelligent logging, makes it easier to detect circular iterations
*
* @var int
*/
private $iterationDepth = 0;
private int $iterationDepth = 0;

/**
* @var LoggerInterface
*/
private $logger;
private ?LoggerInterface $logger = null;

/**
* @var bool
*/
private $supportsLazyLoading = false;
private bool $supportsLazyLoading;

/**
* @var LazyLoadingValueHolderFactory|null
*/
private $lazyProxyFactory;
private ?LazyLoadingValueHolderFactory $lazyProxyFactory = null;

/**
* used for injection loop detection
*
* @var array
*/
protected $alreadyVisitedClasses = [];
protected array $alreadyVisitedClasses = [];

/**
* @param Configuration $config
*/
public function __construct(Configuration $config = null) {
public function __construct(?Configuration $config = null) {
$this->config = $config ? : new Configuration();

if (!self::$defaultInstance) {
Expand All @@ -92,44 +68,32 @@ public function __construct(Configuration $config = null) {
}

/**
* @static
* @throws InjectionException
* @return DependencyInjectionContainer
*/
public static function getDefaultInstance() {
public static function getDefaultInstance(): DependencyInjectionContainer {
if (self::$defaultInstance) {
return self::$defaultInstance;
}

throw new InjectionException('dependency injection container was not instantiated yet');
}

/**
* @param DependencyInjectionContainer $instance
*/
public static function setDefaultInstance(DependencyInjectionContainer $instance) {
self::$defaultInstance = $instance;
}

/**
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger) {
$this->logger = $logger;
}

/**
* @return Configuration
*/
public function getConfig() {
public function getConfig(): Configuration {
return $this->config;
}

/**
* @param string $className
* @throws InjectionLoopException
*/
protected function checkForInjectionLoop($className) {
protected function checkForInjectionLoop(string $className) {
if ($this->iterationDepth > 1000) {
throw new InjectionLoopException(
'Injection loop detected ' . $className . ' ' . $this->iterationDepth . PHP_EOL . print_r(
Expand Down Expand Up @@ -431,12 +395,12 @@ private function injectProperty($property, $instance) {
* @param string $fullClassName
* @return string
*/
public function getFullClassNameBecauseOfImports($property, $fullClassName) {
public function getFullClassNameBecauseOfImports(ReflectionProperty $property, string $fullClassName) {
// only process names which are not fully qualified, yet
// fully qualified names must start with a \
if ('\\' !== $fullClassName[0]) {
$parser = new PhpParser();
$useStatements = $parser->parseClass($property->getDeclaringClass());
$useStatements = $parser->parseUseStatements($property->getDeclaringClass());
if ($property->getDeclaringClass()->inNamespace()) {
$parentNamespace = $property->getDeclaringClass()->getNamespaceName();
}
Expand All @@ -456,17 +420,20 @@ public function getFullClassNameBecauseOfImports($property, $fullClassName) {
return trim($fullClassName, '\\');
}

/**
* @param ReflectionProperty $property
* @return string|null
*/
public function getClassFromProperty($property) {
public function getClassFromProperty(ReflectionProperty $property): ?string {
$fullClassName = $this->annotationReader->getClassFromVarTypeHint($property->getDocComment());

if (!$fullClassName && PHP_VERSION_ID >= 70400) {
$namedType = $property->getType();
if ($namedType instanceof ReflectionNamedType && $namedType->getName()) {
$fullClassName = '\\' . $namedType->getName();
if (!$fullClassName) {
$type = $property->getType();
if ($type instanceof ReflectionNamedType && $type->isBuiltin() === false && $type->getName()) {
$fullClassName = '\\' . $type->getName();
} elseif ($type !== null && method_exists($type, 'getTypes')) {
foreach ($type->getTypes() as $namedType) {
if ($namedType->isBuiltin() === false && $namedType->getName()) {
$fullClassName = '\\' . $namedType->getName();
}
break;
}
}
}

Expand Down Expand Up @@ -788,7 +755,7 @@ public function getMethodArguments(\ReflectionMethod $methodReflection, array $d

$isNumericDefaultArguments = !(bool) count(array_filter(array_keys($defaultArguments), 'is_string'));
foreach ($arguments as $key => $argument) {
/** @var \ReflectionParameter $argument */
/** @var ReflectionParameter $argument */
if ($isNumericDefaultArguments && array_key_exists($key, $defaultArguments)) {
$argumentValues[$argument->name] = $this->getValueOfDefaultArgument($defaultArguments[$key]);
} else if (array_key_exists($argument->name, $defaultArguments)) {
Expand Down Expand Up @@ -823,41 +790,42 @@ private function getValueOfDefaultArgument($argumentConfig) {
}

/**
* @param \ReflectionParameter $argument
* @param ReflectionParameter $argument
* @return object
* @throws InjectionException
* @throws InjectionException|UnexpectedValueException
*/
private function getInstanceOfArgument(\ReflectionParameter $argument) {
if (!$argument->getClass()) {
private function getInstanceOfArgument(ReflectionParameter $argument) {
$className = ReflectionClassHelper::getClassNameFromReflectionParameter($argument);
if (!$className) {
if ($argument->isOptional()) {
return $argument->getDefaultValue();
}
throw new InjectionException('Invalid argument without class typehint class: [' . $argument->getDeclaringClass()->name . '] method: [' . $argument->getDeclaringFunction()->name . '] argument: [' . $argument->name . ']');
}

$argumentClassConfig = $this->config->getClassConfig($argument->getClass()->name);
$argumentClassConfig = $this->config->getClassConfig($className);

$arguments = $this->getParamsFromTypeHint($argument);

$providedInstance = $this->getNamedProvidedInstance($argument->getClass()->name, $argumentClassConfig, $argument->getDeclaringFunction()->getDocComment(), $argument->name, $arguments);
$providedInstance = $this->getNamedProvidedInstance($className, $argumentClassConfig, $argument->getDeclaringFunction()->getDocComment(), $argument->name, $arguments);
if ($providedInstance) {
return $providedInstance;
}

$namedClassName = $this->getNamedClassOfArgument($argument->getClass()->name, $argument->getDeclaringFunction()->getDocComment(), $argument->name);
$namedClassName = $this->getNamedClassOfArgument($className, $argument->getDeclaringFunction()->getDocComment(), $argument->name);

if ($namedClassName) {
return $this->getInstanceOfClass($namedClassName, $arguments);
}

return $this->getInstanceOfClass($argument->getClass()->name, $arguments);
return $this->getInstanceOfClass($className, $arguments);
}

/**
* @param \ReflectionParameter $argument
* @param ReflectionParameter $argument
* @return array
*/
public function getParamsFromTypeHint(\ReflectionParameter $argument) {
public function getParamsFromTypeHint(ReflectionParameter $argument) {
return $this->annotationReader->getParamsFromTypeHint($argument->getDeclaringFunction()->getDocComment(), $argument->name, 'param');
}

Expand Down
40 changes: 40 additions & 0 deletions src/rg/injektor/ReflectionClassHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace rg\injektor;

use ReflectionNamedType;
use ReflectionParameter;
use UnexpectedValueException;
use function get_class;
use function method_exists;

/**
* @copyright ResearchGate GmbH
*/
class ReflectionClassHelper
{
/**
* @throws UnexpectedValueException
*/
public static function getClassNameFromReflectionParameter(ReflectionParameter $parameter): ?string
{
$reflectionType = $parameter->getType();
if ($reflectionType === null) {
return null;
} elseif ($reflectionType instanceof ReflectionNamedType) {
if ($reflectionType->isBuiltin() === false) {
return $reflectionType->getName();
}
} elseif (method_exists($reflectionType, 'getTypes')) {
foreach($reflectionType->getTypes() as $type) {
if ($type->isBuiltin() === false) {
return $type->getName();
}
}
} else {
throw new UnexpectedValueException('Unsupported Reflection type: ' . get_class($reflectionType));
}

return null;
}
}
6 changes: 3 additions & 3 deletions src/rg/injektor/SimpleAnnotationReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ public function getClassFromVarTypeHint($docComment) {
/**
* @param string $docComment
* @param string $tag
* @return string mixed
* @return string|null
*/
private function getClassFromTypeHint($docComment, $tag) {
private function getClassFromTypeHint($docComment, $tag): ?string {
$matches = array();
preg_match('/' . $tag . '\s([a-zA-Z0-9\_\\\\\[\\]]+)/', $docComment, $matches);
preg_match('/' . $tag . '\s([a-zA-Z0-9_\\\\\[\\]]+)/', $docComment, $matches);
if (isset($matches[1])) {
return $matches[1];
}
Expand Down
Loading

0 comments on commit 2e4cb81

Please sign in to comment.