Skip to content

Commit

Permalink
Merge pull request #2 from robvanaarle/PHP-8.2
Browse files Browse the repository at this point in the history
PHP 8.2
  • Loading branch information
robvanaarle authored Jun 16, 2023
2 parents ecb6c9a + d4a45b0 commit fcec428
Show file tree
Hide file tree
Showing 20 changed files with 259 additions and 32 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/test-php-82.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Test - PHP 8.2
on:
push:
pull_request:
types: [closed]
branches: [main]

jobs:
call-test:
uses: ./.github/workflows/test.yml
with:
php_version: 8.2
phpunit_version: 9.5.20
13 changes: 7 additions & 6 deletions composer.lock

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

52 changes: 44 additions & 8 deletions src/Code/MethodSignatureBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use ReflectionClass;
use ReflectionMethod;
use ReflectionParameter;
use ReflectionProperty;
use ReflectionType;

class MethodSignatureBuilder
Expand Down Expand Up @@ -38,17 +39,35 @@ protected function getParameterSignatures(
): array {
$parameters = [];
foreach ($reflectionMethod->getParameters() as $reflectionParameter) {
$parameters[] = $this->getParameterSignature($reflectionParameter, $reflectionClass);
$parameters[] = $this->getParameterSignature($reflectionParameter, $reflectionMethod, $reflectionClass);
}
return $parameters;
}

