diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index d3c3808..7e5d3b6 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- php: [8.1, 8.2, 8.3]
+ php: [8.1, 8.2, 8.3, 8.4]
dependency-versions: [locked, lowest, highest]
steps:
- uses: actions/checkout@v4
@@ -76,7 +76,7 @@ jobs:
with:
composer-options: --optimize-autoloader
dependency-versions: ${{ matrix.dependency-versions }}
- - run: composer test -- --colors=always --coverage-clover coverage.xml
+ - run: composer test -- --colors=always --coverage-clover=coverage.xml ${{ matrix.php == '8.4' && '--testsuite=php84' || '' }}
- if: ${{ matrix.php == '8.1' && matrix.dependency-versions == 'locked' }}
uses: codecov/codecov-action@v4
env:
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 3798c0d..6962eaa 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -11,7 +11,7 @@
>
-
+
@@ -19,6 +19,11 @@
tests
+
+ tests
+ tests/Internal/NativeAdapter/AdapterCompatibilityTest.php
+
+
tests/Internal/Inheritance/TypeResolversTest.php
tests/Internal/PhpDoc/PhpDocTypeReflectorTest.php
diff --git a/psalm.xml.dist b/psalm.xml.dist
index 49d36da..facdcd2 100644
--- a/psalm.xml.dist
+++ b/psalm.xml.dist
@@ -115,5 +115,6 @@
+
diff --git a/src/Internal/NativeAdapter/ClassAdapter.php b/src/Internal/NativeAdapter/ClassAdapter.php
index 6406791..1079116 100644
--- a/src/Internal/NativeAdapter/ClassAdapter.php
+++ b/src/Internal/NativeAdapter/ClassAdapter.php
@@ -525,6 +525,86 @@ public function setStaticPropertyValue(string $name, mixed $value): void
parent::setStaticPropertyValue($name, $value);
}
+ /**
+ * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function newLazyGhost(callable $initializer, int $options = 0): object
+ {
+ $this->loadNative();
+
+ return parent::newLazyGhost($initializer, $options);
+ }
+
+ /**
+ * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function newLazyProxy(callable $factory, int $options = 0): object
+ {
+ $this->loadNative();
+
+ return parent::newLazyProxy($factory, $options);
+ }
+
+ /**
+ * @psalm-suppress UndefinedMethod
+ */
+ public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void
+ {
+ $this->loadNative();
+
+ parent::resetAsLazyGhost($object, $initializer, $options);
+ }
+
+ /**
+ * @psalm-suppress UndefinedMethod
+ */
+ public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void
+ {
+ $this->loadNative();
+
+ parent::resetAsLazyProxy($object, $factory, $options);
+ }
+
+ /**
+ * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function initializeLazyObject(object $object): object
+ {
+ $this->loadNative();
+
+ return parent::initializeLazyObject($object);
+ }
+
+ /**
+ * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function markLazyObjectAsInitialized(object $object): object
+ {
+ $this->loadNative();
+
+ return parent::markLazyObjectAsInitialized($object);
+ }
+
+ /**
+ * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function getLazyInitializer(object $object): ?callable
+ {
+ $this->loadNative();
+
+ return parent::getLazyInitializer($object);
+ }
+
+ /**
+ * @psalm-suppress MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function isUninitializedLazyObject(object $object): bool
+ {
+ $this->loadNative();
+
+ return parent::isUninitializedLazyObject($object);
+ }
+
/**
* @return Properties
*/
diff --git a/src/Internal/NativeAdapter/ClassConstantAdapter.php b/src/Internal/NativeAdapter/ClassConstantAdapter.php
index 6a56511..f16b1d4 100644
--- a/src/Internal/NativeAdapter/ClassConstantAdapter.php
+++ b/src/Internal/NativeAdapter/ClassConstantAdapter.php
@@ -124,6 +124,15 @@ public function hasType(): bool
return $this->reflection->type(TypeKind::Native) !== null;
}
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, UnusedPsalmSuppress
+ * @TODO
+ */
+ public function isDeprecated(): bool
+ {
+ return false;
+ }
+
public function isEnumCase(): bool
{
return $this->reflection->isEnumCase();
diff --git a/src/Internal/NativeAdapter/EnumAdapter.php b/src/Internal/NativeAdapter/EnumAdapter.php
index 7015046..a695a66 100644
--- a/src/Internal/NativeAdapter/EnumAdapter.php
+++ b/src/Internal/NativeAdapter/EnumAdapter.php
@@ -361,4 +361,68 @@ public function setStaticPropertyValue(string $name, mixed $value): void
{
$this->_class->setStaticPropertyValue($name, $value);
}
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function newLazyGhost(callable $initializer, int $options = 0): object
+ {
+ return $this->_class->newLazyGhost($initializer, $options);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function newLazyProxy(callable $factory, int $options = 0): object
+ {
+ return $this->_class->newLazyProxy($factory, $options);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void
+ {
+ $this->_class->resetAsLazyGhost($object, $initializer, $options);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void
+ {
+ $this->_class->resetAsLazyProxy($object, $factory, $options);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function initializeLazyObject(object $object): object
+ {
+ return $this->_class->initializeLazyObject($object);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function markLazyObjectAsInitialized(object $object): object
+ {
+ return $this->_class->markLazyObjectAsInitialized($object);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function getLazyInitializer(object $object): ?callable
+ {
+ return $this->_class->getLazyInitializer($object);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function isUninitializedLazyObject(object $object): bool
+ {
+ return $this->_class->isUninitializedLazyObject($object);
+ }
}
diff --git a/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php b/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php
index dffaac4..e11dbda 100644
--- a/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php
+++ b/src/Internal/NativeAdapter/EnumBackedCaseAdapter.php
@@ -102,6 +102,14 @@ public function hasType(): bool
return $this->constant->hasType();
}
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function isDeprecated(): bool
+ {
+ return $this->constant->isDeprecated();
+ }
+
public function isEnumCase(): bool
{
return $this->constant->isEnumCase();
diff --git a/src/Internal/NativeAdapter/PropertyAdapter.php b/src/Internal/NativeAdapter/PropertyAdapter.php
index 79155dc..c9f618c 100644
--- a/src/Internal/NativeAdapter/PropertyAdapter.php
+++ b/src/Internal/NativeAdapter/PropertyAdapter.php
@@ -179,6 +179,154 @@ public function setValue(mixed $objectOrValue, mixed $value = null): void
parent::setValue($objectOrValue, $value);
}
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function getHooks(): array
+ {
+ $this->loadNative();
+
+ return parent::getHooks();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function getHook(\PropertyHookType $hook): ?\ReflectionMethod
+ {
+ $this->loadNative();
+
+ return parent::getHook($hook);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function isVirtual(): bool
+ {
+ $this->loadNative();
+
+ return parent::isVirtual();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function getSettableType(): ?\ReflectionType
+ {
+ $this->loadNative();
+
+ return parent::getSettableType();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod
+ */
+ public function getRawValue(object $object): mixed
+ {
+ $this->loadNative();
+
+ return parent::getRawValue($object);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod
+ */
+ public function setRawValue(object $object, mixed $value): void
+ {
+ $this->loadNative();
+
+ parent::setRawValue($object, $value);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod
+ */
+ public function setRawValueWithoutLazyInitialization(object $object, mixed $value): void
+ {
+ $this->loadNative();
+
+ parent::setRawValueWithoutLazyInitialization($object, $value);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, UndefinedMethod
+ */
+ public function skipLazyInitialization(object $object): void
+ {
+ $this->loadNative();
+
+ parent::skipLazyInitialization($object);
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function isPrivateSet(): bool
+ {
+ $this->loadNative();
+
+ return parent::isPrivateSet();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function isProtectedSet(): bool
+ {
+ $this->loadNative();
+
+ return parent::isProtectedSet();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function isDynamic(): bool
+ {
+ return false;
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function isAbstract(): bool
+ {
+ $this->loadNative();
+
+ return parent::isAbstract();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function isFinal(): bool
+ {
+ $this->loadNative();
+
+ return parent::isFinal();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function hasHooks(): bool
+ {
+ $this->loadNative();
+
+ return parent::hasHooks();
+ }
+
+ /**
+ * @psalm-suppress PossiblyUnusedMethod, MixedInferredReturnType, MixedReturnStatement, UndefinedMethod
+ */
+ public function hasHook(\PropertyHookType $hook): bool
+ {
+ $this->loadNative();
+
+ return parent::hasHook($hook);
+ }
+
private bool $nativeLoaded = false;
private function loadNative(): void
diff --git a/src/Internal/NativeAdapter/PropertyHookType.php b/src/Internal/NativeAdapter/PropertyHookType.php
new file mode 100644
index 0000000..d1eb16e
--- /dev/null
+++ b/src/Internal/NativeAdapter/PropertyHookType.php
@@ -0,0 +1,13 @@
+hasType(), $typhoon->hasType(), $messagePrefix . '.hasType()');
}
+ if (method_exists(\ReflectionClassConstant::class, 'isDeprecated')) {
+ /** @psalm-suppress MixedArgument, UnusedPsalmSuppress */
+ self::assertSame($native->isDeprecated(), $typhoon->isDeprecated(), $messagePrefix . '.isDeprecated()');
+ }
self::assertSame($native->isEnumCase(), $typhoon->isEnumCase(), $messagePrefix . '.isEnumCase()');
self::assertSame($native->isFinal(), $typhoon->isFinal(), $messagePrefix . '.isFinal()');
self::assertSame($native->isPrivate(), $typhoon->isPrivate(), $messagePrefix . '.isPrivate()');
@@ -336,6 +340,10 @@ private static function assertPropertyEquals(\ReflectionProperty $native, \Refle
self::assertSame($native->hasDefaultValue(), $typhoon->hasDefaultValue(), $messagePrefix . '.hasDefaultValue()');
self::assertSame($native->hasType(), $typhoon->hasType(), $messagePrefix . '.hasType()');
self::assertSame($native->isDefault(), $typhoon->isDefault(), $messagePrefix . '.isDefault()');
+ if (method_exists(\ReflectionProperty::class, 'isDynamic')) {
+ /** @psalm-suppress MixedArgument, UnusedPsalmSuppress */
+ self::assertSame($native->isDynamic(), $typhoon->isDynamic(), $messagePrefix . '.isDynamic()');
+ }
// TODO isInitialized()
self::assertSame($native->isPrivate(), $typhoon->isPrivate(), $messagePrefix . '.isPrivate()');
self::assertSame($native->isPromoted(), $typhoon->isPromoted(), $messagePrefix . '.isPromoted()');