Skip to content

Commit

Permalink
Remove dependency to doctrine/common
Browse files Browse the repository at this point in the history
Two good reasons to do this move:
- This was a circular dependency.
- doctrine/common is being sunset.
  • Loading branch information
greg0ire committed Jun 19, 2022
1 parent 25ec98a commit 51ba295
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 28 deletions.
8 changes: 8 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ awareness about deprecated code.
- Use of our low-overhead runtime deprecation API, details:
https://github.com/doctrine/deprecations/

# Upgrade to 3.1

## Deprecated partial implementation of `Doctrine\Persistence\Proxy`

Classes implementing `Doctrine\Persistence\Proxy` should implement all the
methods described with the standard `@method` phpdoc annotations that were
added to it. These methods will be added to the interface in 4.0.

# Upgrade to 3.0

## Removed `OnClearEventArgs::clearsAllEntities()` and `OnClearEventArgs::getEntityClass()`
Expand Down
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"phpstan/phpstan-strict-rules": "^1.1",
"doctrine/annotations": "^1.7",
"doctrine/coding-standard": "^9.0",
"doctrine/common": "^3.0",
"phpunit/phpunit": "^8.5 || ^9.5",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
"vimeo/psalm": "4.22.0"
Expand Down
10 changes: 10 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ parameters:
count: 1
path: src/Persistence/Reflection/EnumReflectionProperty.php

-
message: "#^Call to function method_exists\\(\\) with Doctrine\\\\Persistence\\\\Proxy and '__getInitializer' will always evaluate to true\\.$#"
count: 1
path: src/Persistence/Reflection/RuntimePublicReflectionProperty.php

-
message: "#^Call to function method_exists\\(\\) with Doctrine\\\\Persistence\\\\Proxy and '__setInitializer' will always evaluate to true\\.$#"
count: 1
path: src/Persistence/Reflection/RuntimePublicReflectionProperty.php

-
message: "#^Variable property access on \\$this\\(Doctrine\\\\Persistence\\\\Reflection\\\\TypedNoDefaultRuntimePublicReflectionProperty\\)\\.$#"
count: 1
Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ parameters:

