diff --git a/README.md b/README.md index f4f514a..ed2f386 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Actions Status](https://github.com/php-kafka/php-avro-schema-generator/workflows/CI/badge.svg)](https://github.com/php-kafka/php-avro-schema-generator/workflows/CI/badge.svg) [![Maintainability](https://api.codeclimate.com/v1/badges/41aecf21566d7e9bfb69/maintainability)](https://codeclimate.com/github/php-kafka/php-avro-schema-generator/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/41aecf21566d7e9bfb69/test_coverage)](https://codeclimate.com/github/php-kafka/php-avro-schema-generator/test_coverage) -![Supported PHP versions: 7.4 .. 8.x](https://img.shields.io/badge/php-7.4%20..%208.x-blue.svg) +![Supported PHP versions: ^8.2](https://img.shields.io/badge/php-%5E8.2-blue.svg) [![Latest Stable Version](https://poser.pugx.org/php-kafka/php-avro-schema-generator/v/stable)](https://packagist.org/packages/php-kafka/php-avro-schema-generator) ## Installation @@ -53,7 +53,7 @@ How to enable optimizer: **Console example** ```bash -./vendor/bin/avro-cli --optimizeFullNames --optimizeFieldOrder --optimizePrimitiveSchemas avro:subschema:merge ./example/schemaTemplates ./example/schema +./bin/avro-cli --optimizeFullNames --optimizeFieldOrder --optimizePrimitiveSchemas avro:subschema:merge ./example/schemaTemplates ./example/schema ``` **PHP Example** ```php @@ -87,7 +87,7 @@ After you have reviewed and adjusted your templates you will need to merge them **Console example** ```bash -./vendor/bin/avro-cli avro:schema:generate ./example/classes ./example/schemaTemplates +./bin/avro-cli avro:schema:generate ./example/classes ./example/schemaTemplates ``` **PHP Example** diff --git a/composer.json b/composer.json index 82142f1..d9f41af 100644 --- a/composer.json +++ b/composer.json @@ -9,15 +9,15 @@ "license": "MIT", "require": { "ext-json": "*", - "flix-tech/avro-php": "^3.0|^4.0|^5.0", - "symfony/console": "^4.3|^5.1|^6.0", - "nikic/php-parser": "^4.13", + "flix-tech/avro-php": "^5.1", + "symfony/console": "^7.2", + "nikic/php-parser": "^5.4", "pimple/pimple": "^3.5" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.19|^3.15", - "infection/infection": "^0.25|^0.27", - "composer/xdebug-handler": "^2.0|^3.0", + "friendsofphp/php-cs-fixer": "^3.70", + "infection/infection": "^0.29", + "composer/xdebug-handler": "^3.0", "phpstan/phpstan": "^1.2", "phpunit/phpunit": "^9.3", "rregeer/phpunit-coverage-check": "^0.3", diff --git a/docker-compose.yml b/docker-compose.yml index 60a5517..2f15772 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,3 +9,7 @@ services: HOST_USER_ID: ${USER_ID} volumes: - ./:/var/www/html + - type: tmpfs + target: /tmp/test + tmpfs: + size: 256M diff --git a/docker/dev/php/Dockerfile b/docker/dev/php/Dockerfile index bd0b4d9..8270110 100644 --- a/docker/dev/php/Dockerfile +++ b/docker/dev/php/Dockerfile @@ -1,8 +1,15 @@ -FROM php:7.4-cli-alpine3.15 +FROM php:8.2-cli-alpine3.19 ARG HOST_USER_ID ARG HOST_USER +# Create sanitized username as an ENV variable so it persists throughout the build +ENV CLEAN_USER=default +RUN if [ ! -z "$HOST_USER" ]; then \ + export CLEAN_USER=$(echo "$HOST_USER" | sed 's/[^a-zA-Z0-9._-]/_/g') && \ + echo "export CLEAN_USER=$CLEAN_USER" >> /etc/profile; \ +fi + COPY php/files/bin/ /usr/local/bin/ # SYS: Install required packages @@ -10,9 +17,14 @@ RUN apk --no-cache upgrade && \ apk --no-cache add bash git sudo make autoconf gcc g++ RUN if [ ! -z "$HOST_USER_ID" ]; then \ - adduser -u $HOST_USER_ID -D -H $HOST_USER && \ - echo "ALL ALL=NOPASSWD: ALL" >> /etc/sudoers; \ - fi + adduser -u $HOST_USER_ID -D -H $CLEAN_USER && \ + echo "ALL ALL=NOPASSWD: ALL" >> /etc/sudoers; \ +fi + +# Create a directory with proper permissions for test files +RUN mkdir -p /tmp/test && \ + chmod 777 /tmp/test && \ + chown $CLEAN_USER:$CLEAN_USER /tmp/test # PHP: Install php extensions RUN mkdir /phpIni && \ @@ -23,9 +35,8 @@ RUN mkdir /phpIni && \ # COMPOSER: install binary RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer -USER $HOST_USER +USER $CLEAN_USER WORKDIR /var/www/html CMD tail -f /dev/null - diff --git a/example/generate.php b/example/generate.php index 8caf056..23b18d1 100644 --- a/example/generate.php +++ b/example/generate.php @@ -11,8 +11,9 @@ use PhpKafka\PhpAvroSchemaGenerator\Parser\ClassPropertyParser; use PhpKafka\PhpAvroSchemaGenerator\Generator\SchemaGenerator; use PhpParser\ParserFactory; +use PhpParser\PhpVersion; -$parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); +$parser = (new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)); $classPropertyParser = new ClassPropertyParser(new DocCommentParser()); $classParser = new ClassParser($parser, $classPropertyParser); diff --git a/src/Parser/ClassParser.php b/src/Parser/ClassParser.php index ddf24f7..0087933 100644 --- a/src/Parser/ClassParser.php +++ b/src/Parser/ClassParser.php @@ -142,7 +142,7 @@ public function getNamespace(): ?string foreach ($this->statements as $statement) { if ($statement instanceof Namespace_) { if ($statement->name instanceof Name) { - return implode('\\', $statement->name->parts); + return $statement->name->toString(); } } } diff --git a/src/ServiceProvider/ParserServiceProvider.php b/src/ServiceProvider/ParserServiceProvider.php index 70530e5..a8fe1cc 100644 --- a/src/ServiceProvider/ParserServiceProvider.php +++ b/src/ServiceProvider/ParserServiceProvider.php @@ -12,6 +12,7 @@ use PhpKafka\PhpAvroSchemaGenerator\Parser\DocCommentParserInterface; use PhpParser\ParserFactory; use PhpParser\Parser; +use PhpParser\PhpVersion; use Pimple\Container; use Pimple\ServiceProviderInterface; @@ -24,7 +25,7 @@ public function register(Container $container): void }; $container[Parser::class] = static function (Container $container): Parser { - return $container[ParserFactory::class]->create(ParserFactory::PREFER_PHP7); + return $container[ParserFactory::class]->createForVersion(PhpVersion::fromComponents(8, 2)); }; $container[DocCommentParserInterface::class] = static function (): DocCommentParserInterface { diff --git a/tests/Integration/Parser/ClassParserTest.php b/tests/Integration/Parser/ClassParserTest.php index d712bbf..9c2fe6f 100644 --- a/tests/Integration/Parser/ClassParserTest.php +++ b/tests/Integration/Parser/ClassParserTest.php @@ -9,6 +9,7 @@ use PhpKafka\PhpAvroSchemaGenerator\Parser\DocCommentParser; use PhpKafka\PhpAvroSchemaGenerator\PhpClass\PhpClassPropertyInterface; use PhpParser\ParserFactory; +use PhpParser\PhpVersion; use PHPUnit\Framework\TestCase; /** @@ -20,7 +21,7 @@ public function testGetClassName(): void { $filePath = __DIR__ . '/../../../example/classes/SomeTestClass.php'; $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode((string) file_get_contents($filePath)); self::assertEquals('SomeTestClass', $parser->getClassName()); self::assertEquals('SomeTestClass', $parser->getClassName()); @@ -30,7 +31,7 @@ public function testGetClassNameForInterface(): void { $filePath = __DIR__ . '/../../../example/classes/SomeTestInterface.php'; $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode((string) file_get_contents($filePath)); self::assertNull($parser->getClassName()); } @@ -39,7 +40,7 @@ public function testGetNamespace(): void { $filePath = __DIR__ . '/../../../example/classes/SomeTestClass.php'; $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode((string) file_get_contents($filePath)); self::assertEquals('PhpKafka\\PhpAvroSchemaGenerator\\Example', $parser->getNamespace()); self::assertEquals('PhpKafka\\PhpAvroSchemaGenerator\\Example', $parser->getNamespace()); @@ -49,7 +50,7 @@ public function testGetProperties(): void { $filePath = __DIR__ . '/../../../example/classes/SomeTestClass.php'; $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode((string) file_get_contents($filePath)); $properties = $parser->getProperties(); self::assertCount(16, $properties); @@ -62,7 +63,7 @@ public function testGetProperties(): void public function testClassAndNamespaceAreNullWithNoCode(): void { $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $refObject = new \ReflectionObject($parser); $refProperty = $refObject->getProperty('statements'); $refProperty->setAccessible(true); @@ -76,7 +77,7 @@ public function testClassAndNamespaceAreNullWithNoCode(): void public function testClassWithNoParent(): void { $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode('getNamespace()); self::assertNull($parser->getParentClassName()); @@ -87,7 +88,7 @@ public function testClassWithNoParent(): void public function testClassWithNullableType(): void { $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode('getProperties(); self::assertEquals(1, count($properties)); diff --git a/tests/Integration/Parser/ClassPropertyParserTest.php b/tests/Integration/Parser/ClassPropertyParserTest.php index 751e11c..a612e6d 100644 --- a/tests/Integration/Parser/ClassPropertyParserTest.php +++ b/tests/Integration/Parser/ClassPropertyParserTest.php @@ -7,8 +7,8 @@ use PhpKafka\PhpAvroSchemaGenerator\Parser\ClassParser; use PhpKafka\PhpAvroSchemaGenerator\Parser\ClassPropertyParser; use PhpKafka\PhpAvroSchemaGenerator\Parser\DocCommentParser; -use PhpKafka\PhpAvroSchemaGenerator\PhpClass\PhpClassPropertyInterface; use PhpParser\ParserFactory; +use PhpParser\PhpVersion; use PHPUnit\Framework\TestCase; /** @@ -19,7 +19,7 @@ class ClassPropertyParserTest extends TestCase public function testNullDefaultProperty(): void { $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $parser->setCode(' create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $converter = new PhpClassConverter($parser); $registry = new ClassRegistry($converter); $result = $registry->addClassDirectory('/tmp'); @@ -39,7 +40,7 @@ public function testLoad(): void $classDir = __DIR__ . '/../../../example/classes'; $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $converter = new PhpClassConverter($parser); $registry = (new ClassRegistry($converter))->addClassDirectory($classDir)->load(); @@ -58,7 +59,7 @@ public function testRegisterSchemaFileThatDoesntExist(): void { $fileInfo = new SplFileInfo('somenonexistingfile'); $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $converter = new PhpClassConverter($parser); $registry = new ClassRegistry($converter); @@ -73,13 +74,14 @@ public function testRegisterSchemaFileThatDoesntExist(): void public function testRegisterSchemaFileThatIsNotReadable(): void { - touch('testfile'); - chmod('testfile', 222); + $filePath = '/tmp/test/testfile'; + touch($filePath); + chmod($filePath, 222); - $fileInfo = new SplFileInfo('testfile'); + $fileInfo = new SplFileInfo($filePath); $propertyParser = new ClassPropertyParser(new DocCommentParser()); - $parser = new ClassParser((new ParserFactory())->create(ParserFactory::PREFER_PHP7), $propertyParser); + $parser = new ClassParser((new ParserFactory())->createForVersion(PhpVersion::fromComponents(8,2)), $propertyParser); $converter = new PhpClassConverter($parser); $registry = new ClassRegistry($converter); @@ -94,7 +96,7 @@ public function testRegisterSchemaFileThatIsNotReadable(): void try { $method->invokeArgs($registry, [$fileInfo]); } finally { - unlink('testfile'); + @unlink($filePath); } } } diff --git a/tests/Integration/Registry/SchemaRegistryTest.php b/tests/Integration/Registry/SchemaRegistryTest.php index 9512b73..a5c2c1a 100644 --- a/tests/Integration/Registry/SchemaRegistryTest.php +++ b/tests/Integration/Registry/SchemaRegistryTest.php @@ -105,11 +105,11 @@ public function testRegisterSchemaFileThatDoesntExist(): void public function testRegisterSchemaFileThatIsNotReadable(): void { - touch('testfile'); - chmod('testfile', 222); - - $fileInfo = new SplFileInfo('testfile'); + $filePath = '/tmp/test/testfile'; + touch($filePath); + chmod($filePath, 222); + $fileInfo = new SplFileInfo($filePath); $registry = new SchemaRegistry(); self::expectException(SchemaRegistryException::class); @@ -123,15 +123,16 @@ public function testRegisterSchemaFileThatIsNotReadable(): void try { $method->invokeArgs($registry, [$fileInfo]); } finally { - unlink('testfile'); + @unlink($filePath); } } public function testRegisterSchemaWithInvalidContent(): void { - touch('testfile'); + $filePath = '/tmp/test/testfile'; + touch($filePath); - $fileInfo = new SplFileInfo('testfile'); + $fileInfo = new SplFileInfo($filePath); $registry = new SchemaRegistry(); @@ -146,7 +147,7 @@ public function testRegisterSchemaWithInvalidContent(): void try { $method->invokeArgs($registry, [$fileInfo]); } finally { - unlink('testfile'); + @unlink($filePath); } } } diff --git a/tests/Unit/Merger/SchemaMergerTest.php b/tests/Unit/Merger/SchemaMergerTest.php index 7762e09..5271e7c 100644 --- a/tests/Unit/Merger/SchemaMergerTest.php +++ b/tests/Unit/Merger/SchemaMergerTest.php @@ -4,7 +4,6 @@ namespace PhpKafka\PhpAvroSchemaGenerator\Tests\Unit\Merger; -use AvroSchema; use PhpKafka\PhpAvroSchemaGenerator\Exception\SchemaMergerException; use PhpKafka\PhpAvroSchemaGenerator\Merger\SchemaMerger; use PhpKafka\PhpAvroSchemaGenerator\Optimizer\OptimizerInterface; @@ -522,7 +521,7 @@ public function testMerge(): void self::assertEquals(1, $mergedFiles); self::assertFileExists('/tmp/foobar/com.example.Book.avsc'); unlink('/tmp/foobar/com.example.Book.avsc'); - rmdir('/tmp/foobar'); + $this->cleanupDirectory('/tmp/foobar'); } public function testMergePrimitive(): void @@ -557,7 +556,7 @@ public function testMergePrimitive(): void self::assertFileExists('/tmp/foobar/primitive-type.avsc'); unlink('/tmp/foobar/primitive-type.avsc'); - rmdir('/tmp/foobar'); + $this->cleanupDirectory('/tmp/foobar'); } public function testMergePrimitiveWithOptimizerEnabled(): void @@ -599,7 +598,7 @@ public function testMergePrimitiveWithOptimizerEnabled(): void self::assertFileExists('/tmp/foobar/primitive-type.avsc'); unlink('/tmp/foobar/primitive-type.avsc'); - rmdir('/tmp/foobar'); + $this->cleanupDirectory('/tmp/foobar'); } public function testMergeWithFilenameOption(): void @@ -639,7 +638,7 @@ public function testMergeWithFilenameOption(): void self::assertFileExists('/tmp/foobar/bla.avsc'); unlink('/tmp/foobar/bla.avsc'); - rmdir('/tmp/foobar'); + $this->cleanupDirectory('/tmp/foobar'); } public function testExportSchema(): void @@ -714,4 +713,22 @@ private function reformatJsonString(string $jsonString): string { return json_encode(json_decode($jsonString, false, JSON_THROW_ON_ERROR), JSON_THROW_ON_ERROR); } + + private function cleanupDirectory(string $dir): void + { + if (!is_dir($dir)) { + return; + } + + $files = scandir($dir); + foreach ($files as $file) { + if ($file !== '.' && $file !== '..') { + $path = $dir . '/' . $file; + if (is_file($path)) { + unlink($path); + } + } + } + rmdir($dir); + } }