Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHP 8.4 support #1462

Draft
wants to merge 17 commits into
base: 6.44.x
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
php-version:
- "8.2"
- "8.3"
- "8.4"
operating-system:
- "ubuntu-latest"
- "windows-latest"
Expand Down Expand Up @@ -67,6 +68,7 @@ jobs:
php-version:
- "8.2"
- "8.3"
- "8.4"
operating-system:
- "ubuntu-latest"

Expand Down Expand Up @@ -107,6 +109,7 @@ jobs:
dependencies:
- "locked"
php-version:
# Throws deprecated errors on 8.4
- "8.3"
operating-system:
- "ubuntu-latest"
Expand Down Expand Up @@ -148,6 +151,7 @@ jobs:
dependencies:
- "locked"
php-version:
# Does not work on 8.4
- "8.3"
operating-system:
- "ubuntu-latest"
Expand Down Expand Up @@ -192,7 +196,7 @@ jobs:
dependencies:
- "locked"
php-version:
- "8.3"
- "8.4"
operating-system:
- "ubuntu-latest"
- "windows-latest"
Expand Down Expand Up @@ -234,7 +238,7 @@ jobs:
dependencies:
- "locked"
php-version:
- "8.3"
- "8.4"
operating-system:
- "ubuntu-latest"

Expand Down Expand Up @@ -268,7 +272,7 @@ jobs:
dependencies:
- "locked"
php-version:
- "8.3"
- "8.4"
operating-system:
- "ubuntu-latest"
- "windows-latest"
Expand Down Expand Up @@ -347,7 +351,7 @@ jobs:
dependencies:
- "locked"
php-version:
- "8.3"
- "8.4"
operating-system:
- "ubuntu-latest"

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"description": "Better Reflection - an improved code reflection API",
"license": "MIT",
"require": {
"php": "~8.2.0 || ~8.3.2",
"php": "~8.2.0 || ~8.3.2 || ~8.4.1",
"ext-json": "*",
"jetbrains/phpstorm-stubs": "2024.2",
"nikic/php-parser": "^5.3.1"
Expand Down
4 changes: 2 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ parameters:
- phar://%currentWorkingDirectory%/test/unit/Fixture/autoload.phar/vendor/autoload.php

ignoreErrors:
-
identifier: class.notFound
message: '#(unknown class|invalid type) PropertyHookType#'

-
identifier: missingType.generics
-
Expand Down
9 changes: 9 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,15 @@
<code><![CDATA[getEndLine]]></code>
<code><![CDATA[getEndLine]]></code>
<code><![CDATA[getEndLine]]></code>
<code><![CDATA[getEndLine]]></code>
<code><![CDATA[getReturnType]]></code>
<code><![CDATA[getStartLine]]></code>
<code><![CDATA[getStartLine]]></code>
<code><![CDATA[getStartLine]]></code>
<code><![CDATA[getStartLine]]></code>
<code><![CDATA[getStartLine]]></code>
<code><![CDATA[getStmts]]></code>
<code><![CDATA[getStmts]]></code>
<code><![CDATA[getSubNodeNames]]></code>
<code><![CDATA[traverse]]></code>
</ImpureMethodCall>
Expand Down Expand Up @@ -201,14 +205,19 @@
<ImpureMethodCall>
<code><![CDATA[__invoke]]></code>
<code><![CDATA[classExists]]></code>
<code><![CDATA[createFromNode]]></code>
<code><![CDATA[getEndLine]]></code>
<code><![CDATA[getStartLine]]></code>
<code><![CDATA[getStmts]]></code>
<code><![CDATA[isPrivate]]></code>
<code><![CDATA[isPrivateSet]]></code>
<code><![CDATA[isProtected]]></code>
<code><![CDATA[isProtectedSet]]></code>
<code><![CDATA[isPublic]]></code>
<code><![CDATA[isReadonly]]></code>
<code><![CDATA[isStatic]]></code>
<code><![CDATA[traitExists]]></code>
<code><![CDATA[traverse]]></code>
</ImpureMethodCall>
</file>
<file src="src/Reflection/ReflectionType.php">
Expand Down
50 changes: 50 additions & 0 deletions src/Reflection/Adapter/ReflectionClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
*/
final class ReflectionClass extends CoreReflectionClass
{
/** @internal */
public const SKIP_INITIALIZATION_ON_SERIALIZE_COMPATIBILITY = 8;

/** @internal */
public const SKIP_DESTRUCTOR_COMPATIBILITY = 16;

public function __construct(private BetterReflectionClass|BetterReflectionEnum $betterReflectionClass)
{
unset($this->name);
Expand Down Expand Up @@ -450,6 +456,50 @@ public function newInstanceArgs(array|null $args = null): object
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<self::SKIP_*> $options */
public function newLazyGhost(callable $initializer, int $options = 0): object
{
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<self::SKIP_*> $options */
public function newLazyProxy(callable $factory, int $options = 0): object
{
throw new Exception\NotImplemented('Not implemented');
}

public function markLazyObjectAsInitialized(object $object): object
{
throw new Exception\NotImplemented('Not implemented');
}

public function getLazyInitializer(object $object): callable|null
{
throw new Exception\NotImplemented('Not implemented');
}

public function initializeLazyObject(object $object): object
{
throw new Exception\NotImplemented('Not implemented');
}

public function isUninitializedLazyObject(object $object): bool
{
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<self::SKIP_*> $options */
public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void
{
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<self::SKIP_*> $options */
public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void
{
throw new Exception\NotImplemented('Not implemented');
}

/** @psalm-mutation-free */
public function getParentClass(): ReflectionClass|false
{
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Adapter/ReflectionClassConstant.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ public function isEnumCase(): bool
return $this->betterClassConstantOrEnumCase instanceof BetterReflectionEnumCase;
}

public function isDeprecated(): bool
{
return $this->betterClassConstantOrEnumCase->isDeprecated();
}

public function __get(string $name): mixed
{
if ($name === 'name') {
Expand Down
44 changes: 44 additions & 0 deletions src/Reflection/Adapter/ReflectionEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,50 @@ public function newInstanceArgs(array|null $args = null): object
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<ReflectionClass::SKIP_*> $options */
public function newLazyGhost(callable $initializer, int $options = 0): object
{
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<ReflectionClass::SKIP_*> $options */
public function newLazyProxy(callable $factory, int $options = 0): object
{
throw new Exception\NotImplemented('Not implemented');
}

public function markLazyObjectAsInitialized(object $object): object
{
throw new Exception\NotImplemented('Not implemented');
}

public function getLazyInitializer(object $object): callable|null
{
throw new Exception\NotImplemented('Not implemented');
}

public function initializeLazyObject(object $object): object
{
throw new Exception\NotImplemented('Not implemented');
}

public function isUninitializedLazyObject(object $object): bool
{
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<ReflectionClass::SKIP_*> $options */
public function resetAsLazyGhost(object $object, callable $initializer, int $options = 0): void
{
throw new Exception\NotImplemented('Not implemented');
}

/** @param int-mask-of<ReflectionClass::SKIP_*> $options */
public function resetAsLazyProxy(object $object, callable $factory, int $options = 0): void
{
throw new Exception\NotImplemented('Not implemented');
}

public function getParentClass(): ReflectionClass|false
{
return false;
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Adapter/ReflectionEnumBackedCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ public function getBackingValue(): int|string
return $this->betterReflectionEnumCase->getValue();
}

public function isDeprecated(): bool
{
return $this->betterReflectionEnumCase->isDeprecated();
}

public function __get(string $name): mixed
{
if ($name === 'name') {
Expand Down
5 changes: 5 additions & 0 deletions src/Reflection/Adapter/ReflectionEnumUnitCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ public function getEnum(): ReflectionEnum
return new ReflectionEnum($this->betterReflectionEnumCase->getDeclaringEnum());
}

public function isDeprecated(): bool
{
return $this->betterReflectionEnumCase->isDeprecated();
}

public function __get(string $name): mixed
{
if ($name === 'name') {
Expand Down
44 changes: 34 additions & 10 deletions src/Reflection/Adapter/ReflectionNamedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,48 @@
/** @psalm-immutable */
final class ReflectionNamedType extends CoreReflectionNamedType
{
public function __construct(private BetterReflectionNamedType $betterReflectionType, private bool $allowsNull)
/** @var non-empty-string */
private string $nameType;

private bool $isBuiltin;

/** @var non-empty-string */
private string $toString;

/** @param \Roave\BetterReflection\Reflection\ReflectionNamedType|non-empty-string $type */
public function __construct(BetterReflectionNamedType|string $type, private bool $allowsNull)
{
if ($type instanceof BetterReflectionNamedType) {
$nameType = $type->getName();
$this->nameType = $nameType;
$this->isBuiltin = $this->computeIsBuiltin($nameType, $type->isBuiltin());
$this->toString = $type->__toString();
} else {
$this->nameType = $type;
$this->isBuiltin = true;
$this->toString = $type;
}
}

public function getName(): string
{
return $this->betterReflectionType->getName();
return $this->nameType;
}

/** @return non-empty-string */
public function __toString(): string
{
$type = strtolower($this->betterReflectionType->getName());
$normalizedType = strtolower($this->nameType);

if (
! $this->allowsNull
|| $type === 'mixed'
|| $type === 'null'
|| $normalizedType === 'mixed'
|| $normalizedType === 'null'
) {
return $this->betterReflectionType->__toString();
return $this->toString;
}

return '?' . $this->betterReflectionType->__toString();
return '?' . $this->toString;
}

public function allowsNull(): bool
Expand All @@ -44,12 +63,17 @@ public function allowsNull(): bool

public function isBuiltin(): bool
{
$type = strtolower($this->betterReflectionType->getName());
return $this->isBuiltin;
}

private function computeIsBuiltin(string $namedType, bool $isBuiltin): bool
{
$normalizedType = strtolower($namedType);

if ($type === 'self' || $type === 'parent' || $type === 'static') {
if ($normalizedType === 'self' || $normalizedType === 'parent' || $normalizedType === 'static') {
return false;
}

return $this->betterReflectionType->isBuiltin();
return $isBuiltin;
}
}
Loading
Loading