ignoreErrors:
- '#Variable property access on \$this\(Doctrine\\Persistence\\Reflection\\TypedNoDefaultReflectionProperty\)\.#'
- '#Variable property access on Doctrine\\Common\\Proxy\\Proxy\.#'
- '#Variable property access on Doctrine\\Persistence\\Proxy\.#'
- '#Variable property access on object.#'
-
message: '#Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\RuntimeReflectionService\:\:getParentClasses\(\) expects class\-string, string given\.#'
Expand Down
16 changes: 16 additions & 0 deletions src/Persistence/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@
* Interface for proxy classes.
*
* @template T of object
* @method void __setInitialized(bool $initialized) Marks the proxy as initialized or not.
* @method void __setInitializer(Closure|null $initializer = null) Sets the
* initializer callback to be used when initializing the proxy. That
* initializer should accept 3 parameters: $proxy, $method and $params. Those
* are respectively the proxy object that is being initialized, the method name
* that triggered initialization and the parameters passed to that method.
* @method Closure|null __getInitializer() Retrieves the initializer callback
* used to initialize the proxy.
* @method void __setCloner(Closure|null $cloner = null) Sets the callback to
* be used when cloning the proxy. That initializer should accept a single
* parameter, which is the cloned proxy instance itself.
* @method Closure|null __getCloner() Retrieves the callback to be used when
* cloning the proxy.
* @method array<string, mixed> __getLazyProperties() Retrieves the list of
* lazy loaded properties for a given proxy. Keys are the property names, and
* values are the default values for those properties.
*/
interface Proxy
{
Expand Down
15 changes: 13 additions & 2 deletions src/Persistence/Reflection/RuntimePublicReflectionProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@

namespace Doctrine\Persistence\Reflection;

use Doctrine\Common\Proxy\Proxy;
use Doctrine\Persistence\Proxy;
use InvalidArgumentException;
use ReflectionProperty;
use ReturnTypeWillChange;

use function method_exists;
use function sprintf;

/**
* PHP Runtime Reflection Public Property - special overrides for public properties.
*/
Expand All @@ -18,7 +22,7 @@ class RuntimePublicReflectionProperty extends ReflectionProperty
*
* Checks is the value actually exist before fetching it.
* This is to avoid calling `__get` on the provided $object if it
* is a {@see \Doctrine\Common\Proxy\Proxy}.
* is a {@see \Doctrine\Persistence\Proxy}.
*
* @return mixed
*/
Expand All @@ -28,6 +32,13 @@ public function getValue($object = null)
$name = $this->getName();

if ($object instanceof Proxy && ! $object->__isInitialized()) {
if (! method_exists($object, '__getInitializer') || ! method_exists($object, '__setInitializer')) {
throw new InvalidArgumentException(sprintf(
'The proxy class must have all methods documented with @method on %s.',
Proxy::class
));
}

$originalInitializer = $object->__getInitializer();
$object->__setInitializer(null);
$val = $object->$name ?? null;
Expand Down
63 changes: 39 additions & 24 deletions tests/Persistence/RuntimePublicReflectionPropertyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
namespace Doctrine\Tests\Persistence;

use Closure;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\Persistence\Proxy;
use Doctrine\Persistence\Reflection\RuntimePublicReflectionProperty;
use InvalidArgumentException;
use LogicException;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -106,6 +107,37 @@ public function testSetValueOnProxyPublicProperty(): void
$reflProperty->setValue($mockProxy, 'againNewValue');
self::assertSame('againNewValue', $mockProxy->checkedProperty);
}

public function testItThrowsWhenSomeMethodsAreMissingFromAProxy(): void
{
$object = new MyProxy();

$reflProperty = new RuntimePublicReflectionProperty(
MyProxy::class,
'test'
);

$this->expectException(InvalidArgumentException::class);
$reflProperty->getValue($object);
}
}

/**
* @template-implements Proxy<object>
*/
class MyProxy implements Proxy
{
/** @var string */
public $test = 'test';

public function __load(): void
{
}

public function __isInitialized(): bool
{
return false;
}
}

/**
Expand All @@ -124,30 +156,22 @@ class RuntimePublicReflectionPropertyTestProxyMock implements Proxy
/** @var string */
public $checkedProperty = 'testValue';

/**
* {@inheritDoc}
*/
public function __getInitializer()
public function __getInitializer(): ?Closure
{
return $this->initializer;
}

/**
* {@inheritDoc}
*/
public function __setInitializer(?Closure $initializer = null)
public function __setInitializer(?Closure $initializer = null): void
{
$this->initializer = $initializer;
}

/**
* {@inheritDoc}
*
* @return mixed[] Keys are the property names, and values are the default
* values for those properties.
* @phpstan-return array<string, mixed>
*/
public function __getLazyProperties()
public function __getLazyProperties(): array
{
return [];
}
Expand All @@ -161,10 +185,7 @@ public function __isInitialized(): bool
return $this->initialized;
}

/**
* {@inheritDoc}
*/
public function __setInitialized($initialized)
public function __setInitialized(bool $initialized): void
{
$this->initialized = $initialized;
}
Expand Down Expand Up @@ -206,17 +227,11 @@ public function __isset(string $name): bool
return isset($this->checkedProperty);
}

/**
* {@inheritDoc}
*/
public function __setCloner(?Closure $cloner = null)
public function __setCloner(?Closure $cloner = null): void
{
}

/**
* {@inheritDoc}
*/
public function __getCloner()
public function __getCloner(): ?Closure
{
throw new LogicException('Not implemented');
}
Expand Down

0 comments on commit 51ba295

Please sign in to comment.