Skip to content
Snippets Groups Projects
Unverified Commit 7a133e8d authored by Marco Pivetta's avatar Marco Pivetta Committed by GitHub
Browse files

Merge pull request #465 from Ocramius/feature/#463-add-psalm-generic-type-declarations

Introducing initial tentative stub for #463: psalm-specific type declarations
parents 1e347146 9fb01cbb
No related branches found
No related tags found
No related merge requests found
Showing
with 443 additions and 150 deletions
<?php
namespace PHPSTORM_META {
override(
\ProxyManager\Factory\AccessInterceptorScopeLocalizerFactory::createProxy(0),
map([
'@&ProxyManager\Proxy\AccessInterceptorInterface',
])
);
override(
\ProxyManager\Factory\AccessInterceptorValueHolderFactory::createProxy(0),
map([
'@&ProxyManager\Proxy\AccessInterceptorValueHolderInterface',
])
);
override(
\ProxyManager\Factory\LazyLoadingGhostFactory::createProxy(0),
map([
'@&ProxyManager\Proxy\GhostObjectInterface',
])
);
override(
\ProxyManager\Factory\LazyLoadingValueHolderFactory::createProxy(0),
map([
'@&ProxyManager\Proxy\VirtualProxyInterface',
])
);
override(
\ProxyManager\Factory\NullObjectFactory::createProxy(0),
map([
'@&ProxyManager\Proxy\NullObjectInterface',
])
);
override(
\ProxyManager\Factory\RemoteObjectFactory::createProxy(0),
map([
'@&ProxyManager\Proxy\RemoteObjectInterface',
])
);
}
......@@ -30,8 +30,14 @@ script:
- php examples/ghost-object-skipped-properties.php
- php examples/smart-reference.php
- php examples/virtual-proxy.php
- php tests/static-analysis/access-interceptor-scope-localizer.php
- php tests/static-analysis/lazy-loading-ghost-object.php
- php tests/static-analysis/null-object.php
- php tests/static-analysis/access-interceptor-value-holder.php
- php tests/static-analysis/lazy-loading-value-holder.php
- php tests/static-analysis/remote-object.php
- ./vendor/bin/phpcs
- ./vendor/bin/phpstan analyse
- ./vendor/bin/psalm
- ulimit -n 4096 && phpdbg -qrr ./vendor/bin/infection -vvv --test-framework-options='--testsuite=unit' --min-msi=84 --min-covered-msi=86
matrix:
......
......@@ -35,10 +35,9 @@
"couscous/couscous": "^1.7.0",
"nikic/php-parser": "^4.2.1",
"phpbench/phpbench": "^0.16.9",
"phpstan/phpstan": "^0.11.4",
"phpstan/phpstan-phpunit": "^0.11",
"infection/infection": "^0.12.2",
"symfony/console": "^4.2.4"
"symfony/console": "^4.2.4",
"vimeo/psalm": "^3.4.0"
},
"suggest": {
"ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects",
......
......@@ -7,9 +7,12 @@
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
namespace ProxyManager\Example\AccessInterceptorScopeLocalizer;
use ProxyManager\Factory\AccessInterceptorScopeLocalizerFactory;
use ProxyManager\Proxy\AccessInterceptorInterface;
require_once __DIR__ . '/../vendor/autoload.php';
class FluentCounter
{
......@@ -23,25 +26,25 @@ class FluentCounter
}
}
$factory = new AccessInterceptorScopeLocalizerFactory();
$foo = new FluentCounter();
/** @var FluentCounter $proxy */
$proxy = $factory->createProxy(
$foo,
[
'fluentMethod' => function ($proxy) : void {
echo "pre-fluentMethod #{$proxy->counter}!\n";
},
],
[
'fluentMethod' => function ($proxy) : void {
echo "post-fluentMethod #{$proxy->counter}!\n";
},
]
);
$proxy->fluentMethod()->fluentMethod()->fluentMethod()->fluentMethod();
echo 'The proxy counter is now at ' . $proxy->counter . "\n";
echo 'The real instance counter is now at ' . $foo->counter . "\n";
(static function () : void {
$factory = new AccessInterceptorScopeLocalizerFactory();
$foo = new FluentCounter();
$proxy = $factory->createProxy(
$foo,
[
'fluentMethod' => static function (AccessInterceptorInterface $proxy, FluentCounter $realInstance) : void {
echo "pre-fluentMethod #{$realInstance->counter}!\n";
},
],
[
'fluentMethod' => static function (AccessInterceptorInterface $proxy, FluentCounter $realInstance) : void {
echo "post-fluentMethod #{$realInstance->counter}!\n";
},
]
);
$proxy->fluentMethod()->fluentMethod()->fluentMethod()->fluentMethod();
echo 'The proxy counter is now at ' . $proxy->counter . "\n";
echo 'The real instance counter is now at ' . $foo->counter . "\n";
})();
......@@ -2,48 +2,60 @@
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
namespace ProxyManager\Example\GhostObjectSkippedProperties;
use Closure;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\Proxy\GhostObjectInterface;
use ReflectionProperty;
require_once __DIR__ . '/../vendor/autoload.php';
class User
{
private ?int $id = null;
private ?string $username = null;
public function getId() : int
public function getId() : ?int
{
return $this->id;
}
public function getUsername() : string
public function getUsername() : ?string
{
return $this->username;
}
}
/** @var User $proxy */
$proxy = (new LazyLoadingGhostFactory())->createProxy(
User::class,
function (GhostObjectInterface $proxy, string $method, array $parameters, & $initializer, array $properties) {
$initializer = null;
var_dump('Triggered lazy-loading!');
$properties["\0User\0username"] = 'Ocramius';
return true;
},
[
'skippedProperties' => ["\0User\0id"],
]
);
$idReflection = new ReflectionProperty(User::class, 'id');
$idReflection->setAccessible(true);
$idReflection->setValue($proxy, 123);
var_dump($proxy->getId());
var_dump($proxy->getUsername());
(static function () : void {
$proxy = (new LazyLoadingGhostFactory())->createProxy(
User::class,
static function (
GhostObjectInterface $proxy,
string $method,
array $parameters,
?Closure & $initializer,
array $properties
) {
$initializer = null;
var_dump('Triggered lazy-loading!');
$properties["\0User\0username"] = 'Ocramius';
return true;
},
[
'skippedProperties' => ["\0User\0id"],
]
);
$idReflection = new ReflectionProperty(User::class, 'id');
$idReflection->setAccessible(true);
$idReflection->setValue($proxy, 123);
var_dump($proxy->getId());
var_dump($proxy->getUsername());
})();
......@@ -2,11 +2,14 @@
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
namespace ProxyManager\Example\GhostObject;
use Closure;
use ProxyManager\Factory\LazyLoadingGhostFactory;
use ProxyManager\Proxy\GhostObjectInterface;
require_once __DIR__ . '/../vendor/autoload.php';
class Foo
{
private string $foo = '';
......@@ -17,9 +20,9 @@ class Foo
sleep(5);
}
public function setFoo($foo) : void
public function setFoo(string $foo) : void
{
$this->foo = (string) $foo;
$this->foo = $foo;
}
public function getFoo() : string
......@@ -28,24 +31,35 @@ class Foo
}
}
$startTime = microtime(true);
$factory = new LazyLoadingGhostFactory();
(static function () : void {
$startTime = microtime(true);
$factory = new LazyLoadingGhostFactory();
$i = 0;
for ($i = 0; $i < 1000; $i += 1) {
$proxy = $factory->createProxy(
Foo::class,
function (GhostObjectInterface $proxy, string $method, array $parameters, & $initializer, array $properties) {
$initializer = null;
do {
$proxy = $factory->createProxy(
Foo::class,
function (
GhostObjectInterface $proxy,
string $method,
array $parameters,
?Closure & $initializer,
array $properties
) : bool {
$initializer = null;
$properties["\0Foo\0foo"] = 'Hello World!';
$properties["\0Foo\0foo"] = 'Hello World!';
return true;
}
);
}
return true;
}
);
$i += 1;
} while ($i < 1000);
var_dump('time after 1000 instantiations: ' . (microtime(true) - $startTime));
var_dump('time after 1000 instantiations: ' . (microtime(true) - $startTime));
echo $proxy->getFoo() . "\n";
echo $proxy->getFoo() . "\n";
var_dump('time after single call to doFoo: ' . (microtime(true) - $startTime));
var_dump('time after single call to doFoo: ' . (microtime(true) - $startTime));
})();
......@@ -2,17 +2,19 @@
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
namespace ProxyManager\Example\RemoteProxy;
use ProxyManager\Factory\RemoteObject\Adapter\XmlRpc;
use ProxyManager\Factory\RemoteObjectFactory;
use Zend\Http\Client\Adapter\Exception\RuntimeException;
use Zend\XmlRpc\Client;
require_once __DIR__ . '/../vendor/autoload.php';
if (! class_exists('Zend\XmlRpc\Client')) {
echo "This example needs Zend\\XmlRpc\\Client to run. \n In order to install it, "
. "please run following:\n\n"
. "\$ php composer.phar require zendframework/zend-xmlrpc:2.*\n\n";
. "please run following:\n\n"
. "\$ php composer.phar require zendframework/zend-xmlrpc:2.*\n\n";
exit(2);
}
......@@ -25,15 +27,17 @@ class Foo
}
}
$factory = new RemoteObjectFactory(
new XmlRpc(new Client('http://localhost:9876/remote-proxy/remote-proxy-server.php'))
);
$proxy = $factory->createProxy(Foo::class);
(static function () : void {
$factory = new RemoteObjectFactory(
new XmlRpc(new Client('http://localhost:9876/remote-proxy/remote-proxy-server.php'))
);
$proxy = $factory->createProxy(Foo::class);
try {
var_dump($proxy->bar()); // bar remote !
} catch (RuntimeException $error) {
echo "To run this example, please following before:\n\n\$ php -S localhost:9876 -t \"" . __DIR__ . "\"\n";
try {
var_dump($proxy->bar()); // bar remote !
} catch (RuntimeException $error) {
echo "To run this example, please following before:\n\n\$ php -S localhost:9876 -t \"" . __DIR__ . "\"\n";
exit(2);
}
exit(2);
}
})();
......@@ -2,21 +2,25 @@
declare(strict_types=1);
namespace ProxyManager\Example\RemoteProxyServer;
use Zend\XmlRpc\Server;
require_once __DIR__ . '/../../vendor/autoload.php';
class Foo
{
public function bar()
public function bar() : string
{
return 'bar remote!';
}
}
$server = new Server();
(static function () : void {
$server = new Server();
$server->setClass(new Foo(), 'Foo');
$server->setReturnResponse(false);
$server->setClass(new Foo(), 'Foo');
$server->setReturnResponse(false);
$server->handle();
$server->handle();
})();
......@@ -2,10 +2,12 @@
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
namespace ProxyManager\Example\SmartReference;
use ProxyManager\Factory\AccessInterceptorValueHolderFactory;
require_once __DIR__ . '/../vendor/autoload.php';
class Foo
{
public function doFoo() : void
......@@ -14,20 +16,22 @@ class Foo
}
}
$factory = new AccessInterceptorValueHolderFactory();
$proxy = $factory->createProxy(
new Foo(),
[
'doFoo' => function () : void {
echo "pre-foo!\n";
},
],
[
'doFoo' => function () : void {
echo "post-foo!\n";
},
]
);
$proxy->doFoo();
(static function () : void {
$factory = new AccessInterceptorValueHolderFactory();
$proxy = $factory->createProxy(
new Foo(),
[
'doFoo' => function () : void {
echo "pre-foo!\n";
},
],
[
'doFoo' => function () : void {
echo "post-foo!\n";
},
]
);
$proxy->doFoo();
})();
......@@ -2,10 +2,13 @@
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
namespace ProxyManager\Example\VirtualProxy;
use Closure;
use ProxyManager\Factory\LazyLoadingValueHolderFactory;
require_once __DIR__ . '/../vendor/autoload.php';
class Foo
{
public function __construct()
......@@ -19,23 +22,30 @@ class Foo
}
}
$startTime = microtime(true);
$factory = new LazyLoadingValueHolderFactory();
(static function () : void {
$startTime = microtime(true);
$factory = new LazyLoadingValueHolderFactory();
$i = 0;
for ($i = 0; $i < 1000; $i += 1) {
$proxy = $factory->createProxy(
Foo::class,
function (& $wrappedObject, $proxy, $method, $parameters, & $initializer) {
$initializer = null;
$wrappedObject = new Foo();
do {
$proxy = $factory->createProxy(
Foo::class,
static function (
?object & $wrappedObject, ?object $proxy, string $method, array $parameters, ?Closure & $initializer
) {
$initializer = null;
$wrappedObject = new Foo();
return true;
}
);
}
return true;
}
);
$i += 1;
} while ($i < 1000);
var_dump('time after 1000 instantiations: ' . (microtime(true) - $startTime));
var_dump('time after 1000 instantiations: ' . (microtime(true) - $startTime));
$proxy->doFoo();
$proxy->doFoo();
var_dump('time after single call to doFoo: ' . (microtime(true) - $startTime));
var_dump('time after single call to doFoo: ' . (microtime(true) - $startTime));
})();
......@@ -24,5 +24,9 @@
<!-- we cannot enforce the " : void" return type hint due to BC compliance, for now -->
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingReturnTypeHint"/>
<!-- use statements also affect @psalm-* type declarations, and cannot therefore be dropped -->
<exclude name="SlevomatCodingStandard.Namespaces.UnusedUses.UnusedUse"/>
<!-- enforced types are now declared via psalm's "totallyTyped" restrictions -->
<exclude name="SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingTraversableReturnTypeHintSpecification"/>
</rule>
</ruleset>
parameters:
level: 7
paths:
- src
- tests/language-feature-scripts
- tests/ProxyManagerBench
- tests/ProxyManagerTest
ignoreErrors:
# We only mock this dependency, and never really require it:
- '#Zend\\Server\\Client#'
# https://github.com/Ocramius/ProxyManager/issues/363:
- '#::generate\(\) invoked with#'
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
<?xml version="1.0"?>
<psalm
totallyTyped="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="examples"/>
<directory name="src"/>
<directory name="tests/static-analysis"/>
<ignoreFiles>
<directory name="vendor"/>
<!-- uses dependencies that are not available while testing: -->
<file name="examples/remote-proxy.php"/>
</ignoreFiles>
</projectFiles>
<issueHandlers>
<UndefinedClass>
<errorLevel type="suppress">
<!-- zendframework/zend-server is not a direct package nor build dependency, and it should stay that way -->
<referencedClass name="Zend\Http\Client\Adapter\Exception\RuntimeException"/>
<referencedClass name="Zend\Server\Client"/>
<referencedClass name="Zend\XmlRpc\Server"/>
<referencedClass name="Zend\XmlRpc\Client"/>
</errorLevel>
</UndefinedClass>
<LessSpecificReturnType errorLevel="error"/>
<DeprecatedMethod errorLevel="error"/>
<DeprecatedProperty errorLevel="error"/>
<DeprecatedClass errorLevel="error"/>
<DeprecatedConstant errorLevel="error"/>
<DeprecatedInterface errorLevel="error"/>
<DeprecatedTrait errorLevel="error"/>
<ForbiddenCode errorLevel="suppress"/>
<InternalMethod errorLevel="error"/>
<InternalProperty errorLevel="error"/>
<InternalClass errorLevel="error"/>
<MissingClosureReturnType errorLevel="error"/>
<MissingReturnType errorLevel="error"/>
<MissingPropertyType errorLevel="error"/>
<InvalidDocblock errorLevel="error"/>
<MisplacedRequiredParam errorLevel="error"/>
<PropertyNotSetInConstructor errorLevel="suppress"/>
<MissingConstructor errorLevel="error"/>
<MissingClosureParamType errorLevel="error"/>
<MissingParamType errorLevel="error"/>
<RedundantCondition errorLevel="error"/>
<DocblockTypeContradiction errorLevel="error"/>
<RedundantConditionGivenDocblockType errorLevel="error"/>
<RawObjectIteration errorLevel="error"/>
<InvalidStringClass errorLevel="error"/>
<UnresolvableInclude errorLevel="suppress"/>
</issueHandlers>
</psalm>
......@@ -11,6 +11,8 @@ interface AutoloaderInterface
{
/**
* Callback to allow the object to be handled as autoloader - tries to autoload the given class name
*
* @psalm-param class-string $className
*/
public function __invoke(string $className) : bool;
}
......@@ -25,7 +25,8 @@ abstract class AbstractBaseFactory
/**
* Cached checked class names
*
* @var string[]
* @var array<string, string>
* @psalm-var array<string, class-string>
*/
private array $checkedClasses = [];
......@@ -37,11 +38,17 @@ abstract class AbstractBaseFactory
/**
* Generate a proxy from a class name
*
* @param mixed[] $proxyOptions
* @param array<string, mixed> $proxyOptions
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param class-string<RealObjectType> $className
*
* @psalm-return class-string<RealObjectType>
*/
protected function generateProxy(string $className, array $proxyOptions = []) : string
{
......@@ -81,8 +88,11 @@ abstract class AbstractBaseFactory
/**
* Generates the provided `$proxyClassName` from the given `$className` and `$proxyParameters`
*
* @param string[] $proxyParameters
* @param mixed[] $proxyOptions
* @param array<string, mixed> $proxyParameters
* @param array<string, mixed> $proxyOptions
*
* @psalm-param class-string $proxyClassName
* @psalm-param class-string $className
*/
private function generateProxyClass(
string $proxyClassName,
......@@ -93,10 +103,12 @@ abstract class AbstractBaseFactory
$className = $this->configuration->getClassNameInflector()->getUserClassName($className);
$phpClass = new ClassGenerator($proxyClassName);
/** @psalm-suppress TooManyArguments - generator interface was not updated due to BC compliance */
$this->getGenerator()->generate(new ReflectionClass($className), $phpClass, $proxyOptions);
$phpClass = $this->configuration->getClassSignatureGenerator()->addSignature($phpClass, $proxyParameters);
/** @psalm-suppress TooManyArguments - generator interface was not updated due to BC compliance */
$this->configuration->getGeneratorStrategy()->generate($phpClass, $proxyOptions);
$autoloader = $this->configuration->getProxyAutoloader();
......
......@@ -29,15 +29,39 @@ class AccessInterceptorScopeLocalizerFactory extends AbstractBaseFactory
}
/**
* @param object $instance the object to be localized within the access interceptor
* @param Closure[] $prefixInterceptors an array (indexed by method name) of interceptor closures to be called
* @param object $instance the object to be localized within the access interceptor
* @param array<string, Closure> $prefixInterceptors an array (indexed by method name) of interceptor closures to be called
* before method logic is executed
* @param Closure[] $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* @param array<string, Closure> $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* after method logic is executed
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param RealObjectType $instance
* @psalm-param array<string, Closure(
* RealObjectType&AccessInterceptorInterface<RealObjectType>=,
* RealObjectType=,
* string=,
* array<string, mixed>=,
* bool=
* ) : mixed> $prefixInterceptors
* @psalm-param array<string, Closure(
* RealObjectType&AccessInterceptorInterface<RealObjectType>=,
* RealObjectType=,
* string=,
* array<string, mixed>=,
* mixed=,
* bool=
* ) : mixed> $suffixInterceptors
*
* @psalm-return RealObjectType&AccessInterceptorInterface<RealObjectType>
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
object $instance,
......@@ -46,6 +70,12 @@ class AccessInterceptorScopeLocalizerFactory extends AbstractBaseFactory
) : AccessInterceptorInterface {
$proxyClassName = $this->generateProxy(get_class($instance));
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
......
......@@ -7,7 +7,9 @@ namespace ProxyManager\Factory;
use Closure;
use OutOfBoundsException;
use ProxyManager\Configuration;
use ProxyManager\Proxy\AccessInterceptorInterface;
use ProxyManager\Proxy\AccessInterceptorValueHolderInterface;
use ProxyManager\Proxy\ValueHolderInterface;
use ProxyManager\ProxyGenerator\AccessInterceptorValueHolderGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
......@@ -29,15 +31,39 @@ class AccessInterceptorValueHolderFactory extends AbstractBaseFactory
}
/**
* @param object $instance the object to be wrapped within the value holder
* @param Closure[] $prefixInterceptors an array (indexed by method name) of interceptor closures to be called
* @param object $instance the object to be wrapped within the value holder
* @param array<string, Closure> $prefixInterceptors an array (indexed by method name) of interceptor closures to be called
* before method logic is executed
* @param Closure[] $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* @param array<string, Closure> $suffixInterceptors an array (indexed by method name) of interceptor closures to be called
* after method logic is executed
*
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param RealObjectType $instance
* @psalm-param array<string, callable(
* RealObjectType&AccessInterceptorInterface<RealObjectType>=,
* RealObjectType=,
* string=,
* array<string, mixed>=,
* bool=
* ) : mixed> $prefixInterceptors
* @psalm-param array<string, callable(
* RealObjectType&AccessInterceptorInterface<RealObjectType>=,
* RealObjectType=,
* string=,
* array<string, mixed>=,
* mixed=,
* bool=
* ) : mixed> $suffixInterceptors
*
* @psalm-return RealObjectType&AccessInterceptorInterface<RealObjectType>&ValueHolderInterface<RealObjectType>&AccessInterceptorValueHolderInterface<RealObjectType>
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
object $instance,
......@@ -46,6 +72,12 @@ class AccessInterceptorValueHolderFactory extends AbstractBaseFactory
) : AccessInterceptorValueHolderInterface {
$proxyClassName = $this->generateProxy(get_class($instance));
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($instance, $prefixInterceptors, $suffixInterceptors);
}
......
......@@ -8,6 +8,7 @@ use Closure;
use OutOfBoundsException;
use ProxyManager\Configuration;
use ProxyManager\Proxy\GhostObjectInterface;
use ProxyManager\Proxy\LazyLoadingInterface;
use ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
......@@ -73,6 +74,23 @@ class LazyLoadingGhostFactory extends AbstractBaseFactory
* @throws MissingSignatureException
* @throws InvalidSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType as object
*
* @psalm-param class-string<RealObjectType> $className
* @psalm-param Closure(
* RealObjectType&GhostObjectInterface<RealObjectType>=,
* string=,
* array<string, mixed>=,
* ?Closure=,
* array<string, mixed>=
* ) : bool $initializer
* @psalm-param array{skippedProperties?: array<int, string>} $proxyOptions
*
* @psalm-return RealObjectType&GhostObjectInterface<RealObjectType>
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
string $className,
......@@ -81,6 +99,12 @@ class LazyLoadingGhostFactory extends AbstractBaseFactory
) : GhostObjectInterface {
$proxyClassName = $this->generateProxy($className, $proxyOptions);
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($initializer);
}
}
......@@ -6,6 +6,7 @@ namespace ProxyManager\Factory;
use Closure;
use ProxyManager\Configuration;
use ProxyManager\Proxy\ValueHolderInterface;
use ProxyManager\Proxy\VirtualProxyInterface;
use ProxyManager\ProxyGenerator\LazyLoadingValueHolderGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
......@@ -24,7 +25,25 @@ class LazyLoadingValueHolderFactory extends AbstractBaseFactory
$this->generator = new LazyLoadingValueHolderGenerator();
}
/** @param mixed[] $proxyOptions */
/**
* @param array<string, mixed> $proxyOptions
*
* @psalm-template RealObjectType of object
*
* @psalm-param class-string<RealObjectType> $className
* @psalm-param Closure(
* object|null=,
* RealObjectType&ValueHolderInterface<RealObjectType>&VirtualProxyInterface=,
* string=,
* array<string, mixed>=,
* ?Closure=
* ) : bool $initializer
*
* @psalm-return RealObjectType&ValueHolderInterface<RealObjectType>&VirtualProxyInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy(
string $className,
Closure $initializer,
......@@ -32,6 +51,12 @@ class LazyLoadingValueHolderFactory extends AbstractBaseFactory
) : VirtualProxyInterface {
$proxyClassName = $this->generateProxy($className, $proxyOptions);
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor($initializer);
}
......
......@@ -34,12 +34,27 @@ class NullObjectFactory extends AbstractBaseFactory
* @throws InvalidSignatureException
* @throws MissingSignatureException
* @throws OutOfBoundsException
*
* @psalm-template RealObjectType of object
*
* @psalm-param RealObjectType|class-string<RealObjectType> $instanceOrClassName
*
* @psalm-return RealObjectType&NullObjectInterface
*
* @psalm-suppress MixedInferredReturnType We ignore type checks here, since `staticProxyConstructor` is not
* interfaced (by design)
*/
public function createProxy($instanceOrClassName) : NullObjectInterface
{
$className = is_object($instanceOrClassName) ? get_class($instanceOrClassName) : $instanceOrClassName;
$proxyClassName = $this->generateProxy($className);
/**
* We ignore type checks here, since `staticProxyConstructor` is not interfaced (by design)
*
* @psalm-suppress MixedMethodCall
* @psalm-suppress MixedReturnStatement
*/
return $proxyClassName::staticProxyConstructor();
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment