Skip to content

Commit f770360

Browse files
Pavel AlexeevPavel Alexeev
Pavel Alexeev
authored and
Pavel Alexeev
committed
Merge branch 'main' of github.com:php-kafka/php-avro-schema-generator into issue-38-fix-search-class-in-the-same-namespace
2 parents 2b0a85e + ec10e84 commit f770360

12 files changed

+264
-30
lines changed

example/classes/SomeTestClass.php

+2
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,6 @@ class SomeTestClass extends SomeBaseClass
7979
* @var array|SomeOtherTestClass[]
8080
*/
8181
public $someOtherTestClasses;
82+
83+
public int|string $blaaaaaaaa;
8284
}

src/Converter/PhpClassConverter.php

+21-11
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,31 @@
1111
use PhpKafka\PhpAvroSchemaGenerator\PhpClass\PhpClassProperty;
1212
use PhpKafka\PhpAvroSchemaGenerator\PhpClass\PhpClassPropertyInterface;
1313

14-
class PhpClassConverter implements PhpClassConverterInterface
14+
final class PhpClassConverter implements PhpClassConverterInterface
1515
{
1616
private ClassParserInterface $parser;
1717

1818
/**
1919
* @var array<string,int>
2020
*/
21-
private array $typesToSkip = [
21+
private array $singleTypesToSkip = [
2222
'null' => 1,
2323
'object' => 1,
2424
'callable' => 1,
2525
'resource' => 1,
2626
'mixed' => 1
2727
];
2828

29+
/**
30+
* @var array<string,int>
31+
*/
32+
private array $unionTypesToSkip = [
33+
'object' => 1,
34+
'callable' => 1,
35+
'resource' => 1,
36+
'mixed' => 1
37+
];
38+
2939
/**
3040
* @param ClassParserInterface $parser
3141
*/
@@ -98,13 +108,17 @@ private function getConvertedType(string $type)
98108
return $this->getConvertedUnionType($types);
99109
}
100110

101-
private function getFullTypeName(string $type): ?string
111+
private function getFullTypeName(string $type, bool $isUnionType = false): ?string
102112
{
113+
103114
if (true === isset(Avro::MAPPED_TYPES[$type])) {
104115
$type = Avro::MAPPED_TYPES[$type];
105116
}
106117

107-
if (true === isset($this->typesToSkip[$type])) {
118+
if (
119+
(false === $isUnionType && true === isset($this->singleTypesToSkip[$type]))
120+
|| (true === $isUnionType && true === isset($this->unionTypesToSkip[$type]))
121+
) {
108122
return null;
109123
}
110124

@@ -134,20 +148,16 @@ private function getConvertedUnionType(array $types): array
134148
$convertedUnionType = [];
135149

136150
foreach ($types as $type) {
137-
if (true === isset($this->typesToSkip[$type])) {
138-
continue;
139-
}
140-
141-
if (false === $this->isArrayType($type)) {
142-
$convertedUnionType[] = $this->getFullTypeName($type);
151+
if (false === $this->isArrayType($type) && null !== $formattedType = $this->getFullTypeName($type, true)) {
152+
$convertedUnionType[] = $formattedType;
143153
}
144154
}
145155

146156
$arrayType = $this->getArrayType($types);
147157

148158
if (0 !== count($convertedUnionType) && [] !== $arrayType) {
149159
$convertedUnionType[] = $arrayType;
150-
} else {
160+
} elseif (0 === count($convertedUnionType) && [] !== $arrayType) {
151161
return $arrayType;
152162
}
153163

src/Parser/ClassParser.php

+20-6
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use ReflectionClass;
1818
use ReflectionException;
1919

20-
class ClassParser implements ClassParserInterface
20+
final class ClassParser implements ClassParserInterface
2121
{
2222
private ClassPropertyParserInterface $propertyParser;
2323
private Parser $parser;
@@ -163,13 +163,27 @@ private function getClassProperties(array $statements): array
163163
if ($statement instanceof Namespace_) {
164164
foreach ($statement->stmts as $nsStatement) {
165165
if ($nsStatement instanceof Class_) {
166-
foreach ($nsStatement->stmts as $pStatement) {
167-
if ($pStatement instanceof Property) {
168-
$properties[] = $this->propertyParser->parseProperty($pStatement);
169-
}
170-
}
166+
$properties = $this->getAllClassProperties($nsStatement, $properties);
171167
}
172168
}
169+
} elseif ($statement instanceof Class_) {
170+
$properties = $this->getAllClassProperties($statement, $properties);
171+
}
172+
}
173+
174+
return $properties;
175+
}
176+
177+
/**
178+
* @param Class_ $class
179+
* @param PhpClassPropertyInterface[] $properties
180+
* @return PhpClassPropertyInterface[]
181+
*/
182+
private function getAllClassProperties(Class_ $class, array $properties): array
183+
{
184+
foreach ($class->stmts as $pStatement) {
185+
if ($pStatement instanceof Property) {
186+
$properties[] = $this->propertyParser->parseProperty($pStatement);
173187
}
174188
}
175189

src/Parser/ClassPropertyParser.php

+43-6
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
use PhpKafka\PhpAvroSchemaGenerator\PhpClass\PhpClassPropertyInterface;
1010
use PhpParser\Comment\Doc;
1111
use PhpParser\Node\Identifier;
12+
use PhpParser\Node\NullableType;
1213
use PhpParser\Node\Stmt\Property;
1314
use PhpParser\Node\UnionType;
1415
use RuntimeException;
1516

16-
class ClassPropertyParser implements ClassPropertyParserInterface
17+
final class ClassPropertyParser implements ClassPropertyParserInterface
1718
{
1819
private DocCommentParserInterface $docParser;
1920

@@ -80,7 +81,12 @@ private function getPropertyName(Property $property): string
8081
*/
8182
private function getPropertyType(Property $property, array $docComments): string
8283
{
83-
if ($property->type instanceof Identifier) {
84+
if ($property->type instanceof NullableType) {
85+
if ($property->type->type instanceof Identifier) {
86+
$type = Avro::MAPPED_TYPES[$property->type->type->name] ?? $property->type->type->name;
87+
return 'null|' . $type;
88+
}
89+
} elseif ($property->type instanceof Identifier) {
8490
return Avro::MAPPED_TYPES[$property->type->name] ?? $property->type->name;
8591
} elseif ($property->type instanceof UnionType) {
8692
$types = '';
@@ -89,7 +95,7 @@ private function getPropertyType(Property $property, array $docComments): string
8995
foreach ($property->type->types as $type) {
9096
$type = Avro::MAPPED_TYPES[$type->name] ?? $type->name;
9197
$types .= $separator . $type;
92-
$separator = ',';
98+
$separator = '|';
9399
}
94100

95101
return $types;
@@ -118,11 +124,42 @@ private function getTypeFromDocComment(array $docComments): ?string
118124

119125
/**
120126
* @param array<string, mixed> $docComments
121-
* @return string
127+
* @return string|int|float|null
128+
*/
129+
private function getDefaultFromDocComment(array $docComments)
130+
{
131+
if (false === isset($docComments['avro-default'])) {
132+
return PhpClassPropertyInterface::NO_DEFAULT;
133+
}
134+
135+
if (PhpClassPropertyInterface::EMPTY_STRING_DEFAULT === $docComments['avro-default']) {
136+
return '';
137+
}
138+
139+
if (true === is_string($docComments['avro-default']) && true === is_numeric($docComments['avro-default'])) {
140+
$docComments['avro-default'] = $this->convertStringToNumber($docComments['avro-default']);
141+
}
142+
143+
if ('null' === $docComments['avro-default']) {
144+
return null;
145+
}
146+
147+
return $docComments['avro-default'];
148+
}
149+
150+
/**
151+
* @param string $number
152+
* @return float|int
122153
*/
123-
private function getDefaultFromDocComment(array $docComments): string
154+
private function convertStringToNumber(string $number)
124155
{
125-
return $docComments['avro-default'] ?? PhpClassPropertyInterface::NO_DEFAULT;
156+
$int = (int) $number;
157+
158+
if (strval($int) == $number) {
159+
return $int;
160+
}
161+
162+
return (float) $number;
126163
}
127164

128165
/**

src/Parser/DocCommentParser.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace PhpKafka\PhpAvroSchemaGenerator\Parser;
66

7-
class DocCommentParser implements DocCommentParserInterface
7+
final class DocCommentParser implements DocCommentParserInterface
88
{
99
/**
1010
* @param string $docComment

src/PhpClass/PhpClass.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace PhpKafka\PhpAvroSchemaGenerator\PhpClass;
66

7-
class PhpClass implements PhpClassInterface
7+
final class PhpClass implements PhpClassInterface
88
{
99
private string $classBody;
1010
private string $className;

src/PhpClass/PhpClassProperty.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use PhpKafka\PhpAvroSchemaGenerator\Parser\PropertyAttributesInterface;
88

9-
class PhpClassProperty implements PhpClassPropertyInterface
9+
final class PhpClassProperty implements PhpClassPropertyInterface
1010
{
1111
/** @var mixed */
1212
private $propertyDefault;

src/PhpClass/PhpClassPropertyInterface.php

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
interface PhpClassPropertyInterface
88
{
99
public const NO_DEFAULT = 'there-was-no-default-set';
10+
public const EMPTY_STRING_DEFAULT = 'empty-string-default';
11+
1012

1113
/**
1214
* @return mixed

src/Registry/ClassRegistry.php

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PhpKafka\PhpAvroSchemaGenerator\Converter\PhpClassConverterInterface;
99
use PhpKafka\PhpAvroSchemaGenerator\Exception\ClassRegistryException;
1010
use PhpKafka\PhpAvroSchemaGenerator\Parser\TokenParser;
11-
use PhpKafka\PhpAvroSchemaGenerator\PhpClass\PhpClass;
1211
use PhpKafka\PhpAvroSchemaGenerator\PhpClass\PhpClassInterface;
1312
use RecursiveDirectoryIterator;
1413
use RecursiveIteratorIterator;

tests/Integration/Parser/ClassParserTest.php

+76-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public function testGetProperties()
5252
$parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser);
5353
$parser->setCode(file_get_contents($filePath));
5454
$properties = $parser->getProperties();
55-
self::assertCount(15, $properties);
55+
self::assertCount(16, $properties);
5656

5757
foreach($properties as $property) {
5858
self::assertInstanceOf(PhpClassPropertyInterface::class, $property);
@@ -84,12 +84,85 @@ public function testClassWithNoParent(): void
8484

8585
}
8686

87+
public function testClassWithNullableType(): void
88+
{
89+
$propertyParser = new ClassPropertyParser(new DocCommentParser());
90+
$parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser);
91+
$parser->setCode('
92+
<?php
93+
class foo {
94+
public ?string $bla;
95+
}
96+
');
97+
$properties = $parser->getProperties();
98+
self::assertEquals(1, count($properties));
99+
self::assertEquals('null|string', $properties[0]->getPropertyType());
100+
}
101+
102+
public function testClassWithUnionType(): void
103+
{
104+
$propertyParser = new ClassPropertyParser(new DocCommentParser());
105+
$parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser);
106+
$parser->setCode('
107+
<?php
108+
class foo {
109+
public int|string $bla;
110+
}
111+
');
112+
$properties = $parser->getProperties();
113+
self::assertEquals(1, count($properties));
114+
self::assertEquals('int|string', $properties[0]->getPropertyType());
115+
}
116+
117+
public function testClassWithDocUnionType(): void
118+
{
119+
$propertyParser = new ClassPropertyParser(new DocCommentParser());
120+
$parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser);
121+
$parser->setCode('
122+
<?php
123+
class foo {
124+
/**
125+
* @var int|string
126+
*/
127+
public $bla;
128+
}
129+
');
130+
$properties = $parser->getProperties();
131+
self::assertEquals(1, count($properties));
132+
self::assertEquals('int|string', $properties[0]->getPropertyType());
133+
}
134+
135+
public function testClassWithAnnotations(): void
136+
{
137+
$propertyParser = new ClassPropertyParser(new DocCommentParser());
138+
$parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser);
139+
$parser->setCode('
140+
<?php
141+
class foo {
142+
/**
143+
* @avro-type string
144+
* @avro-default abc def
145+
* @avro-doc some doc bla bla
146+
* @var int|string
147+
*/
148+
public $bla;
149+
}
150+
');
151+
$properties = $parser->getProperties();
152+
self::assertEquals(1, count($properties));
153+
self::assertEquals('string', $properties[0]->getPropertyType());
154+
self::assertEquals('abc def', $properties[0]->getPropertyDefault());
155+
self::assertEquals('some doc bla bla', $properties[0]->getPropertyDoc());
156+
157+
}
158+
87159
public function testClassWithNoParentFile(): void
88160
{
89161
$propertyParser = new ClassPropertyParser(new DocCommentParser());
90162
$parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser);
91163
$parser->setCode('<?php class foo extends \RuntimeException {private $x;}');
92-
self::assertEquals([], $parser->getProperties());
93-
164+
$properties = $parser->getProperties();
165+
self::assertEquals(1, count($properties));
166+
self::assertEquals('string', $properties[0]->getPropertyType());
94167
}
95168
}

0 commit comments

Comments
 (0)