Skip to content

Commit

Permalink
simplify privates accessor, not necessary to check type
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Aug 6, 2023
1 parent ce5a65a commit 18da34e
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
use PHPStan\Analyser\NodeScopeResolver;
use PHPStan\Analyser\ScopeContext;
use PHPStan\Node\UnreachableStatementNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use PHPStan\Type\TypeCombinator;
Expand Down Expand Up @@ -222,28 +221,20 @@ public function processNodes(

$traitScope = clone $mutatingScope;

$scopeContext = $this->privatesAccessor->getPrivatePropertyOfClass(
$traitScope,
self::CONTEXT,
ScopeContext::class
);
/** @var ScopeContext $scopeContext */
$scopeContext = $this->privatesAccessor->getPrivateProperty($traitScope, self::CONTEXT);

$traitContext = clone $scopeContext;

// before entering the class/trait again, we have to tell scope no class was set, otherwise it crashes
$this->privatesAccessor->setPrivatePropertyOfClass(
$this->privatesAccessor->setPrivateProperty(
$traitContext,
'classReflection',
$traitClassReflection,
ClassReflection::class
);
$this->privatesAccessor->setPrivatePropertyOfClass(
$traitScope,
self::CONTEXT,
$traitContext,
ScopeContext::class
);

$this->privatesAccessor->setPrivateProperty($traitScope, self::CONTEXT, $traitContext);

$node->setAttribute(AttributeKey::SCOPE, $traitScope);
$this->nodeScopeResolver->processNodes($node->stmts, $traitScope, $nodeCallback);
$this->decorateTraitAttrGroups($node, $traitScope);
Expand Down
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -677,3 +677,8 @@ parameters:
-
message: '#Public method "Rector\\Core\\DependencyInjection\\RectorContainerFactory\:\:createFromBootstrapConfigs\(\)" is never used#'
path: src/DependencyInjection/RectorContainerFactory.php

# will be more generic later
-
message: '#Parameters should use "Symfony\\Component\\Console\\Application\|string\|callable" types as the only types passed to this method#'
path: src/Util/Reflection/PrivatesAccessor.php
15 changes: 3 additions & 12 deletions rules/TypeDeclaration/TypeNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,9 @@ public function normalizeArrayTypeAndArrayNever(Type $type): Type
assert($traversedType instanceof ConstantArrayType);

// not sure why, but with direct new node everything gets nulled to MixedType
$this->privatesAccessor->setPrivatePropertyOfClass(
$traversedType,
'keyType',
new MixedType(),
Type::class
);
$this->privatesAccessor->setPrivatePropertyOfClass(
$traversedType,
'itemType',
new MixedType(),
Type::class
);
$this->privatesAccessor->setPrivateProperty($traversedType, 'keyType', new MixedType());

$this->privatesAccessor->setPrivateProperty($traversedType, 'itemType', new MixedType());

return $traversedType;
}
Expand Down
18 changes: 18 additions & 0 deletions src/DependencyInjection/LazyContainerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Rector\Caching\CacheFactory;
use Rector\Core\Configuration\Option;
use Rector\Core\Configuration\Parameter\SimpleParameterProvider;
use Rector\Core\Util\Reflection\PrivatesAccessor;
use Rector\NodeNameResolver\Contract\NodeNameResolverInterface;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
Expand All @@ -30,6 +31,7 @@
use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface;
use Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper;
use Rector\StaticTypeMapper\PhpDoc\PhpDocTypeMapper;
use Symfony\Component\Console\Application;

final class LazyContainerFactory
{
Expand Down Expand Up @@ -61,6 +63,22 @@ public function create(): Container
SimpleParameterProvider::setParameter(Option::CACHE_DIR, sys_get_temp_dir() . '/rector_cached_files');
SimpleParameterProvider::setParameter(Option::CONTAINER_CACHE_DIRECTORY, sys_get_temp_dir());

$container->singleton(Application::class, static function (): Application {
$application = new Application();

// @todo inject commands

$privatesAccessor = new PrivatesAccessor();
$privatesAccessor->propertyClosure($application, 'commands', function (array $commands): array {
unset($commands['completion']);
unset($commands['help']);

return $commands;
});

return $application;
});

$container->singleton(Inflector::class, static function (): Inflector {
$inflectorFactory = new InflectorFactory();
return $inflectorFactory->build();
Expand Down
11 changes: 0 additions & 11 deletions src/Exception/Reflection/InvalidPrivatePropertyTypeException.php

This file was deleted.

52 changes: 13 additions & 39 deletions src/Util/Reflection/PrivatesAccessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Rector\Core\Util\Reflection;

use Rector\Core\Exception\Reflection\InvalidPrivatePropertyTypeException;
use Rector\Core\Exception\Reflection\MissingPrivatePropertyException;
use ReflectionClass;
use ReflectionMethod;
Expand All @@ -15,6 +14,19 @@
*/
final class PrivatesAccessor
{
/**
* @param callable(mixed $value): mixed $closure
*/
public function propertyClosure(object $object, string $propertyName, callable $closure): void
{
$property = $this->getPrivateProperty($object, $propertyName);

// modify value
$property = $closure($property);

$this->setPrivateProperty($object, $propertyName, $property);
}

/**
* @param object|class-string $object
* @param mixed[] $arguments
Expand All @@ -32,23 +44,6 @@ public function callPrivateMethod(object|string $object, string $methodName, arr
return $reflectionMethod->invokeArgs($object, $arguments);
}

/**
* @template T of object
*
* @param class-string<T> $valueClassName
* @return T
*/
public function getPrivatePropertyOfClass(object $object, string $propertyName, string $valueClassName): object
{
$value = $this->getPrivateProperty($object, $propertyName);
if ($value instanceof $valueClassName) {
return $value;
}

$errorMessage = sprintf('The type "%s" is required, but "%s" type given', $valueClassName, $value::class);
throw new InvalidPrivatePropertyTypeException($errorMessage);
}

public function getPrivateProperty(object $object, string $propertyName): mixed
{
$reflectionProperty = $this->resolvePropertyReflection($object, $propertyName);
Expand All @@ -57,27 +52,6 @@ public function getPrivateProperty(object $object, string $propertyName): mixed
return $reflectionProperty->getValue($object);
}

/**
* @template T of object
*
* @param class-string<T> $valueClassName
* @param T $value
*/
public function setPrivatePropertyOfClass(
object $object,
string $propertyName,
mixed $value,
string $valueClassName
): void {
if ($value instanceof $valueClassName) {
$this->setPrivateProperty($object, $propertyName, $value);
return;
}

$errorMessage = sprintf('The type "%s" is required, but "%s" type given', $valueClassName, $value::class);
throw new InvalidPrivatePropertyTypeException($errorMessage);
}

public function setPrivateProperty(object $object, string $propertyName, mixed $value): void
{
$reflectionProperty = $this->resolvePropertyReflection($object, $propertyName);
Expand Down
14 changes: 3 additions & 11 deletions tests/Util/Reflection/PrivatesAccessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,11 @@ public function testGetterSetterTypesafe(): void

$newObject = new stdClass();
$this->assertNotSame($newObject, $someClassWithPrivateProperty->getObject());
$privatesAccessor->setPrivatePropertyOfClass(
$someClassWithPrivateProperty,
'object',
$newObject,
stdClass::class
);
$privatesAccessor->setPrivateProperty($someClassWithPrivateProperty, 'object', $newObject);
$this->assertSame($newObject, $someClassWithPrivateProperty->getObject());

$fetchedValue = $privatesAccessor->getPrivatePropertyOfClass(
$someClassWithPrivateProperty,
'object',
stdClass::class
);
$fetchedValue = $privatesAccessor->getPrivateProperty($someClassWithPrivateProperty, 'object');

$this->assertSame($someClassWithPrivateProperty->getObject(), $fetchedValue);
}
}

0 comments on commit 18da34e

Please sign in to comment.