protected function getParameterSignature(
ReflectionParameter $reflectionParameter,
ReflectionMethod $reflectionMethod,
ReflectionClass $reflectionClass
): string {
$definition = [];

if ($reflectionMethod->getShortName() == '__construct') {
$reflectionProperty = $this->findClassProperty($reflectionParameter->getName(), $reflectionClass);
if ($reflectionProperty !== null && $reflectionProperty->isPromoted()) {
if ($reflectionProperty->isPublic()) {
$definition[] = 'public';
} elseif ($reflectionProperty->isProtected()) {
$definition[] = 'protected';
} elseif ($reflectionProperty->isPrivate()) {
$definition[] = 'private';
}

if ($reflectionProperty->isReadOnly()) {
$definition[] = 'readonly';
}
}
}

if ($reflectionParameter->hasType()) {
$definition[] = $this->getType($reflectionParameter->getType(), $reflectionClass);
}
Expand Down Expand Up @@ -84,10 +103,23 @@ protected function getParameterSignature(
return implode(' ', $definition);
}

protected function findClassProperty(string $name, ReflectionClass $reflectionClass)
{
$reflectionProperty = null;
while ($reflectionClass && !$reflectionProperty) {
$reflectionProperty = $reflectionClass->hasProperty($name)
? $reflectionClass->getProperty($name)
: null;
$reflectionClass = $reflectionClass->getParentClass();
}
return $reflectionProperty;
}

protected function getType(
ReflectionType $reflectionType,
ReflectionClass $reflectionClass,
bool $suppressNull = false
bool $suppressNull = false,
bool $addIntersectionBrackets = false
): string {
if (!class_exists(\ReflectionNamedType::class)) {
$type = $this->getFQType($reflectionType, $reflectionClass);
Expand All @@ -96,23 +128,27 @@ protected function getType(
} elseif ($reflectionType instanceof \ReflectionNamedType) {
$type = $this->getFQType($reflectionType, $reflectionClass);

if (!$suppressNull && $type !== 'mixed' && $reflectionType->allowsNull()) {
if (!$suppressNull && $type !== 'mixed' && $type !== 'null' && $reflectionType->allowsNull()) {
$type = '?' . $type;
}

return $type;
} elseif ($reflectionType instanceof \ReflectionUnionType) {
$types = array_map(function (\ReflectionNamedType $reflectionNamedType) use ($reflectionClass) {
return $this->getType($reflectionNamedType, $reflectionClass, true);
$types = array_map(function (\ReflectionType $reflectionType) use ($reflectionClass) {
return $this->getType($reflectionType, $reflectionClass, true, true);
}, $reflectionType->getTypes());

return implode('|', $types);
} elseif ($reflectionType instanceof \ReflectionIntersectionType) {
$types = array_map(function (\ReflectionNamedType $reflectionNamedType) use ($reflectionClass) {
return $this->getType($reflectionNamedType, $reflectionClass, true);
$types = array_map(function (\ReflectionType $reflectionType) use ($reflectionClass) {
return $this->getType($reflectionType, $reflectionClass, true, true);
}, $reflectionType->getTypes());

return implode('&', $types);
$type = implode('&', $types);
if ($addIntersectionBrackets) {
$type = '(' . $type . ')';
}
return $type;
} else {
throw new \Exception('Unknown ReflectionType: ' . get_class($reflectionType));
}
Expand Down
9 changes: 8 additions & 1 deletion src/ObjectSeam/CodeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ public function build(): string

protected function getObjectSeamDefinition(): string
{
return "class {$this->objectSeamClass} extends {$this->class} implements " . ObjectSeam::class;
$reflectionClass = new ReflectionClass($this->class);

$readonly = '';
if (method_exists($reflectionClass, 'isReadOnly') && $reflectionClass->isReadOnly()) {
$readonly = 'readonly ';
}

return "{$readonly}class {$this->objectSeamClass} extends {$this->class} implements " . ObjectSeam::class;
}

protected function getMethods(): array
Expand Down
4 changes: 3 additions & 1 deletion test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ elif [ "$phpVersion" == "8.0" ]; then
phpunitVersion="9.5.20"
elif [ "$phpVersion" == "8.1" ]; then
phpunitVersion="9.5.20"
elif [ "$phpVersion" == "8.2" ]; then
phpunitVersion="9.5.20"
else
echo "unsupported PHP version $phpVersion"
exit 1
Expand All @@ -35,4 +37,4 @@ if [ ! -f "./${phpunitPath}" ]; then
fi

# run PHP in docker with corresponding PHPUnit version
docker run --rm -t -v $(pwd):/app -w /app php:"$phpVersion" "$phpunitPath" "${@:2}"
docker run --rm -t -v $(pwd):/app -w /app php:"$phpVersion" "$phpunitPath" "${@:2}"
23 changes: 20 additions & 3 deletions tests/unit/ObjectSeam/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use PHPObjectSeam\ObjectSeam;
use PHPObjectSeam\TestClasses\AbstractCUT;
use PHPObjectSeam\TestClasses\ReadonlyCUT;
use PHPObjectSeam\TestClasses\TestCUT;
use PHPUnit\Framework\TestCase;

Expand Down Expand Up @@ -52,11 +53,27 @@ public function testProtectedStaticMethodInvocationExecutesOriginal()
$this->assertEquals('protectedStaticMethodResult: default;foo', $cut::callProtectedStaticMethod('foo'));
}

public function testObjectSeamCanBeCreatedOfAbstractClassWithAbstractMethods()
public function provideCUTClasses(): array
{
$builder = new Builder(AbstractCUT::class);
$classes = [
[AbstractCUT::class],
];

if (PHP_VERSION_ID >= 80200) {
$classes[] = [ReadonlyCUT::class];
}

return $classes;
}

/**
* @dataProvider provideCUTClasses
*/
public function testObjectSeamCanBeCreatedForClass(string $class)
{
$builder = new Builder($class);
$objectSeam = $builder->build();

$this->assertInstanceOf(AbstractCUT::class, $objectSeam);
$this->assertInstanceOf($class, $objectSeam);
}
}
10 changes: 0 additions & 10 deletions tests/unit/ObjectSeam/SeamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ class SeamTest extends TestCase

protected $objectSeam;

public function testRob()
{
$cut = $this->createObjectSeam(TestCUTChild::class);

$this->assertEquals(
'protectedMethodResult: default;foo',
$cut->seam()->call('protectedMethod', 'foo')
);
}

protected function createSeam()
{
$this->objectSeam = new TestCUTObjectSeam();
Expand Down
10 changes: 10 additions & 0 deletions tests/unit/TestClasses/ArgumentSignatures/DNF.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace PHPObjectSeam\TestClasses\ArgumentSignatures;

class DNF
{
public function method((\Iterator & \Countable)|null $arg)
{
}
}
10 changes: 10 additions & 0 deletions tests/unit/TestClasses/ArgumentSignatures/FalseType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace PHPObjectSeam\TestClasses\ArgumentSignatures;

class FalseType
{
public function method(false $arg)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace PHPObjectSeam\TestClasses\ArgumentSignatures;

class NewObject
class InInitializer
{
public function method(\DateTime $arg = new \DateTime())
{
Expand Down
10 changes: 10 additions & 0 deletions tests/unit/TestClasses/ArgumentSignatures/Intersection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace PHPObjectSeam\TestClasses\ArgumentSignatures;

class Intersection
{
public function method(\Iterator &\Countable $arg)
{
}
}
10 changes: 10 additions & 0 deletions tests/unit/TestClasses/ArgumentSignatures/NullType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace PHPObjectSeam\TestClasses\ArgumentSignatures;

class NullType
{
public function method(null $arg)
{
}
}
10 changes: 10 additions & 0 deletions tests/unit/TestClasses/ArgumentSignatures/ReadonlyProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace PHPObjectSeam\TestClasses\ArgumentSignatures;

class ReadonlyProperty
{
public function __construct(public readonly string $arg)
{
}
}
10 changes: 10 additions & 0 deletions tests/unit/TestClasses/ArgumentSignatures/TrueType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace PHPObjectSeam\TestClasses\ArgumentSignatures;

class TrueType
{
public function method(true $arg)
{
}
}
50 changes: 48 additions & 2 deletions tests/unit/TestClasses/MethodProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public function provideMethods(): array
'method'
],[
\PHPObjectSeam\TestClasses\ArgumentSignatures\ConstructorPropertyPromotion::class,
'public function __construct(?string $arg = null)',
'public function __construct(public ?string $arg = null)',
'__construct'
],[
\PHPObjectSeam\TestClasses\ResultSignatures\MixedResult::class,
Expand All @@ -139,9 +139,17 @@ public function provideMethods(): array
if (PHP_VERSION_ID >= 80100) {
$methods = array_merge($methods, [
[
\PHPObjectSeam\TestClasses\ArgumentSignatures\NewObject::class,
\PHPObjectSeam\TestClasses\ArgumentSignatures\Intersection::class,
'public function method(\Iterator&\Countable $arg)',
'method'
],[
\PHPObjectSeam\TestClasses\ArgumentSignatures\InInitializer::class,
'public function method(\DateTime $arg = new \DateTime)',
'method'
],[
\PHPObjectSeam\TestClasses\ArgumentSignatures\ReadonlyProperty::class,
'public function __construct(public readonly string $arg)',
'__construct'
],[
\PHPObjectSeam\TestClasses\ResultSignatures\NeverResult::class,
'public function method(): never',
Expand All @@ -158,6 +166,44 @@ public function provideMethods(): array
]);
}

if (PHP_VERSION_ID >= 80200) {
$methods = array_merge($methods, [
[
\PHPObjectSeam\TestClasses\ArgumentSignatures\TrueType::class,
'public function method(true $arg)',
'method'
],[
\PHPObjectSeam\TestClasses\ArgumentSignatures\FalseType::class,
'public function method(false $arg)',
'method'
],[
\PHPObjectSeam\TestClasses\ArgumentSignatures\NullType::class,
'public function method(null $arg)',
'method'
],[
\PHPObjectSeam\TestClasses\ArgumentSignatures\DNF::class,
'public function method((\Iterator&\Countable)|null $arg)',
'method'
],[
\PHPObjectSeam\TestClasses\ResultSignatures\TrueResult::class,
'public function method(): true',
'method'
],[
\PHPObjectSeam\TestClasses\ResultSignatures\FalseResult::class,
'public function method(): false',
'method'
],[
\PHPObjectSeam\TestClasses\ResultSignatures\NullResult::class,
'public function method(): null',
'method'
],[
\PHPObjectSeam\TestClasses\ResultSignatures\DNFResult::class,
'public function method(): (\Iterator&\Countable)|null',
'method'
],
]);
}

// deduplicate
$result = [];
foreach ($methods as $method) {
Expand Down
Loading

0 comments on commit fcec428

Please sign in to comment.