From 81b06f5a1b9197b7c4409d43980b39b97cb4f6af Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Fri, 23 Jan 2026 17:19:28 +0100 Subject: [PATCH 1/5] improve code quality and increase phpstan level --- lang/php/lib/Avro.php | 14 +- lang/php/lib/AvroDebug.php | 60 ++--- lang/php/lib/AvroGMP.php | 53 ++-- lang/php/lib/AvroIO.php | 8 +- lang/php/lib/AvroUtil.php | 6 +- lang/php/lib/DataFile/AvroDataIO.php | 87 +++---- lang/php/lib/DataFile/AvroDataIOReader.php | 134 +++++++--- lang/php/lib/DataFile/AvroDataIOWriter.php | 169 ++++++++----- lang/php/lib/Datum/AvroIOBinaryDecoder.php | 64 ++--- lang/php/lib/Datum/AvroIOBinaryEncoder.php | 14 +- lang/php/lib/Datum/AvroIODatumReader.php | 233 +++++++++--------- lang/php/lib/Datum/AvroIODatumWriter.php | 76 +++--- .../lib/Datum/AvroIOSchemaMatchException.php | 12 +- lang/php/lib/IO/AvroFile.php | 38 +-- lang/php/lib/IO/AvroStringIO.php | 84 +++---- lang/php/lib/Protocol/AvroProtocol.php | 10 +- lang/php/lib/Protocol/AvroProtocolMessage.php | 9 +- lang/php/lib/Schema/AvroAliasedSchema.php | 4 + lang/php/lib/Schema/AvroArraySchema.php | 2 +- lang/php/lib/Schema/AvroEnumSchema.php | 14 +- lang/php/lib/Schema/AvroField.php | 20 +- lang/php/lib/Schema/AvroLogicalType.php | 19 +- lang/php/lib/Schema/AvroMapSchema.php | 14 +- lang/php/lib/Schema/AvroName.php | 24 +- lang/php/lib/Schema/AvroNamedSchema.php | 8 + lang/php/lib/Schema/AvroNamedSchemata.php | 2 +- lang/php/lib/Schema/AvroRecordSchema.php | 42 +++- lang/php/lib/Schema/AvroSchema.php | 67 +++-- lang/php/lib/Schema/AvroUnionSchema.php | 31 +-- lang/php/phpstan-baseline.neon | 148 +++++++++++ lang/php/phpstan.neon | 7 +- lang/php/test/DataFileTest.php | 3 +- lang/php/test/DatumIOTest.php | 5 +- lang/php/test/InterOpTest.php | 12 +- 34 files changed, 891 insertions(+), 602 deletions(-) create mode 100644 lang/php/phpstan-baseline.neon diff --git a/lang/php/lib/Avro.php b/lang/php/lib/Avro.php index 778944c6a5a..08b68856b8d 100644 --- a/lang/php/lib/Avro.php +++ b/lang/php/lib/Avro.php @@ -49,24 +49,24 @@ class Avro * self::GMP_BIGINTEGER_MODE on 32-bit platforms that have GMP available, * and to self::PHP_BIGINTEGER_MODE otherwise. */ - private static int $biginteger_mode; + private static int $bigIntegerMode; /** * Wrapper method to call each required check. */ - public static function checkPlatform() + public static function checkPlatform(): void { self::check64Bit(); } /** - * @returns bool true if the PHP GMP extension is used and false otherwise. + * @return bool true if the PHP GMP extension is used and false otherwise. * @internal Requires Avro::check64Bit() (exposed via Avro::checkPlatform()) * to have been called to set Avro::$biginteger_mode. */ public static function usesGmp(): bool { - return self::GMP_BIGINTEGER_MODE === self::$biginteger_mode; + return self::GMP_BIGINTEGER_MODE === self::$bigIntegerMode; } /** @@ -74,17 +74,17 @@ public static function usesGmp(): bool * * @throws AvroException if the platform cannot handle long integers. */ - private static function check64Bit() + private static function check64Bit(): void { if (8 !== PHP_INT_SIZE) { if (extension_loaded('gmp')) { - self::$biginteger_mode = self::GMP_BIGINTEGER_MODE; + self::$bigIntegerMode = self::GMP_BIGINTEGER_MODE; } else { throw new AvroException('This platform cannot handle a 64-bit operations. ' .'Please install the GMP PHP extension.'); } } else { - self::$biginteger_mode = self::PHP_BIGINTEGER_MODE; + self::$bigIntegerMode = self::PHP_BIGINTEGER_MODE; } } } diff --git a/lang/php/lib/AvroDebug.php b/lang/php/lib/AvroDebug.php index b64e7b19048..eae3ef1daf5 100644 --- a/lang/php/lib/AvroDebug.php +++ b/lang/php/lib/AvroDebug.php @@ -41,13 +41,13 @@ class AvroDebug /** * @param string $format format string for the given arguments. Passed as is * to vprintf. - * @param array $args array of arguments to pass to vsprinf. - * @param int $debug_level debug level at which to print this statement - * @returns boolean true + * @param list $args array of arguments to pass to vsprinf. + * @param int $debugLevel debug level at which to print this statement + * @return bool true */ - public static function debug($format, $args, $debug_level = self::DEBUG1) + public static function debug(string $format, array $args, int $debugLevel = self::DEBUG1): bool { - if (self::isDebug($debug_level)) { + if (self::isDebug($debugLevel)) { vprintf($format."\n", $args); } @@ -59,37 +59,34 @@ public static function debug($format, $args, $debug_level = self::DEBUG1) * or more verbose than than the current debug level * and false otherwise. */ - public static function isDebug(int $debug_level = self::DEBUG1): bool + public static function isDebug(int $debugLevel = self::DEBUG1): bool { - return self::DEBUG_LEVEL >= $debug_level; + return self::DEBUG_LEVEL >= $debugLevel; } /** - * @param string $str * @param string $joiner string used to join - * @returns string hex-represented bytes of each byte of $str + * @return string hex-represented bytes of each byte of $str * joined by $joiner */ - public static function hexString($str, $joiner = ' ') + public static function hexString(string $str, string $joiner = ' '): string { return implode($joiner, self::hexArray($str)); } /** - * @param string $str - * @returns string[] array of hex representation of each byte of $str + * @return string[] array of hex representation of each byte of $str */ - public static function hexArray($str) + public static function hexArray(string $str): array { return self::bytesArray($str); } /** - * @param string $str * @param string $format format to represent bytes - * @returns string[] array of each byte of $str formatted using $format + * @return string[] array of each byte of $str formatted using $format */ - public static function bytesArray($str, $format = 'x%02x') + public static function bytesArray(string $str, string $format = 'x%02x'): array { $x = []; foreach (str_split($str) as $b) { @@ -100,40 +97,35 @@ public static function bytesArray($str, $format = 'x%02x') } /** - * @param string $str * @param string $joiner string to join bytes of $str - * @returns string of bytes of $str represented in decimal format + * @return string of bytes of $str represented in decimal format * @uses decArray() */ - public static function decString($str, $joiner = ' ') + public static function decString(string $str, string $joiner = ' '): string { return implode($joiner, self::decArray($str)); } /** - * @param string $str - * @returns string[] array of bytes of $str represented in decimal format ('%3d') + * @return string[] array of bytes of $str represented in decimal format ('%3d') */ - public static function decArray($str) + public static function decArray(string $str): array { return self::bytesArray($str, '%3d'); } /** - * @param string $str * @param string $format one of 'ctrl', 'hex', or 'dec'. * See {@link self::asciiArray()} for more description - * @param string $joiner - * @returns string of bytes joined by $joiner + * @return string of bytes joined by $joiner * @uses asciiArray() */ - public static function asciiString($str, $format = 'ctrl', $joiner = ' ') + public static function asciiString(string $str, string $format = 'ctrl', string $joiner = ' '): string { return implode($joiner, self::asciiArray($str, $format)); } /** - * @param string $str * @param string $format one of 'ctrl', 'hex', or 'dec' for control, * hexadecimal, or decimal format for bytes. * - ctrl: ASCII control characters represented as text. @@ -142,16 +134,16 @@ public static function asciiString($str, $format = 'ctrl', $joiner = ' ') * others are represented as a decimal ('%03d') * - hex: bytes represented in hexadecimal ('%02X') * - dec: bytes represented in decimal ('%03d') - * @returns string[] array of bytes represented in the given format. * @throws AvroException + * @return string[] array of bytes represented in the given format. */ - public static function asciiArray($str, $format = 'ctrl') + public static function asciiArray(string $str, string $format = 'ctrl'): array { if (!in_array($format, ['ctrl', 'hex', 'dec'])) { throw new AvroException('Unrecognized format specifier'); } - $ctrl_chars = [ + $ctrlChars = [ 'NUL', 'SOH', 'STX', @@ -191,7 +183,7 @@ public static function asciiArray($str, $format = 'ctrl') if ($db < 32) { switch ($format) { case 'ctrl': - $x[] = str_pad($ctrl_chars[$db], 3, ' ', STR_PAD_LEFT); + $x[] = str_pad($ctrlChars[$db], 3, ' ', STR_PAD_LEFT); break; case 'hex': @@ -199,7 +191,7 @@ public static function asciiArray($str, $format = 'ctrl') break; case 'dec': - $x[] = str_pad($db, 3, '0', STR_PAD_LEFT); + $x[] = str_pad((string) $db, 3, '0', STR_PAD_LEFT); break; } @@ -218,7 +210,7 @@ public static function asciiArray($str, $format = 'ctrl') break; case 'dec': - $x[] = str_pad($db, 3, '0', STR_PAD_LEFT); + $x[] = str_pad((string) $db, 3, '0', STR_PAD_LEFT); break; } @@ -226,7 +218,7 @@ public static function asciiArray($str, $format = 'ctrl') if ('hex' === $format) { $x[] = sprintf("x%02X", $db); } else { - $x[] = str_pad($db, 3, '0', STR_PAD_LEFT); + $x[] = str_pad((string) $db, 3, '0', STR_PAD_LEFT); } } } diff --git a/lang/php/lib/AvroGMP.php b/lang/php/lib/AvroGMP.php index 44828627a5e..cc1cd2a4750 100644 --- a/lang/php/lib/AvroGMP.php +++ b/lang/php/lib/AvroGMP.php @@ -32,33 +32,33 @@ class AvroGMP /** * @var \GMP memoized GMP resource for zero */ - private static $gmp_0; + private static \GMP $gmp_0; /** * @var \GMP memoized GMP resource for one (1) */ - private static $gmp_1; + private static \GMP $gmp_1; /** * @var \GMP memoized GMP resource for two (2) */ - private static $gmp_2; + private static \GMP $gmp_2; /** * @var \GMP memoized GMP resource for 0x7f */ - private static $gmp_0x7f; + private static \GMP $gmp_0x7f; /** * @var \GMP memoized GMP resource for 64-bit ~0x7f */ - private static $gmp_n0x7f; + private static \GMP $gmp_n0x7f; /** * @var \GMP memoized GMP resource for 64-bits of 1 */ - private static $gmp_0xfs; + private static \GMP $gmp_0xfs; /** * @param int|string $n integer (or string representation of integer) to encode * @return string $bytes of the long $n encoded per the Avro spec */ - public static function encodeLong($n) + public static function encodeLong(int|string $n): string { $g = gmp_init($n); $g = gmp_xor( @@ -77,11 +77,10 @@ public static function encodeLong($n) /** * @interal Only works up to shift 63 (doesn't wrap bits around). - * @param int|resource|string $g * @param int $shift number of bits to shift left - * @returns resource $g shifted left + * @return \GMP $g shifted left */ - public static function shiftLeft($g, $shift) + public static function shiftLeft(int|string|\GMP $g, int $shift) { if (0 == $shift) { return $g; @@ -104,21 +103,19 @@ public static function shiftLeft($g, $shift) } /** - * @param \GMP $g resource * @return \GMP resource 64-bit two's complement of input. */ - public static function gmpTwosComplement($g) + public static function gmpTwosComplement(int|string|\GMP $g) { return gmp_neg(gmp_sub(gmp_pow(self::gmp_2(), 64), $g)); } /** * Arithmetic right shift - * @param int|resource|string $g * @param int $shift number of bits to shift right - * @returns resource $g shifted right $shift bits + * @return \GMP $g shifted right $shift bits */ - public static function shiftRight($g, $shift) + public static function shiftRight(int|string|\GMP $g, int $shift) { if (0 == $shift) { return $g; @@ -143,13 +140,11 @@ public static function shiftRight($g, $shift) return $m; } - // phpcs:enable - /** * @param int[] $bytes array of ascii codes of bytes to decode * @return string represenation of decoded long. */ - public static function decodeLongFromArray($bytes) + public static function decodeLongFromArray(array $bytes): string { $b = array_shift($bytes); $g = gmp_init($b & 0x7F); @@ -167,7 +162,7 @@ public static function decodeLongFromArray($bytes) // phpcs:disable PSR1.Methods.CamelCapsMethodName /** - * @returns \GMP GMP resource for two (2) + * @return \GMP GMP resource for two (2) */ private static function gmp_2() { @@ -179,9 +174,9 @@ private static function gmp_2() } /** - * @returns resource GMP resource for 64-bits of 1 + * @return \GMP GMP resource for 64-bits of 1 */ - private static function gmp_0xfs() + private static function gmp_0xfs(): \GMP { if (!isset(self::$gmp_0xfs)) { self::$gmp_0xfs = gmp_init('0xffffffffffffffff'); @@ -191,9 +186,9 @@ private static function gmp_0xfs() } /** - * @returns resource GMP resource for one (1) + * @return \GMP GMP resource for one (1) */ - private static function gmp_1() + private static function gmp_1(): \GMP { if (!isset(self::$gmp_1)) { self::$gmp_1 = gmp_init('1'); @@ -203,9 +198,9 @@ private static function gmp_1() } /** - * @returns resource GMP resource for zero + * @return \GMP GMP resource for zero */ - private static function gmp_0() + private static function gmp_0(): \GMP { if (!isset(self::$gmp_0)) { self::$gmp_0 = gmp_init('0'); @@ -215,9 +210,9 @@ private static function gmp_0() } /** - * @returns resource GMP resource for 64-bit ~0x7f + * @return \GMP GMP resource for 64-bit ~0x7f */ - private static function gmp_n0x7f() + private static function gmp_n0x7f(): \GMP { if (!isset(self::$gmp_n0x7f)) { self::$gmp_n0x7f = gmp_init('0xffffffffffffff80'); @@ -227,9 +222,9 @@ private static function gmp_n0x7f() } /** - * @returns resource GMP resource for 0x7f + * @return \GMP GMP resource for 0x7f */ - private static function gmp_0x7f() + private static function gmp_0x7f(): \GMP { if (!isset(self::$gmp_0x7f)) { self::$gmp_0x7f = gmp_init('0x7f'); diff --git a/lang/php/lib/AvroIO.php b/lang/php/lib/AvroIO.php index 4cec58f80fc..869c76a0ff3 100644 --- a/lang/php/lib/AvroIO.php +++ b/lang/php/lib/AvroIO.php @@ -58,8 +58,8 @@ public function read(int $len): string; /** * Append bytes to this buffer. (Nothing more is needed to support Avro.) * @param string $bytes bytes to write - * @returns int count of bytes written. * @throws IO\AvroIOException if $args is not a string value. + * @return int count of bytes written. */ public function write(string $bytes): int; @@ -75,15 +75,15 @@ public function tell(): int; * * @param int $whence one of AvroIO::SEEK_SET, AvroIO::SEEK_CUR, * or Avro::SEEK_END - * @returns boolean true * * @throws IO\AvroIOException + * @return bool true */ public function seek(int $offset, int $whence = self::SEEK_SET): bool; /** * Flushes any buffered data to the AvroIO object. - * @returns bool true upon success. + * @return bool true upon success. */ public function flush(): bool; @@ -94,7 +94,7 @@ public function flush(): bool; * Note isEof() is not like eof in C or feof in PHP: * it returns TRUE if the *next* read would be end of file, * rather than if the *most recent* read read end of file. - * @returns bool true if at the end of file, and false otherwise + * @return bool true if at the end of file, and false otherwise */ public function isEof(): bool; diff --git a/lang/php/lib/AvroUtil.php b/lang/php/lib/AvroUtil.php index cdd4e2f29c7..b32d8a8eed5 100644 --- a/lang/php/lib/AvroUtil.php +++ b/lang/php/lib/AvroUtil.php @@ -31,10 +31,10 @@ class AvroUtil * or a list (an array with monotonically increasing integer indicies * starting with zero). * - * @param array $ary array to test - * @returns true if the array is a list and false otherwise. + * @param mixed $ary array to test + * @return bool true if the array is a list and false otherwise. */ - public static function isList($ary): bool + public static function isList(mixed $ary): bool { if (is_array($ary)) { $i = 0; diff --git a/lang/php/lib/DataFile/AvroDataIO.php b/lang/php/lib/DataFile/AvroDataIO.php index 7027664b699..06ca2aee6a0 100644 --- a/lang/php/lib/DataFile/AvroDataIO.php +++ b/lang/php/lib/DataFile/AvroDataIO.php @@ -24,6 +24,7 @@ use Apache\Avro\Datum\AvroIODatumReader; use Apache\Avro\Datum\AvroIODatumWriter; use Apache\Avro\IO\AvroFile; +use Apache\Avro\IO\AvroIOException; use Apache\Avro\Schema\AvroSchema; class AvroDataIO @@ -75,9 +76,9 @@ class AvroDataIO public const BZIP2_CODEC = 'bzip2'; /** - * @var array array of valid codec names + * @var array array of valid codec names */ - private static $validCodecs = [ + private static array $validCodecs = [ self::NULL_CODEC, self::DEFLATE_CODEC, self::SNAPPY_CODEC, @@ -91,7 +92,7 @@ class AvroDataIO private static ?AvroSchema $metadataSchema = null; /** - * @returns int count of bytes in the initial "magic" segment of the + * @return int count of bytes in the initial "magic" segment of the * Avro container file header */ public static function magicSize(): int @@ -122,90 +123,84 @@ public static function metadataSchema(): AvroSchema /** * @param string $file_path file_path of file to open * @param string $mode one of AvroFile::READ_MODE or AvroFile::WRITE_MODE - * @param string $schemaJson JSON of writer's schema + * @param null|string $schemaJson JSON of writer's schema * @param string $codec compression codec - * @returns AvroDataIOWriter instance of AvroDataIOWriter * - * @throws AvroDataIOException if $writers_schema is not provided - * or if an invalid $mode is given. + * @throws AvroDataIOException if $writers_schema is not provided or if an invalid $mode is given. + * @throws AvroIOException */ public static function openFile( - $file_path, - $mode = AvroFile::READ_MODE, - $schemaJson = null, - $codec = self::NULL_CODEC - ) { + string $file_path, + string $mode = AvroIO::READ_MODE, + ?string $schemaJson = null, + string $codec = self::NULL_CODEC + ): AvroDataIOReader|AvroDataIOWriter { $schema = !is_null($schemaJson) ? AvroSchema::parse($schemaJson) : null; - $io = false; switch ($mode) { - case AvroFile::WRITE_MODE: + case AvroIO::WRITE_MODE: if (is_null($schema)) { throw new AvroDataIOException('Writing an Avro file requires a schema.'); } - $file = new AvroFile($file_path, AvroFile::WRITE_MODE); - $io = self::openWriter($file, $schema, $codec); + $file = new AvroFile($file_path, AvroIO::WRITE_MODE); - break; - case AvroFile::READ_MODE: - $file = new AvroFile($file_path, AvroFile::READ_MODE); - $io = self::openReader($file, $schema); + return self::openWriter($file, $schema, $codec); + + case AvroIO::READ_MODE: + $file = new AvroFile($file_path, AvroIO::READ_MODE); + + return self::openReader($file, $schema); - break; default: throw new AvroDataIOException( sprintf( "Only modes '%s' and '%s' allowed. You gave '%s'.", - AvroFile::READ_MODE, - AvroFile::WRITE_MODE, + AvroIO::READ_MODE, + AvroIO::WRITE_MODE, $mode ) ); } - - return $io; } /** - * @param string $codec - * @returns boolean true if $codec is a valid codec value and false otherwise + * @return bool true if $codec is a valid codec value and false otherwise */ - public static function isValidCodec($codec) + public static function isValidCodec(?string $codec): bool { - return in_array($codec, self::validCodecs()); + return is_string($codec) && in_array($codec, self::validCodecs()); } /** - * @returns array array of valid codecs + * @return array array of valid codecs */ - public static function validCodecs() + public static function validCodecs(): array { return self::$validCodecs; } /** - * @param AvroIO $io - * @param AvroSchema $schema - * @param string $codec - * @returns AvroDataIOWriter + * @throws AvroDataIOException */ - protected static function openWriter($io, $schema, $codec = self::NULL_CODEC) + protected static function openWriter(AvroIO $io, ?AvroSchema $schema, string $codec = self::NULL_CODEC): AvroDataIOWriter { - $writer = new AvroIODatumWriter($schema); - - return new AvroDataIOWriter($io, $writer, $schema, $codec); + return new AvroDataIOWriter( + io: $io, + datumWriter: new AvroIODatumWriter($schema), + writersSchema: $schema, + codec: $codec + ); } /** - * @param AvroIO $io - * @param AvroSchema $schema - * @returns AvroDataIOReader + * @throws AvroDataIOException */ - protected static function openReader($io, $schema) + protected static function openReader(AvroIO $io, ?AvroSchema $schema): AvroDataIOReader { - $reader = new AvroIODatumReader(null, $schema); - - return new AvroDataIOReader($io, $reader); + return new AvroDataIOReader( + io: $io, + datumReader: new AvroIODatumReader(null, $schema) + ); } } diff --git a/lang/php/lib/DataFile/AvroDataIOReader.php b/lang/php/lib/DataFile/AvroDataIOReader.php index 6990fba8c67..177b73b6e27 100644 --- a/lang/php/lib/DataFile/AvroDataIOReader.php +++ b/lang/php/lib/DataFile/AvroDataIOReader.php @@ -35,7 +35,7 @@ class AvroDataIOReader { public string $sync_marker; /** - * @var array object container metadata + * @var array object container metadata */ public array $metadata; @@ -43,7 +43,7 @@ class AvroDataIOReader /** * @var int count of items in block */ - private int $block_count; + private int $blockCount; /** * @var string compression codec @@ -52,7 +52,7 @@ class AvroDataIOReader /** * @param AvroIO $io source from which to read - * @param AvroIODatumReader $datum_reader reader that understands + * @param AvroIODatumReader $datumReader reader that understands * the data schema * @throws AvroDataIOException if $io is not an instance of AvroIO * or the codec specified in the header @@ -61,7 +61,7 @@ class AvroDataIOReader */ public function __construct( private AvroIO $io, - private AvroIODatumReader $datum_reader + private AvroIODatumReader $datumReader ) { $this->decoder = new AvroIOBinaryDecoder($this->io); $this->readHeader(); @@ -72,10 +72,10 @@ public function __construct( } $this->codec = $codec; - $this->block_count = 0; + $this->blockCount = 0; // FIXME: Seems unsanitary to set writers_schema here. // Can't constructor take it as an argument? - $this->datum_reader->setWritersSchema( + $this->datumReader->setWritersSchema( AvroSchema::parse($this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR]) ); } @@ -83,7 +83,7 @@ public function __construct( /** * @throws AvroException * @throws AvroIOException - * @return array of data from object container. + * @return list of data from object container. * @internal Would be nice to implement data() as an iterator, I think */ public function data(): array @@ -91,52 +91,39 @@ public function data(): array $data = []; $decoder = $this->decoder; while (true) { - if (0 == $this->block_count) { + if (0 == $this->blockCount) { if ($this->isEof()) { break; } if ($this->skipSync()) { + /** @phpstan-ignore if.alwaysFalse */ if ($this->isEof()) { break; } } $length = $this->readBlockHeader(); - if (AvroDataIO::DEFLATE_CODEC == $this->codec) { + if (AvroDataIO::DEFLATE_CODEC === $this->codec) { $compressed = $decoder->read($length); - $datum = gzinflate($compressed); + $datum = $this->gzUncompress($compressed); $decoder = new AvroIOBinaryDecoder(new AvroStringIO($datum)); } elseif (AvroDataIO::ZSTANDARD_CODEC === $this->codec) { - if (!extension_loaded('zstd')) { - throw new AvroException('Please install ext-zstd to use zstandard compression.'); - } $compressed = $decoder->read($length); - $datum = zstd_uncompress($compressed); + $datum = $this->zstdUncompress($compressed); $decoder = new AvroIOBinaryDecoder(new AvroStringIO($datum)); } elseif (AvroDataIO::SNAPPY_CODEC === $this->codec) { - if (!extension_loaded('snappy')) { - throw new AvroException('Please install ext-snappy to use snappy compression.'); - } $compressed = $decoder->read($length); - $crc32 = unpack('N', substr((string) $compressed, -4))[1]; - $datum = snappy_uncompress(substr((string) $compressed, 0, -4)); - if ($crc32 === crc32($datum)) { - $decoder = new AvroIOBinaryDecoder(new AvroStringIO($datum)); - } else { - $decoder = new AvroIOBinaryDecoder(new AvroStringIO(snappy_uncompress($datum))); - } + $datum = $this->snappyUncompress($compressed); + $decoder = new AvroIOBinaryDecoder(new AvroStringIO($datum)); } elseif (AvroDataIO::BZIP2_CODEC === $this->codec) { - if (!extension_loaded('bz2')) { - throw new AvroException('Please install ext-bz2 to use bzip2 compression.'); - } $compressed = $decoder->read($length); - $datum = bzdecompress($compressed); + $datum = $this->bzUncompress($compressed); $decoder = new AvroIOBinaryDecoder(new AvroStringIO($datum)); } } - $data[] = $this->datum_reader->read($decoder); - --$this->block_count; + $data[] = $this->datumReader->read($decoder); + --$this->blockCount; } return $data; @@ -177,7 +164,7 @@ private function readHeader(): void ); } - $this->metadata = $this->datum_reader->readData( + $this->metadata = $this->datumReader->readData( AvroDataIO::metadataSchema(), AvroDataIO::metadataSchema(), $this->decoder @@ -187,19 +174,16 @@ private function readHeader(): void /** * @uses AvroIO::seek() - * @param mixed $offset - * @param mixed $whence */ - private function seek($offset, $whence): bool + private function seek(int $offset, int $whence): bool { return $this->io->seek($offset, $whence); } /** * @uses AvroIO::read() - * @param mixed $len */ - private function read($len): string + private function read(int $len): string { return $this->io->read($len); } @@ -214,8 +198,8 @@ private function isEof(): bool private function skipSync(): bool { - $proposed_sync_marker = $this->read(AvroDataIO::SYNC_SIZE); - if ($proposed_sync_marker != $this->sync_marker) { + $proposedSyncMarker = $this->read(AvroDataIO::SYNC_SIZE); + if ($proposedSyncMarker !== $this->sync_marker) { $this->seek(-AvroDataIO::SYNC_SIZE, AvroIO::SEEK_CUR); return false; @@ -227,12 +211,82 @@ private function skipSync(): bool /** * Reads the block header (which includes the count of items in the block * and the length in bytes of the block) - * @returns int length in bytes of the block. + * @return int length in bytes of the block. */ private function readBlockHeader(): string|int { - $this->block_count = $this->decoder->readLong(); + $this->blockCount = $this->decoder->readLong(); return $this->decoder->readLong(); } + + /** + * @throws AvroException + */ + private function gzUncompress(string $compressed): string + { + $datum = gzinflate($compressed); + + if (false === $datum) { + throw new AvroException('gzip/deflate uncompression failed.'); + } + + return $datum; + } + + /** + * @throws AvroException + */ + private function zstdUncompress(string $compressed): string + { + if (!extension_loaded('zstd')) { + throw new AvroException('Please install ext-zstd to use zstandard compression.'); + } + $datum = zstd_uncompress($compressed); + + if (false === $datum) { + throw new AvroException('zstd uncompression failed.'); + } + + return $datum; + } + + /** + * @throws AvroException + */ + private function bzUncompress(string $compressed): string + { + if (!extension_loaded('bz2')) { + throw new AvroException('Please install ext-bz2 to use bzip2 compression.'); + } + $datum = bzdecompress($compressed); + + if (false === $datum) { + throw new AvroException('bz uncompression failed.'); + } + + return $datum; + } + + /** + * @throws AvroException + */ + private function snappyUncompress(string $compressed): string + { + if (!extension_loaded('snappy')) { + throw new AvroException('Please install ext-snappy to use snappy compression.'); + } + $crc32 = unpack('N', substr((string) $compressed, -4))[1]; + $datum = snappy_uncompress(substr((string) $compressed, 0, -4)); + + if (false === $datum) { + throw new AvroException('snappy uncompression failed.'); + } + + if ($crc32 !== crc32($datum)) { + throw new AvroException('snappy uncompression failed - crc32 mismatch.'); + } + + return $datum; + } } diff --git a/lang/php/lib/DataFile/AvroDataIOWriter.php b/lang/php/lib/DataFile/AvroDataIOWriter.php index 1b76fd0d153..4ad9e2422e6 100644 --- a/lang/php/lib/DataFile/AvroDataIOWriter.php +++ b/lang/php/lib/DataFile/AvroDataIOWriter.php @@ -46,18 +46,18 @@ class AvroDataIOWriter */ private AvroStringIO $buffer; - private AvroIODatumWriter $datum_writer; + private AvroIODatumWriter $datumWriter; /** * @var AvroIOBinaryEncoder encoder for buffer */ - private AvroIOBinaryEncoder $buffer_encoder; + private AvroIOBinaryEncoder $bufferEncoder; /** * @var int count of items written to block */ - private int $block_count; + private int $blockCount; /** - * @var array map of object container metadata + * @var array map of object container metadata */ private array $metadata; /** @@ -67,52 +67,49 @@ class AvroDataIOWriter /** * @var string sync marker */ - private string $sync_marker; + private string $syncMarker; public function __construct( AvroIO $io, - AvroIODatumWriter $datum_writer, - string|AvroSchema|null $writers_schema = null, + AvroIODatumWriter $datumWriter, + string|AvroSchema|null $writersSchema = null, string $codec = AvroDataIO::NULL_CODEC ) { $this->io = $io; - $this->datum_writer = $datum_writer; + $this->datumWriter = $datumWriter; $this->encoder = new AvroIOBinaryEncoder($this->io); $this->buffer = new AvroStringIO(); - $this->buffer_encoder = new AvroIOBinaryEncoder($this->buffer); - $this->block_count = 0; + $this->bufferEncoder = new AvroIOBinaryEncoder($this->buffer); + $this->blockCount = 0; $this->metadata = []; - if ($writers_schema) { + if ($writersSchema) { if (!AvroDataIO::isValidCodec($codec)) { throw new AvroDataIOException( sprintf('codec %s is not supported', $codec) ); } - $this->sync_marker = self::generateSyncMarker(); + $this->syncMarker = self::generateSyncMarker(); $this->metadata[AvroDataIO::METADATA_CODEC_ATTR] = $this->codec = $codec; - $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = (string) $writers_schema; + $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = (string) $writersSchema; $this->writeHeader(); } else { $dfr = new AvroDataIOReader($this->io, new AvroIODatumReader()); - $this->sync_marker = $dfr->sync_marker; + $this->syncMarker = $dfr->sync_marker; $this->metadata[AvroDataIO::METADATA_CODEC_ATTR] = $this->codec = $dfr->metadata[AvroDataIO::METADATA_CODEC_ATTR]; $schema_from_file = $dfr->metadata[AvroDataIO::METADATA_SCHEMA_ATTR]; $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = $schema_from_file; - $this->datum_writer->writersSchema = AvroSchema::parse($schema_from_file); + $this->datumWriter->writersSchema = AvroSchema::parse($schema_from_file); $this->seek(0, SEEK_END); } } - /** - * @param mixed $datum - */ - public function append($datum) + public function append(mixed $datum): void { - $this->datum_writer->write($datum, $this->buffer_encoder); - $this->block_count++; + $this->datumWriter->write($datum, $this->bufferEncoder); + $this->blockCount++; if ($this->buffer->length() >= AvroDataIO::SYNC_INTERVAL) { $this->writeBlock(); @@ -121,10 +118,9 @@ public function append($datum) /** * Flushes buffer to AvroIO object container and closes it. - * @return mixed value of $io->close() * @see AvroIO::close() */ - public function close() + public function close(): bool { $this->flush(); @@ -132,9 +128,9 @@ public function close() } /** - * @returns string a new, unique sync marker. + * @return string a new, unique sync marker. */ - private static function generateSyncMarker() + private static function generateSyncMarker(): string { // From https://php.net/manual/en/function.mt-rand.php comments return pack( @@ -156,29 +152,27 @@ private static function generateSyncMarker() private function writeHeader(): void { $this->write(AvroDataIO::magic()); - $this->datum_writer->writeData( + $this->datumWriter->writeData( AvroDataIO::metadataSchema(), $this->metadata, $this->encoder ); - $this->write($this->sync_marker); + $this->write($this->syncMarker); } /** * @param string $bytes * @uses AvroIO::write() */ - private function write($bytes) + private function write($bytes): int { return $this->io->write($bytes); } /** - * @param int $offset - * @param int $whence * @uses AvroIO::seek() */ - private function seek($offset, $whence) + private function seek(int $offset, int $whence): bool { return $this->io->seek($offset, $whence); } @@ -186,50 +180,99 @@ private function seek($offset, $whence) /** * Writes a block of data to the AvroIO object container. */ - private function writeBlock() + private function writeBlock(): void { - if ($this->block_count > 0) { - $this->encoder->writeLong($this->block_count); - $to_write = (string) $this->buffer; - - if (AvroDataIO::DEFLATE_CODEC === $this->codec) { - $to_write = gzdeflate($to_write); - } elseif (AvroDataIO::ZSTANDARD_CODEC === $this->codec) { - if (!extension_loaded('zstd')) { - throw new AvroException('Please install ext-zstd to use zstandard compression.'); - } - $to_write = zstd_compress($to_write); - } elseif (AvroDataIO::SNAPPY_CODEC === $this->codec) { - if (!extension_loaded('snappy')) { - throw new AvroException('Please install ext-snappy to use snappy compression.'); - } - $crc32 = crc32($to_write); - $compressed = snappy_compress($to_write); - $to_write = pack('a*N', $compressed, $crc32); - } elseif (AvroDataIO::BZIP2_CODEC === $this->codec) { - if (!extension_loaded('bz2')) { - throw new AvroException('Please install ext-bz2 to use bzip2 compression.'); - } - $to_write = bzcompress($to_write); - } + if ($this->blockCount > 0) { + $this->encoder->writeLong($this->blockCount); + $toWrite = (string) $this->buffer; - $this->encoder->writeLong(strlen($to_write)); - $this->write($to_write); - $this->write($this->sync_marker); + $toWrite = match ($this->codec) { + AvroDataIO::DEFLATE_CODEC => $this->gzCompress($toWrite), + AvroDataIO::ZSTANDARD_CODEC => $this->zstdCompress($toWrite), + AvroDataIO::SNAPPY_CODEC => $this->snappyCompress($toWrite), + AvroDataIO::BZIP2_CODEC => $this->bzCompress($toWrite), + default => $toWrite, + }; + + $this->encoder->writeLong(strlen($toWrite)); + $this->write($toWrite); + $this->write($this->syncMarker); $this->buffer->truncate(); - $this->block_count = 0; + $this->blockCount = 0; } } /** * Flushes biffer to AvroIO object container. - * @returns mixed value of $io->flush() * @see AvroIO::flush() */ - private function flush() + private function flush(): void { $this->writeBlock(); + $this->io->flush(); + } + + /** + * @throws AvroException + */ + private function gzCompress(string $data): string + { + $data = gzdeflate($data); + if (false === $data) { + throw new AvroException('Deflate compression failed.'); + } + + return $data; + } + + /** + * @throws AvroException + */ + private function zstdCompress(string $data): string + { + if (!extension_loaded('zstd')) { + throw new AvroException('Please install ext-zstd to use zstandard compression.'); + } + $data = zstd_compress($data); + + if (false === $data) { + throw new AvroException('zstd compression failed.'); + } + + return $data; + } + + /** + * @throws AvroException + */ + private function snappyCompress(string $data): string + { + if (!extension_loaded('snappy')) { + throw new AvroException('Please install ext-snappy to use snappy compression.'); + } + $crc32 = crc32($data); + $compressed = snappy_compress($data); + if (false === $compressed) { + throw new AvroException('snappy compression failed.'); + } + + return pack('a*N', $compressed, $crc32); + } + + /** + * @throws AvroException + */ + private function bzCompress(string $toWrite): string + { + if (!extension_loaded('bz2')) { + throw new AvroException('Please install ext-bz2 to use bzip2 compression.'); + } + $toWrite = bzcompress($toWrite); + + if (is_int($toWrite)) { + throw new AvroException("bz compression failed (error: {$toWrite})."); + } - return $this->io->flush(); + return $toWrite; } } diff --git a/lang/php/lib/Datum/AvroIOBinaryDecoder.php b/lang/php/lib/Datum/AvroIOBinaryDecoder.php index 6a7956c2e30..ac5fcf7e929 100644 --- a/lang/php/lib/Datum/AvroIOBinaryDecoder.php +++ b/lang/php/lib/Datum/AvroIOBinaryDecoder.php @@ -48,16 +48,13 @@ public function __construct( } /** - * @returns null + * @return null */ public function readNull() { return null; } - /** - * @returns boolean - */ public function readBoolean(): bool { return 1 === ord($this->nextByte()); @@ -65,7 +62,6 @@ public function readBoolean(): bool /** * @param int $len count of bytes to read - * @returns string */ public function read(int $len): string { @@ -77,9 +73,6 @@ public function readInt(): int return (int) $this->readLong(); } - /** - * @returns string|int - */ public function readLong(): string|int { $byte = ord($this->nextByte()); @@ -133,9 +126,6 @@ public static function intBitsToFloat(string $bits): float return (float) $float[1]; } - /** - * @returns double - */ public function readDouble(): float { return self::longBitsToDouble($this->read(8)); @@ -147,7 +137,7 @@ public function readDouble(): float * XXX: This is not endian-aware! See comments in * {@link AvroIOBinaryEncoder::floatToIntBits()} for details. */ - public static function longBitsToDouble(string $bits) + public static function longBitsToDouble(string $bits): float { $double = unpack('e', $bits); @@ -157,16 +147,12 @@ public static function longBitsToDouble(string $bits) /** * A string is encoded as a long followed by that many bytes * of UTF-8 encoded character data. - * @returns string */ public function readString(): string { return $this->readBytes(); } - /** - * @returns string - */ public function readBytes(): string { return $this->read($this->readLong()); @@ -174,7 +160,7 @@ public function readBytes(): string public function skipNull(): void { - return; + } public function skipBoolean(): void @@ -224,61 +210,61 @@ public function skipBytes(): void $this->skip($this->readLong()); } - public function skipFixed(AvroFixedSchema $writers_schema, AvroIOBinaryDecoder $decoder): void + public function skipFixed(AvroFixedSchema $writersSchema, AvroIOBinaryDecoder $decoder): void { - $decoder->skip($writers_schema->size()); + $decoder->skip($writersSchema->size()); } - public function skipEnum(AvroSchema $writers_schema, AvroIOBinaryDecoder $decoder): void + public function skipEnum(AvroSchema $writersSchema, AvroIOBinaryDecoder $decoder): void { $decoder->skipInt(); } - public function skipUnion(AvroUnionSchema $writers_schema, AvroIOBinaryDecoder $decoder): void + public function skipUnion(AvroUnionSchema $writersSchema, AvroIOBinaryDecoder $decoder): void { $index = $decoder->readLong(); - AvroIODatumReader::skipData($writers_schema->schemaByIndex($index), $decoder); + AvroIODatumReader::skipData($writersSchema->schemaByIndex($index), $decoder); } - public function skipRecord(AvroRecordSchema $writers_schema, AvroIOBinaryDecoder $decoder): void + public function skipRecord(AvroRecordSchema $writersSchema, AvroIOBinaryDecoder $decoder): void { - foreach ($writers_schema->fields() as $f) { + foreach ($writersSchema->fields() as $f) { AvroIODatumReader::skipData($f->type(), $decoder); } } - public function skipArray(AvroArraySchema $writers_schema, AvroIOBinaryDecoder $decoder): void + public function skipArray(AvroArraySchema $writersSchema, AvroIOBinaryDecoder $decoder): void { - $block_count = $decoder->readLong(); - while (0 !== $block_count) { - if ($block_count < 0) { + $blockCount = $decoder->readLong(); + while (0 !== $blockCount) { + if ($blockCount < 0) { $decoder->skip($this->readLong()); } - for ($i = 0; $i < $block_count; $i++) { - AvroIODatumReader::skipData($writers_schema->items(), $decoder); + for ($i = 0; $i < $blockCount; $i++) { + AvroIODatumReader::skipData($writersSchema->items(), $decoder); } - $block_count = $decoder->readLong(); + $blockCount = $decoder->readLong(); } } - public function skipMap(AvroMapSchema $writers_schema, AvroIOBinaryDecoder $decoder): void + public function skipMap(AvroMapSchema $writersSchema, AvroIOBinaryDecoder $decoder): void { - $block_count = $decoder->readLong(); - while (0 !== $block_count) { - if ($block_count < 0) { + $blockCount = $decoder->readLong(); + while (0 !== $blockCount) { + if ($blockCount < 0) { $decoder->skip($this->readLong()); } - for ($i = 0; $i < $block_count; $i++) { + for ($i = 0; $i < $blockCount; $i++) { $decoder->skipString(); - AvroIODatumReader::skipData($writers_schema->values(), $decoder); + AvroIODatumReader::skipData($writersSchema->values(), $decoder); } - $block_count = $decoder->readLong(); + $blockCount = $decoder->readLong(); } } /** - * @returns string the next byte from $this->io. * @throws AvroException if the next byte cannot be read. + * @return string the next byte from $this->io. */ private function nextByte(): string { diff --git a/lang/php/lib/Datum/AvroIOBinaryEncoder.php b/lang/php/lib/Datum/AvroIOBinaryEncoder.php index 6d5aa624307..1ef350809a7 100644 --- a/lang/php/lib/Datum/AvroIOBinaryEncoder.php +++ b/lang/php/lib/Datum/AvroIOBinaryEncoder.php @@ -45,7 +45,7 @@ public function __construct( */ public function writeNull($datum): void { - return; + } public function writeBoolean(bool $datum): void @@ -75,7 +75,7 @@ public function writeLong(int|string $n): void /** * @param int|string $n - * @returns string long $n encoded as bytes + * @return string long $n encoded as bytes * @internal This relies on 64-bit PHP. */ public static function encodeLong($n) @@ -121,7 +121,7 @@ public function writeFloat($datum): void * encoding required by the Avro spec. * * @param float $float - * @returns string bytes + * @return string bytes * @see Avro::checkPlatform() */ public static function floatToIntBits($float): string @@ -144,7 +144,7 @@ public function writeDouble(float $datum): void * {@link AvroIOBinaryEncoder::floatToIntBits()} for details. * * @param float $double - * @returns string bytes + * @return string bytes */ public static function doubleToLongBits($double): string { @@ -172,9 +172,7 @@ public function writeDecimal(string $decimal, int $scale, int $precision): void } $value = ((float) $decimal) * (10 ** $scale); - if (!is_int($value)) { - $value = (int) round($value); - } + $value = (int) round($value); $maxValue = 10 ** $precision; if (abs($value) >= $maxValue) { @@ -207,7 +205,7 @@ public function writeDecimal(string $decimal, int $scale, int $precision): void $this->writeBytes($value); } - private static function getMostSignificantBitAt($bytes, $offset): int + private static function getMostSignificantBitAt(string $bytes, int $offset): int { return ord($bytes[$offset]) & 0x80; } diff --git a/lang/php/lib/Datum/AvroIODatumReader.php b/lang/php/lib/Datum/AvroIODatumReader.php index 57768e31e0d..0d7a8bc50f2 100644 --- a/lang/php/lib/Datum/AvroIODatumReader.php +++ b/lang/php/lib/Datum/AvroIODatumReader.php @@ -43,50 +43,50 @@ class AvroIODatumReader { public function __construct( - private ?AvroSchema $writers_schema = null, - private ?AvroSchema $readers_schema = null + private ?AvroSchema $writersSchema = null, + private ?AvroSchema $readersSchema = null ) { } - public function setWritersSchema(AvroSchema $readers_schema): void + public function setWritersSchema(AvroSchema $schema): void { - $this->writers_schema = $readers_schema; + $this->writersSchema = $schema; } public function read(AvroIOBinaryDecoder $decoder): mixed { - if (is_null($this->readers_schema)) { - $this->readers_schema = $this->writers_schema; + if (is_null($this->readersSchema)) { + $this->readersSchema = $this->writersSchema; } return $this->readData( - $this->writers_schema, - $this->readers_schema, + $this->writersSchema, + $this->readersSchema, $decoder ); } public function readData( - AvroSchema $writers_schema, - AvroSchema $readers_schema, + AvroSchema $writersSchema, + AvroSchema $readersSchema, AvroIOBinaryDecoder $decoder ): mixed { // Schema resolution: reader's schema is a union, writer's schema is not if ( - $readers_schema instanceof AvroUnionSchema - && AvroSchema::UNION_SCHEMA === $readers_schema->type() - && AvroSchema::UNION_SCHEMA !== $writers_schema->type() + $readersSchema instanceof AvroUnionSchema + && AvroSchema::UNION_SCHEMA === $readersSchema->type() + && AvroSchema::UNION_SCHEMA !== $writersSchema->type() ) { - foreach ($readers_schema->schemas() as $schema) { - if (self::schemasMatch($writers_schema, $schema)) { - return $this->readData($writers_schema, $schema, $decoder); + foreach ($readersSchema->schemas() as $schema) { + if (self::schemasMatch($writersSchema, $schema)) { + return $this->readData($writersSchema, $schema, $decoder); } } - throw new AvroIOSchemaMatchException($writers_schema, $readers_schema); + throw new AvroIOSchemaMatchException($writersSchema, $readersSchema); } - return match ($writers_schema->type()) { + return match ($writersSchema->type()) { AvroSchema::NULL_TYPE => $decoder->readNull(), AvroSchema::BOOLEAN_TYPE => $decoder->readBoolean(), AvroSchema::INT_TYPE => $decoder->readInt(), @@ -94,73 +94,75 @@ public function readData( AvroSchema::FLOAT_TYPE => $decoder->readFloat(), AvroSchema::DOUBLE_TYPE => $decoder->readDouble(), AvroSchema::STRING_TYPE => $decoder->readString(), - AvroSchema::BYTES_TYPE => $this->readBytes($writers_schema, $readers_schema, $decoder->readBytes()), - AvroSchema::ARRAY_SCHEMA => $this->readArray($writers_schema, $readers_schema, $decoder), - AvroSchema::MAP_SCHEMA => $this->readMap($writers_schema, $readers_schema, $decoder), - AvroSchema::UNION_SCHEMA => $this->readUnion($writers_schema, $readers_schema, $decoder), - AvroSchema::ENUM_SCHEMA => $this->readEnum($writers_schema, $readers_schema, $decoder), - AvroSchema::FIXED_SCHEMA => $this->readFixed($writers_schema, $readers_schema, $decoder), + AvroSchema::BYTES_TYPE => $this->readBytes($writersSchema, $readersSchema, $decoder->readBytes()), + AvroSchema::ARRAY_SCHEMA => $this->readArray($writersSchema, $readersSchema, $decoder), + AvroSchema::MAP_SCHEMA => $this->readMap($writersSchema, $readersSchema, $decoder), + AvroSchema::UNION_SCHEMA => $this->readUnion($writersSchema, $readersSchema, $decoder), + AvroSchema::ENUM_SCHEMA => $this->readEnum($writersSchema, $readersSchema, $decoder), + AvroSchema::FIXED_SCHEMA => $this->readFixed($writersSchema, $readersSchema, $decoder), AvroSchema::RECORD_SCHEMA, AvroSchema::ERROR_SCHEMA, - AvroSchema::REQUEST_SCHEMA => $this->readRecord($writers_schema, $readers_schema, $decoder), + AvroSchema::REQUEST_SCHEMA => $this->readRecord($writersSchema, $readersSchema, $decoder), default => throw new AvroException(sprintf( "Cannot read unknown schema type: %s", - $writers_schema->type() + $writersSchema->type() )), }; } /** * @throws AvroSchemaParseException - * @return bool true if the schemas are consistent with - * each other and false otherwise. + * @return bool true if the schemas are consistent with each other and false otherwise. */ public static function schemasMatch( - AvroSchema $writers_schema, - AvroSchema $readers_schema + AvroSchema $writersSchema, + AvroSchema $readersSchema ): bool { - $writers_schema_type = $writers_schema->type; - $readers_schema_type = $readers_schema->type; + $writersSchemaType = $writersSchema->type; + $readersSchemaType = $readersSchema->type; - if (AvroSchema::UNION_SCHEMA === $writers_schema_type || AvroSchema::UNION_SCHEMA === $readers_schema_type) { + if (AvroSchema::UNION_SCHEMA === $writersSchemaType || AvroSchema::UNION_SCHEMA === $readersSchemaType) { return true; } - if (AvroSchema::isPrimitiveType($writers_schema_type)) { - return true; + if ( + AvroSchema::isPrimitiveType($writersSchemaType) + && AvroSchema::isPrimitiveType($readersSchemaType) + ) { + return $writersSchemaType === $readersSchemaType; } - switch ($readers_schema_type) { + switch ($readersSchemaType) { case AvroSchema::MAP_SCHEMA: if ( - !$writers_schema instanceof AvroMapSchema - || !$readers_schema instanceof AvroMapSchema + !$writersSchema instanceof AvroMapSchema + || !$readersSchema instanceof AvroMapSchema ) { return false; } return self::attributesMatch( - $writers_schema->values(), - $readers_schema->values(), + $writersSchema->values(), + $readersSchema->values(), [AvroSchema::TYPE_ATTR] ); case AvroSchema::ARRAY_SCHEMA: if ( - !$writers_schema instanceof AvroArraySchema - || !$readers_schema instanceof AvroArraySchema + !$writersSchema instanceof AvroArraySchema + || !$readersSchema instanceof AvroArraySchema ) { return false; } return self::attributesMatch( - $writers_schema->items(), - $readers_schema->items(), + $writersSchema->items(), + $readersSchema->items(), [AvroSchema::TYPE_ATTR] ); case AvroSchema::FIXED_SCHEMA: return self::attributesMatch( - $writers_schema, - $readers_schema, + $writersSchema, + $readersSchema, [ AvroSchema::FULLNAME_ATTR, AvroSchema::SIZE_ATTR, @@ -170,8 +172,8 @@ public static function schemasMatch( case AvroSchema::RECORD_SCHEMA: case AvroSchema::ERROR_SCHEMA: return self::attributesMatch( - $writers_schema, - $readers_schema, + $writersSchema, + $readersSchema, [AvroSchema::FULLNAME_ATTR] ); case AvroSchema::REQUEST_SCHEMA: @@ -181,8 +183,8 @@ public static function schemasMatch( } if ( - AvroSchema::INT_TYPE === $writers_schema_type - && in_array($readers_schema_type, [ + AvroSchema::INT_TYPE === $writersSchemaType + && in_array($readersSchemaType, [ AvroSchema::LONG_TYPE, AvroSchema::FLOAT_TYPE, AvroSchema::DOUBLE_TYPE, @@ -192,8 +194,8 @@ public static function schemasMatch( } if ( - AvroSchema::LONG_TYPE === $writers_schema_type - && in_array($readers_schema_type, [ + AvroSchema::LONG_TYPE === $writersSchemaType + && in_array($readersSchemaType, [ AvroSchema::FLOAT_TYPE, AvroSchema::DOUBLE_TYPE, ]) @@ -201,7 +203,7 @@ public static function schemasMatch( return true; } - if (AvroSchema::FLOAT_TYPE === $writers_schema_type && AvroSchema::DOUBLE_TYPE === $readers_schema_type) { + if (AvroSchema::FLOAT_TYPE === $writersSchemaType && AvroSchema::DOUBLE_TYPE === $readersSchemaType) { return true; } @@ -226,7 +228,7 @@ public static function attributesMatch( if (AvroSchema::FULLNAME_ATTR === $attribute_name) { if ( - !($schema_two instanceof AvroAliasedSchema) + !$schema_two instanceof AvroAliasedSchema ) { return false; } @@ -269,37 +271,42 @@ public function readBytes(AvroSchema $writers_schema, AvroSchema $readers_schema return $bytes; } + /** + * @throws AvroException + * @throws AvroIOSchemaMatchException + * @return list + */ public function readArray( - AvroArraySchema $writers_schema, - AvroArraySchema $readers_schema, + AvroArraySchema $writersSchema, + AvroArraySchema $readersSchema, AvroIOBinaryDecoder $decoder ): array { $items = []; - $block_count = $decoder->readLong(); - while (0 !== $block_count) { - if ($block_count < 0) { - $block_count = -$block_count; + $blockCount = $decoder->readLong(); + while (0 !== $blockCount) { + if ($blockCount < 0) { + $blockCount = -$blockCount; $decoder->readLong(); // Read (and ignore) block size } - for ($i = 0; $i < $block_count; $i++) { + for ($i = 0; $i < $blockCount; $i++) { $items[] = $this->readData( - $writers_schema->items(), - $readers_schema->items(), + $writersSchema->items(), + $readersSchema->items(), $decoder ); } - $block_count = $decoder->readLong(); + $blockCount = $decoder->readLong(); } return $items; } /** - * @returns array + * @return array */ public function readMap( - AvroMapSchema $writers_schema, - AvroMapSchema $readers_schema, + AvroMapSchema $writersSchema, + AvroMapSchema $readersSchema, AvroIOBinaryDecoder $decoder ): array { $items = []; @@ -314,8 +321,8 @@ public function readMap( for ($i = 0; $i < $pair_count; $i++) { $key = $decoder->readString(); $items[$key] = $this->readData( - $writers_schema->values(), - $readers_schema->values(), + $writersSchema->values(), + $readersSchema->values(), $decoder ); } @@ -383,31 +390,31 @@ public function readFixed( } /** - * @returns array + * @return array */ public function readRecord( - AvroRecordSchema $writers_schema, - AvroRecordSchema $readers_schema, + AvroRecordSchema $writersSchema, + AvroRecordSchema $readersSchema, AvroIOBinaryDecoder $decoder - ) { - $readers_fields = $readers_schema->fieldsHash(); + ): array { + $readerFields = $readersSchema->fieldsHash(); $record = []; - foreach ($writers_schema->fields() as $writers_field) { - $type = $writers_field->type(); - $readers_field = $readers_fields[$writers_field->name()] ?? null; - if ($readers_field) { - $record[$writers_field->name()] = $this->readData($type, $readers_field->type(), $decoder); - } elseif (isset($readers_schema->fieldsByAlias()[$writers_field->name()])) { - $readers_field = $readers_schema->fieldsByAlias()[$writers_field->name()]; - $field_val = $this->readData($writers_field->type(), $readers_field->type(), $decoder); - $record[$readers_field->name()] = $field_val; + foreach ($writersSchema->fields() as $writersField) { + $type = $writersField->type(); + $readersField = $readerFields[$writersField->name()] ?? null; + if ($readersField) { + $record[$writersField->name()] = $this->readData($type, $readersField->type(), $decoder); + } elseif (isset($readersSchema->fieldsByAlias()[$writersField->name()])) { + $readersField = $readersSchema->fieldsByAlias()[$writersField->name()]; + $field_val = $this->readData($writersField->type(), $readersField->type(), $decoder); + $record[$readersField->name()] = $field_val; } else { self::skipData($type, $decoder); } } // Fill in default values - $writers_fields = $writers_schema->fieldsHash(); - foreach ($readers_fields as $field_name => $field) { + $writers_fields = $writersSchema->fieldsHash(); + foreach ($readerFields as $field_name => $field) { if (isset($writers_fields[$field_name])) { continue; } @@ -420,10 +427,10 @@ public function readRecord( } public static function skipData( - AvroSchema|AvroFixedSchema|AvroEnumSchema|AvroUnionSchema|AvroArraySchema|AvroMapSchema $writers_schema, + AvroSchema|AvroFixedSchema|AvroEnumSchema|AvroUnionSchema|AvroArraySchema|AvroMapSchema $writersSchema, AvroIOBinaryDecoder $decoder ): void { - match ($writers_schema->type()) { + match ($writersSchema->type()) { AvroSchema::NULL_TYPE => $decoder->skipNull(), AvroSchema::BOOLEAN_TYPE => $decoder->skipBoolean(), AvroSchema::INT_TYPE => $decoder->skipInt(), @@ -432,56 +439,58 @@ public static function skipData( AvroSchema::DOUBLE_TYPE => $decoder->skipDouble(), AvroSchema::STRING_TYPE => $decoder->skipString(), AvroSchema::BYTES_TYPE => $decoder->skipBytes(), - AvroSchema::ARRAY_SCHEMA => $decoder->skipArray($writers_schema, $decoder), - AvroSchema::MAP_SCHEMA => $decoder->skipMap($writers_schema, $decoder), - AvroSchema::UNION_SCHEMA => $decoder->skipUnion($writers_schema, $decoder), - AvroSchema::ENUM_SCHEMA => $decoder->skipEnum($writers_schema, $decoder), - AvroSchema::FIXED_SCHEMA => $decoder->skipFixed($writers_schema, $decoder), - AvroSchema::RECORD_SCHEMA, AvroSchema::ERROR_SCHEMA, AvroSchema::REQUEST_SCHEMA => $decoder->skipRecord($writers_schema, $decoder), + AvroSchema::ARRAY_SCHEMA => $decoder->skipArray($writersSchema, $decoder), + AvroSchema::MAP_SCHEMA => $decoder->skipMap($writersSchema, $decoder), + AvroSchema::UNION_SCHEMA => $decoder->skipUnion($writersSchema, $decoder), + AvroSchema::ENUM_SCHEMA => $decoder->skipEnum($writersSchema, $decoder), + AvroSchema::FIXED_SCHEMA => $decoder->skipFixed($writersSchema, $decoder), + AvroSchema::RECORD_SCHEMA, + AvroSchema::ERROR_SCHEMA, + AvroSchema::REQUEST_SCHEMA => $decoder->skipRecord($writersSchema, $decoder), default => throw new AvroException(sprintf( 'Unknown schema type: %s', - $writers_schema->type() + $writersSchema->type() )), }; } /** - * @param null|array|bool|float|int|string $default_value + * @param null|array|bool|float|int|list|string $defaultValue * * @throws AvroException if $field_schema type is unknown. - * @return null|array|bool|float|int|string + * @return null|array|bool|float|int|list|string */ - public function readDefaultValue(AvroSchema $field_schema, mixed $default_value): mixed + public function readDefaultValue(AvroSchema $fieldSchema, mixed $defaultValue): mixed { - switch ($field_schema->type()) { + switch ($fieldSchema->type()) { case AvroSchema::NULL_TYPE: return null; case AvroSchema::BOOLEAN_TYPE: - return $default_value; + return $defaultValue; case AvroSchema::INT_TYPE: case AvroSchema::LONG_TYPE: - return (int) $default_value; + return (int) $defaultValue; case AvroSchema::FLOAT_TYPE: case AvroSchema::DOUBLE_TYPE: - return (float) $default_value; + return (float) $defaultValue; case AvroSchema::STRING_TYPE: case AvroSchema::BYTES_TYPE: - return $this->readBytes($field_schema, $field_schema, $default_value); + return $this->readBytes($fieldSchema, $fieldSchema, $defaultValue); case AvroSchema::ARRAY_SCHEMA: $array = []; - foreach ($default_value as $json_val) { + foreach ($defaultValue as $json_val) { /** @phpstan-ignore method.notFound */ - $val = $this->readDefaultValue($field_schema->items(), $json_val); + $val = $this->readDefaultValue($fieldSchema->items(), $json_val); $array[] = $val; } return $array; case AvroSchema::MAP_SCHEMA: $map = []; - foreach ($default_value as $key => $json_val) { + foreach ($defaultValue as $key => $json_val) { $map[$key] = $this->readDefaultValue( /** @phpstan-ignore method.notFound */ - $field_schema->values(), + $fieldSchema->values(), $json_val ); } @@ -490,18 +499,18 @@ public function readDefaultValue(AvroSchema $field_schema, mixed $default_value) case AvroSchema::UNION_SCHEMA: return $this->readDefaultValue( /** @phpstan-ignore method.notFound */ - $field_schema->schemaByIndex(0), - $default_value + $fieldSchema->schemaByIndex(0), + $defaultValue ); case AvroSchema::ENUM_SCHEMA: case AvroSchema::FIXED_SCHEMA: - return $default_value; + return $defaultValue; case AvroSchema::RECORD_SCHEMA: $record = []; /** @phpstan-ignore method.notFound */ - foreach ($field_schema->fields() as $field) { + foreach ($fieldSchema->fields() as $field) { $field_name = $field->name(); - if (!$json_val = $default_value[$field_name]) { + if (!$json_val = $defaultValue[$field_name]) { $json_val = $field->default_value(); } @@ -513,7 +522,7 @@ public function readDefaultValue(AvroSchema $field_schema, mixed $default_value) return $record; default: - throw new AvroException(sprintf('Unknown type: %s', $field_schema->type())); + throw new AvroException(sprintf('Unknown type: %s', $fieldSchema->type())); } } diff --git a/lang/php/lib/Datum/AvroIODatumWriter.php b/lang/php/lib/Datum/AvroIODatumWriter.php index 3f61c37e38e..e4d9be563ea 100644 --- a/lang/php/lib/Datum/AvroIODatumWriter.php +++ b/lang/php/lib/Datum/AvroIODatumWriter.php @@ -40,16 +40,15 @@ class AvroIODatumWriter { /** * Schema used by this instance to write Avro data. - * @var AvroSchema */ - public $writersSchema; + public ?AvroSchema $writersSchema; - public function __construct(?AvroSchema $writers_schema = null) + public function __construct(?AvroSchema $writersSchema = null) { - $this->writersSchema = $writers_schema; + $this->writersSchema = $writersSchema; } - public function write(mixed $datum, AvroIOBinaryEncoder $encoder) + public function write(mixed $datum, AvroIOBinaryEncoder $encoder): void { $this->writeData($this->writersSchema, $datum, $encoder); } @@ -58,22 +57,23 @@ public function write(mixed $datum, AvroIOBinaryEncoder $encoder) * @throws AvroIOTypeException if $datum is invalid for $writers_schema * @throws AvroException if the type is invalid */ - public function writeData(AvroSchema $writers_schema, mixed $datum, AvroIOBinaryEncoder $encoder): void + public function writeData(AvroSchema $writersSchema, mixed $datum, AvroIOBinaryEncoder $encoder): void { - if (!AvroSchema::isValidDatum($writers_schema, $datum)) { - throw new AvroIOTypeException($writers_schema, $datum); + if (!AvroSchema::isValidDatum($writersSchema, $datum)) { + throw new AvroIOTypeException($writersSchema, $datum); } - $this->writeValidatedData($writers_schema, $datum, $encoder); + $this->writeValidatedData($writersSchema, $datum, $encoder); } /** + * @throws AvroException * @throws AvroIOTypeException if $datum is invalid for $writers_schema - * @return void + * @throws AvroSchemaParseException */ - private function writeValidatedData(AvroSchema $writers_schema, mixed $datum, AvroIOBinaryEncoder $encoder) + private function writeValidatedData(AvroSchema $writersSchema, mixed $datum, AvroIOBinaryEncoder $encoder): void { - switch ($writers_schema->type()) { + switch ($writersSchema->type()) { case AvroSchema::NULL_TYPE: $encoder->writeNull($datum); @@ -103,39 +103,39 @@ private function writeValidatedData(AvroSchema $writers_schema, mixed $datum, Av return; case AvroSchema::BYTES_TYPE: - $this->writeBytes($writers_schema, $datum, $encoder); + $this->writeBytes($writersSchema, $datum, $encoder); return; case AvroSchema::ARRAY_SCHEMA: - $this->writeArray($writers_schema, $datum, $encoder); + $this->writeArray($writersSchema, $datum, $encoder); return; case AvroSchema::MAP_SCHEMA: - $this->writeMap($writers_schema, $datum, $encoder); + $this->writeMap($writersSchema, $datum, $encoder); return; case AvroSchema::FIXED_SCHEMA: - $this->writeFixed($writers_schema, $datum, $encoder); + $this->writeFixed($writersSchema, $datum, $encoder); return; case AvroSchema::ENUM_SCHEMA: - $this->writeEnum($writers_schema, $datum, $encoder); + $this->writeEnum($writersSchema, $datum, $encoder); return; case AvroSchema::RECORD_SCHEMA: case AvroSchema::ERROR_SCHEMA: case AvroSchema::REQUEST_SCHEMA: - $this->writeRecord($writers_schema, $datum, $encoder); + $this->writeRecord($writersSchema, $datum, $encoder); return; case AvroSchema::UNION_SCHEMA: - $this->writeUnion($writers_schema, $datum, $encoder); + $this->writeUnion($writersSchema, $datum, $encoder); return; default: throw new AvroException(sprintf( 'Unknown type: %s', - $writers_schema->type + $writersSchema->type )); } } @@ -159,14 +159,15 @@ private function writeBytes(AvroSchema $writers_schema, string $datum, AvroIOBin } /** + * @param list $datum * @throws AvroIOTypeException */ - private function writeArray(AvroArraySchema $writers_schema, array $datum, AvroIOBinaryEncoder $encoder): void + private function writeArray(AvroArraySchema $writersSchema, array $datum, AvroIOBinaryEncoder $encoder): void { - $datum_count = count($datum); - if (0 < $datum_count) { - $encoder->writeLong($datum_count); - $items = $writers_schema->items(); + $datumCount = count($datum); + if (0 < $datumCount) { + $encoder->writeLong($datumCount); + $items = $writersSchema->items(); foreach ($datum as $item) { $this->writeValidatedData($items, $item, $encoder); } @@ -175,27 +176,28 @@ private function writeArray(AvroArraySchema $writers_schema, array $datum, AvroI } /** + * @param array $datum * @throws AvroIOTypeException */ - private function writeMap(AvroMapSchema $writers_schema, array $datum, AvroIOBinaryEncoder $encoder): void + private function writeMap(AvroMapSchema $writersSchema, array $datum, AvroIOBinaryEncoder $encoder): void { $datum_count = count($datum); if ($datum_count > 0) { $encoder->writeLong($datum_count); foreach ($datum as $k => $v) { $encoder->writeString($k); - $this->writeValidatedData($writers_schema->values(), $v, $encoder); + $this->writeValidatedData($writersSchema->values(), $v, $encoder); } } $encoder->writeLong(0); } private function writeFixed( - AvroSchema $writers_schema, + AvroSchema $writersSchema, string|AvroDuration $datum, AvroIOBinaryEncoder $encoder ): void { - $logicalType = $writers_schema->logicalType(); + $logicalType = $writersSchema->logicalType(); if ( $logicalType instanceof AvroLogicalType ) { @@ -230,15 +232,15 @@ private function writeFixed( $encoder->write($datum); } - private function writeEnum(AvroEnumSchema $writers_schema, $datum, AvroIOBinaryEncoder $encoder): void + private function writeEnum(AvroEnumSchema $writersSchema, string $datum, AvroIOBinaryEncoder $encoder): void { - $datum_index = $writers_schema->symbolIndex($datum); - $encoder->writeInt($datum_index); + $datumIndex = $writersSchema->symbolIndex($datum); + $encoder->writeInt($datumIndex); } - private function writeRecord(AvroRecordSchema $writers_schema, mixed $datum, AvroIOBinaryEncoder $encoder): void + private function writeRecord(AvroRecordSchema $writersSchema, mixed $datum, AvroIOBinaryEncoder $encoder): void { - foreach ($writers_schema->fields() as $field) { + foreach ($writersSchema->fields() as $field) { $this->writeValidatedData($field->type(), $datum[$field->name()] ?? null, $encoder); } } @@ -247,11 +249,11 @@ private function writeRecord(AvroRecordSchema $writers_schema, mixed $datum, Avr * @throws AvroIOTypeException * @throws AvroSchemaParseException */ - private function writeUnion(AvroUnionSchema $writers_schema, mixed $datum, AvroIOBinaryEncoder $encoder): void + private function writeUnion(AvroUnionSchema $writersSchema, mixed $datum, AvroIOBinaryEncoder $encoder): void { $datum_schema_index = -1; $datum_schema = null; - foreach ($writers_schema->schemas() as $index => $schema) { + foreach ($writersSchema->schemas() as $index => $schema) { if (AvroSchema::isValidDatum($schema, $datum)) { $datum_schema_index = $index; $datum_schema = $schema; @@ -261,7 +263,7 @@ private function writeUnion(AvroUnionSchema $writers_schema, mixed $datum, AvroI } if (is_null($datum_schema)) { - throw new AvroIOTypeException($writers_schema, $datum); + throw new AvroIOTypeException($writersSchema, $datum); } $encoder->writeLong($datum_schema_index); diff --git a/lang/php/lib/Datum/AvroIOSchemaMatchException.php b/lang/php/lib/Datum/AvroIOSchemaMatchException.php index 0eaa2b225dc..8706674bc3a 100644 --- a/lang/php/lib/Datum/AvroIOSchemaMatchException.php +++ b/lang/php/lib/Datum/AvroIOSchemaMatchException.php @@ -18,6 +18,8 @@ * limitations under the License. */ +declare(strict_types=1); + namespace Apache\Avro\Datum; use Apache\Avro\AvroException; @@ -30,16 +32,16 @@ class AvroIOSchemaMatchException extends AvroException { /** - * @param AvroSchema $writers_schema - * @param AvroSchema $readers_schema + * @param AvroSchema $writersSchema + * @param AvroSchema $readersSchema */ - public function __construct($writers_schema, $readers_schema) + public function __construct($writersSchema, $readersSchema) { parent::__construct( sprintf( "Writer's schema %s and Reader's schema %s do not match.", - $writers_schema, - $readers_schema + $writersSchema, + $readersSchema ) ); } diff --git a/lang/php/lib/IO/AvroFile.php b/lang/php/lib/IO/AvroFile.php index 7d6209bb6dc..b0b6852ce58 100644 --- a/lang/php/lib/IO/AvroFile.php +++ b/lang/php/lib/IO/AvroFile.php @@ -42,23 +42,23 @@ class AvroFile implements AvroIO /** * @var resource file handle for AvroFile instance */ - private $file_handle; + private $fileHandle; public function __construct( - private string $file_path, + private string $filePath, string $mode = self::READ_MODE ) { switch ($mode) { case self::WRITE_MODE: - $this->file_handle = fopen($this->file_path, self::FOPEN_WRITE_MODE); - if (false === $this->file_handle) { + $this->fileHandle = fopen($this->filePath, self::FOPEN_WRITE_MODE); + if (false === $this->fileHandle) { throw new AvroIOException('Could not open file for writing'); } break; case self::READ_MODE: - $this->file_handle = fopen($this->file_path, self::FOPEN_READ_MODE); - if (false === $this->file_handle) { + $this->fileHandle = fopen($this->filePath, self::FOPEN_READ_MODE); + if (false === $this->fileHandle) { throw new AvroIOException('Could not open file for reading'); } @@ -81,7 +81,7 @@ public function __construct( */ public function write(string $bytes): int { - $len = fwrite($this->file_handle, $bytes); + $len = fwrite($this->fileHandle, $bytes); if (false === $len) { throw new AvroIOException(sprintf('Could not write to file')); } @@ -90,12 +90,12 @@ public function write(string $bytes): int } /** - * @returns int current position within the file * @throws AvroIOException if tell failed. + * @return int current position within the file */ public function tell(): int { - $position = ftell($this->file_handle); + $position = ftell($this->fileHandle); if (false === $position) { throw new AvroIOException('Could not execute tell on reader'); } @@ -105,12 +105,12 @@ public function tell(): int /** * Closes the file. - * @returns bool true if successful. * @throws AvroIOException if there was an error closing the file. + * @return bool true if successful. */ public function close(): bool { - $res = fclose($this->file_handle); + $res = fclose($this->fileHandle); if (false === $res) { throw new AvroIOException('Error closing file.'); } @@ -119,14 +119,14 @@ public function close(): bool } /** - * @returns boolean true if the pointer is at the end of the file, + * @return bool true if the pointer is at the end of the file, * and false otherwise. * @see AvroIO::isEof() as behavior differs from feof() */ public function isEof(): bool { $this->read(1); - if (feof($this->file_handle)) { + if (feof($this->fileHandle)) { return true; } $this->seek(-1, self::SEEK_CUR); @@ -136,8 +136,8 @@ public function isEof(): bool /** * @param int $len count of bytes to read. - * @returns string bytes read * @throws AvroIOException if length value is negative or if the read failed + * @return string bytes read */ public function read(int $len): string { @@ -151,7 +151,7 @@ public function read(int $len): string return ''; } - $bytes = fread($this->file_handle, $len); + $bytes = fread($this->fileHandle, $len); if (false === $bytes) { throw new AvroIOException('Could not read from file'); } @@ -160,13 +160,13 @@ public function read(int $len): string } /** - * @returns boolean true upon success * @throws AvroIOException if seek failed. + * @return bool true upon success * @see AvroIO::seek() */ public function seek(int $offset, int $whence = SEEK_SET): bool { - $res = fseek($this->file_handle, $offset, $whence); + $res = fseek($this->fileHandle, $offset, $whence); // Note: does not catch seeking beyond end of file if (-1 === $res) { throw new AvroIOException( @@ -182,12 +182,12 @@ public function seek(int $offset, int $whence = SEEK_SET): bool } /** - * @returns boolean true if the flush was successful. * @throws AvroIOException if there was an error flushing the file. + * @return bool true if the flush was successful. */ public function flush(): bool { - $res = fflush($this->file_handle); + $res = fflush($this->fileHandle); if (false === $res) { throw new AvroIOException('Could not flush file.'); } diff --git a/lang/php/lib/IO/AvroStringIO.php b/lang/php/lib/IO/AvroStringIO.php index 4136f6deb3c..8c55438fe58 100644 --- a/lang/php/lib/IO/AvroStringIO.php +++ b/lang/php/lib/IO/AvroStringIO.php @@ -29,66 +29,45 @@ */ class AvroStringIO implements AvroIO, \Stringable { - private string $string_buffer; + private string $stringBuffer; /** * @var int current position in string */ - private int $current_index; + private int $currentIndex; /** * @var bool whether or not the string is closed. */ - private bool $is_closed; + private bool $isClosed; /** * @param string $str initial value of AvroStringIO buffer. Regardless * of the initial value, the pointer is set to the * beginning of the buffer. - * @throws AvroIOException if a non-string value is passed as $str */ public function __construct(string $str = '') { - $this->is_closed = false; - $this->string_buffer = ''; - $this->current_index = 0; - - if (is_string($str)) { - $this->string_buffer .= $str; - } else { - throw new AvroIOException( - sprintf('constructor argument must be a string: %s', gettype($str)) - ); - } + $this->isClosed = false; + $this->stringBuffer = $str; + $this->currentIndex = 0; } - /** - * @returns string - */ public function __toString(): string { - return $this->string_buffer; + return $this->stringBuffer; } /** * Append bytes to this buffer. * (Nothing more is needed to support Avro.) * @param string $bytes bytes to write - * @returns int count of bytes written. * @throws AvroIOException if $args is not a string value. + * @return int count of bytes written. */ public function write(string $bytes): int { $this->checkClosed(); - if (is_string($bytes)) { - return $this->appendStr($bytes); - } - throw new AvroIOException( - sprintf( - 'write argument must be a string: (%s) %s', - gettype($bytes), - var_export($bytes, true) - ) - ); + return $this->appendStr($bytes); } /** @@ -96,45 +75,45 @@ public function write(string $bytes): int */ public function isClosed(): bool { - return $this->is_closed; + return $this->isClosed; } /** - * @returns string bytes read from buffer * @todo test for fencepost errors wrt updating current_index * @param mixed $len + * @return string bytes read from buffer */ public function read($len): string { $this->checkClosed(); $read = ''; - for ($i = $this->current_index; $i < ($this->current_index + $len); $i++) { - $read .= $this->string_buffer[$i] ?? ''; + for ($i = $this->currentIndex; $i < ($this->currentIndex + $len); $i++) { + $read .= $this->stringBuffer[$i] ?? ''; } if (strlen($read) < $len) { - $this->current_index = $this->length(); + $this->currentIndex = $this->length(); } else { - $this->current_index += $len; + $this->currentIndex += $len; } return $read; } /** - * @returns int count of bytes in the buffer + * @return int count of bytes in the buffer * @internal Could probably memoize length for performance, but * no need do this yet. */ public function length(): int { - return strlen($this->string_buffer); + return strlen($this->stringBuffer); } /** - * @returns boolean true if successful * @param mixed $offset * @param mixed $whence * @throws AvroIOException if the seek failed. + * @return bool true if successful */ public function seek($offset, $whence = self::SEEK_SET): bool { @@ -147,21 +126,21 @@ public function seek($offset, $whence = self::SEEK_SET): bool if (0 > $offset) { throw new AvroIOException('Cannot seek before beginning of file.'); } - $this->current_index = $offset; + $this->currentIndex = $offset; break; case self::SEEK_CUR: - if (0 > $this->current_index + $whence) { + if (0 > $this->currentIndex + $whence) { throw new AvroIOException('Cannot seek before beginning of file.'); } - $this->current_index += $offset; + $this->currentIndex += $offset; break; case self::SEEK_END: if (0 > $this->length() + $offset) { throw new AvroIOException('Cannot seek before beginning of file.'); } - $this->current_index = $this->length() + $offset; + $this->currentIndex = $this->length() + $offset; break; default: @@ -176,7 +155,7 @@ public function seek($offset, $whence = self::SEEK_SET): bool */ public function tell(): int { - return $this->current_index; + return $this->currentIndex; } /** @@ -184,12 +163,12 @@ public function tell(): int */ public function isEof(): bool { - return $this->current_index >= $this->length(); + return $this->currentIndex >= $this->length(); } /** * No-op provided for compatibility with AvroIO interface. - * @returns bool true + * @return bool true */ public function flush(): bool { @@ -202,7 +181,7 @@ public function flush(): bool public function close(): bool { $this->checkClosed(); - $this->is_closed = true; + $this->isClosed = true; return true; } @@ -210,19 +189,18 @@ public function close(): bool /** * Truncates the truncate buffer to 0 bytes and returns the pointer * to the beginning of the buffer. - * @returns bool true + * @return bool true */ public function truncate(): bool { $this->checkClosed(); - $this->string_buffer = ''; - $this->current_index = 0; + $this->stringBuffer = ''; + $this->currentIndex = 0; return true; } /** - * @returns string * @uses self::__toString() */ public function string(): string @@ -248,9 +226,9 @@ private function checkClosed(): void private function appendStr(string $str): int { $this->checkClosed(); - $this->string_buffer .= $str; + $this->stringBuffer .= $str; $len = strlen($str); - $this->current_index += $len; + $this->currentIndex += $len; return $len; } diff --git a/lang/php/lib/Protocol/AvroProtocol.php b/lang/php/lib/Protocol/AvroProtocol.php index 7a651bfe0c4..c7e2eba2be5 100644 --- a/lang/php/lib/Protocol/AvroProtocol.php +++ b/lang/php/lib/Protocol/AvroProtocol.php @@ -28,6 +28,14 @@ /** * Avro library for protocols + * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema + * @phpstan-import-type AvroProtocolMessageDefinitionArray from AvroProtocolMessage + * @phpstan-type AvroProtocolDefinitionArray array{ + * types: AvroSchemaDefinitionArray, + * protocol: string, + * namespace: string, + * messages: array + * } */ class AvroProtocol { @@ -60,7 +68,7 @@ public static function parse(string $json): self } /** - * @param array $avro AVRO protocol as associative array + * @param AvroProtocolDefinitionArray $avro AVRO protocol as associative array * @throws AvroSchemaParseException */ public static function realParse(array $avro): self diff --git a/lang/php/lib/Protocol/AvroProtocolMessage.php b/lang/php/lib/Protocol/AvroProtocolMessage.php index 9677591dd2b..5546b51e741 100644 --- a/lang/php/lib/Protocol/AvroProtocolMessage.php +++ b/lang/php/lib/Protocol/AvroProtocolMessage.php @@ -29,6 +29,10 @@ use Apache\Avro\Schema\AvroSchema; use Apache\Avro\Schema\AvroSchemaParseException; +/** + * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema + * @phpstan-type AvroProtocolMessageDefinitionArray array{request: AvroSchemaDefinitionArray, response: string} + */ class AvroProtocolMessage { public readonly AvroRecordSchema $request; @@ -36,6 +40,7 @@ class AvroProtocolMessage public readonly ?AvroSchema $response; /** + * @param AvroProtocolMessageDefinitionArray $avro * @throws AvroSchemaParseException */ public function __construct( @@ -49,7 +54,7 @@ public function __construct( doc: null, fields: $avro['request'], schemata: $schemata, - schema_type: AvroSchema::REQUEST_SCHEMA + schemaType: AvroSchema::REQUEST_SCHEMA ); $response = null; @@ -58,7 +63,7 @@ public function __construct( new AvroName( name: $avro['response'], namespace: $namespace, - default_namespace: $namespace + defaultNamespace: $namespace ) ); diff --git a/lang/php/lib/Schema/AvroAliasedSchema.php b/lang/php/lib/Schema/AvroAliasedSchema.php index 13ca0a638d4..90a33c4feab 100644 --- a/lang/php/lib/Schema/AvroAliasedSchema.php +++ b/lang/php/lib/Schema/AvroAliasedSchema.php @@ -22,7 +22,11 @@ namespace Apache\Avro\Schema; +/** + * @phpstan-type AvroAliases list + */ interface AvroAliasedSchema { + /** @return null|AvroAliases */ public function getAliases(): ?array; } diff --git a/lang/php/lib/Schema/AvroArraySchema.php b/lang/php/lib/Schema/AvroArraySchema.php index ff8d6563b19..1a7a049a911 100644 --- a/lang/php/lib/Schema/AvroArraySchema.php +++ b/lang/php/lib/Schema/AvroArraySchema.php @@ -59,7 +59,7 @@ public function __construct($items, ?string $defaultNamespace, AvroNamedSchemata } /** - * @returns AvroName|AvroSchema named schema name or AvroSchema + * @return AvroName|AvroSchema named schema name or AvroSchema * of this array schema's elements. */ public function items(): AvroName|AvroSchema diff --git a/lang/php/lib/Schema/AvroEnumSchema.php b/lang/php/lib/Schema/AvroEnumSchema.php index 8159bfead1e..6b6fd12c987 100644 --- a/lang/php/lib/Schema/AvroEnumSchema.php +++ b/lang/php/lib/Schema/AvroEnumSchema.php @@ -43,7 +43,7 @@ public function __construct(AvroName $name, ?string $doc, mixed $symbols, AvroNa if (count(array_unique($symbols)) > count($symbols)) { throw new AvroSchemaParseException( - sprintf('Duplicate symbols: %s', $symbols) + sprintf('Duplicate symbols: %s', implode(", ", $symbols)) ); } @@ -60,7 +60,7 @@ public function __construct(AvroName $name, ?string $doc, mixed $symbols, AvroNa } /** - * @returns string[] this enum schema's symbols + * @return string[] this enum schema's symbols */ public function symbols() { @@ -69,7 +69,7 @@ public function symbols() /** * @param string $symbol - * @returns boolean true if the given symbol exists in this + * @return bool true if the given symbol exists in this * enum schema and false otherwise */ public function hasSymbol($symbol) @@ -79,7 +79,7 @@ public function hasSymbol($symbol) /** * @param int $index - * @returns string enum schema symbol with the given (zero-based) index + * @return string enum schema symbol with the given (zero-based) index */ public function symbolByIndex($index) { @@ -91,10 +91,10 @@ public function symbolByIndex($index) } /** - * @param string $symbol - * @returns int the index of the given $symbol in the enum schema + * @throws AvroException + * @return int the index of the given $symbol in the enum schema */ - public function symbolIndex($symbol) + public function symbolIndex(string $symbol): int { $idx = array_search($symbol, $this->symbols, true); if (false !== $idx) { diff --git a/lang/php/lib/Schema/AvroField.php b/lang/php/lib/Schema/AvroField.php index 2d1510ffc08..8658cb3d709 100644 --- a/lang/php/lib/Schema/AvroField.php +++ b/lang/php/lib/Schema/AvroField.php @@ -24,6 +24,9 @@ /** * Field of an {@link AvroRecordSchema} + * + * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema + * @phpstan-import-type AvroAliases from AvroAliasedSchema */ class AvroField extends AvroSchema implements AvroAliasedSchema { @@ -58,7 +61,7 @@ class AvroField extends AvroSchema implements AvroAliasedSchema public const IGNORE_SORT_ORDER = 'ignore'; /** - * @var array list of valid field sort order values + * @var list list of valid field sort order values */ private static array $validFieldSortOrders = [ self::ASC_SORT_ORDER, @@ -83,6 +86,8 @@ class AvroField extends AvroSchema implements AvroAliasedSchema * @var null|string sort order of this field */ private ?string $order; + + /** @var null|AvroAliases */ private ?array $aliases; private ?string $doc; @@ -114,6 +119,7 @@ private function __construct( } /** + * @param AvroSchemaDefinitionArray $avro * @throws AvroSchemaParseException */ public static function fromFieldDefinition(array $avro, ?string $defaultNamespace, AvroNamedSchemata $schemata): self @@ -166,6 +172,9 @@ public static function fromFieldDefinition(array $avro, ?string $defaultNamespac ); } + /** + * @return AvroSchemaDefinitionArray|string the Avro representation of this field + */ public function toAvro(): string|array { $avro = [self::FIELD_NAME_ATTR => $this->name]; @@ -196,7 +205,7 @@ public function toAvro(): string|array } /** - * @returns string the name of this field + * @return string the name of this field */ public function name(): string { @@ -204,7 +213,7 @@ public function name(): string } /** - * @returns mixed the default value of this field + * @return mixed the default value of this field */ public function defaultValue() { @@ -212,13 +221,16 @@ public function defaultValue() } /** - * @returns boolean true if the field has a default and false otherwise + * @return bool true if the field has a default and false otherwise */ public function hasDefaultValue(): bool { return $this->hasDefault; } + /** + * @return null|AvroAliases + */ public function getAliases(): ?array { return $this->aliases; diff --git a/lang/php/lib/Schema/AvroLogicalType.php b/lang/php/lib/Schema/AvroLogicalType.php index 150f179d7fc..c432c8511df 100644 --- a/lang/php/lib/Schema/AvroLogicalType.php +++ b/lang/php/lib/Schema/AvroLogicalType.php @@ -22,13 +22,22 @@ use Apache\Avro\AvroException; +/** + * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema + * @phpstan-type AvroLogicalTypeAttributes array + */ class AvroLogicalType { public const ATTRIBUTE_DECIMAL_PRECISION = 'precision'; public const ATTRIBUTE_DECIMAL_SCALE = 'scale'; - public function __construct(private readonly string $name, private readonly array $attributes = []) - { + /** + * @param AvroLogicalTypeAttributes $attributes + */ + public function __construct( + private readonly string $name, + private readonly array $attributes = [] + ) { } public function name(): string @@ -36,11 +45,17 @@ public function name(): string return $this->name; } + /** + * @return AvroLogicalTypeAttributes + */ public function attributes(): array { return $this->attributes; } + /** + * @return AvroSchemaDefinitionArray|string + */ public function toAvro(): string|array { $avro[AvroSchema::LOGICAL_TYPE_ATTR] = $this->name; diff --git a/lang/php/lib/Schema/AvroMapSchema.php b/lang/php/lib/Schema/AvroMapSchema.php index 32e9e7ba865..c8f6f76f423 100644 --- a/lang/php/lib/Schema/AvroMapSchema.php +++ b/lang/php/lib/Schema/AvroMapSchema.php @@ -23,6 +23,8 @@ /** * Avro map schema consisting of named values of defined * Avro Schema types. + * + * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema */ class AvroMapSchema extends AvroSchema { @@ -37,28 +39,32 @@ class AvroMapSchema extends AvroSchema */ private bool $isValuesSchemaFromSchemata; + /** + * @param AvroSchemaDefinitionArray|string $values + * @throws AvroSchemaParseException + */ public function __construct(string|array $values, ?string $defaultNamespace, AvroNamedSchemata $schemata) { parent::__construct(AvroSchema::MAP_SCHEMA); $this->isValuesSchemaFromSchemata = false; - $values_schema = null; + $valuesSchema = null; if ( is_string($values) - && $values_schema = $schemata->schemaByName( + && $valuesSchema = $schemata->schemaByName( new AvroName($values, null, $defaultNamespace) ) ) { $this->isValuesSchemaFromSchemata = true; } else { - $values_schema = AvroSchema::subparse( + $valuesSchema = AvroSchema::subparse( $values, $defaultNamespace, $schemata ); } - $this->values = $values_schema; + $this->values = $valuesSchema; } public function values(): AvroSchema diff --git a/lang/php/lib/Schema/AvroName.php b/lang/php/lib/Schema/AvroName.php index a7869b998c4..8b8f48b6a8d 100644 --- a/lang/php/lib/Schema/AvroName.php +++ b/lang/php/lib/Schema/AvroName.php @@ -40,12 +40,12 @@ class AvroName implements \Stringable /** * @var string Name qualified as necessary given its default namespace. */ - private string $qualified_name; + private string $qualifiedName; /** * @throws AvroSchemaParseException */ - public function __construct(mixed $name, ?string $namespace, ?string $default_namespace) + public function __construct(mixed $name, ?string $namespace, ?string $defaultNamespace) { if (!is_string($name) || empty($name)) { throw new AvroSchemaParseException('Name must be a non-empty string.'); @@ -57,14 +57,14 @@ public function __construct(mixed $name, ?string $namespace, ?string $default_na throw new AvroSchemaParseException(sprintf('Invalid name "%s"', $name)); } elseif (!is_null($namespace)) { $this->fullname = self::parseFullname($name, $namespace); - } elseif (!is_null($default_namespace)) { - $this->fullname = self::parseFullname($name, $default_namespace); + } elseif (!is_null($defaultNamespace)) { + $this->fullname = self::parseFullname($name, $defaultNamespace); } else { $this->fullname = $name; } [$this->name, $this->namespace] = self::extractNamespace($this->fullname); - $this->qualified_name = (is_null($this->namespace) || $this->namespace === $default_namespace) + $this->qualifiedName = (is_null($this->namespace) || $this->namespace === $defaultNamespace) ? $this->name : $this->fullname; } @@ -93,7 +93,7 @@ public static function extractNamespace(string $name, ?string $namespace = null) } /** - * @returns boolean true if the given name is well-formed + * @return bool true if the given name is well-formed * (is a non-null, non-empty string) and false otherwise */ public static function isWellFormedName(mixed $name): bool @@ -109,20 +109,17 @@ public function nameAndNamespace(): array return [$this->name, $this->namespace]; } - /** - * @returns string - */ - public function fullname() + public function fullname(): string { return $this->fullname; } /** - * @returns string name qualified for its context + * @return string name qualified for its context */ - public function qualifiedName() + public function qualifiedName(): string { - return $this->qualified_name; + return $this->qualifiedName; } public function namespace(): ?string @@ -149,7 +146,6 @@ private static function checkNamespaceNames(string $namespace): bool /** * @param string $name * @param string $namespace - * @returns string * @throws AvroSchemaParseException if any of the names are not valid. */ private static function parseFullname($name, $namespace): string diff --git a/lang/php/lib/Schema/AvroNamedSchema.php b/lang/php/lib/Schema/AvroNamedSchema.php index 4064fbeff89..b34e90f30ca 100644 --- a/lang/php/lib/Schema/AvroNamedSchema.php +++ b/lang/php/lib/Schema/AvroNamedSchema.php @@ -22,10 +22,12 @@ /** * Parent class of named Avro schema + * @phpstan-import-type AvroAliases from AvroAliasedSchema */ class AvroNamedSchema extends AvroSchema implements AvroAliasedSchema { /** + * @param null|AvroAliases $aliases * @throws AvroSchemaParseException */ public function __construct( @@ -46,11 +48,17 @@ public function __construct( } } + /** + * @return null|AvroAliases + */ public function getAliases(): ?array { return $this->aliases; } + /** + * @return array|string + */ public function toAvro(): string|array { $avro = parent::toAvro(); diff --git a/lang/php/lib/Schema/AvroNamedSchemata.php b/lang/php/lib/Schema/AvroNamedSchemata.php index 78912d0ffe7..643a1876e56 100644 --- a/lang/php/lib/Schema/AvroNamedSchemata.php +++ b/lang/php/lib/Schema/AvroNamedSchemata.php @@ -93,7 +93,7 @@ public function registerNamedSchema(AvroNamedSchema $schema): self } /** - * @returns bool true if there exists a schema with the given name + * @return bool true if there exists a schema with the given name * and false otherwise. */ public function hasName(string $fullname): bool diff --git a/lang/php/lib/Schema/AvroRecordSchema.php b/lang/php/lib/Schema/AvroRecordSchema.php index c2337a17e28..f3aa229769e 100644 --- a/lang/php/lib/Schema/AvroRecordSchema.php +++ b/lang/php/lib/Schema/AvroRecordSchema.php @@ -20,6 +20,9 @@ namespace Apache\Avro\Schema; +/** + * @phpstan-import-type AvroAliases from AvroAliasedSchema + */ class AvroRecordSchema extends AvroNamedSchema { /** @@ -33,12 +36,17 @@ class AvroRecordSchema extends AvroNamedSchema */ private ?array $fieldsHash = null; + /** + * @param null|array $fields + * @param null|AvroAliases $aliases + * @throws AvroSchemaParseException + */ public function __construct( AvroName $name, ?string $doc, ?array $fields, AvroNamedSchemata $schemata, - string $schema_type = AvroSchema::RECORD_SCHEMA, + string $schemaType = AvroSchema::RECORD_SCHEMA, ?array $aliases = null ) { if (is_null($fields)) { @@ -47,10 +55,10 @@ public function __construct( ); } - if (AvroSchema::REQUEST_SCHEMA === $schema_type) { - parent::__construct($schema_type, $name); + if (AvroSchema::REQUEST_SCHEMA === $schemaType) { + parent::__construct($schemaType, $name); } else { - parent::__construct($schema_type, $name, $doc, $schemata, $aliases); + parent::__construct($schemaType, $name, $doc, $schemata, $aliases); } [$x, $namespace] = $name->nameAndNamespace(); @@ -58,12 +66,14 @@ public function __construct( } /** - * @param null|string $default_namespace namespace of enclosing schema + * @param array $fieldsDefinitions + * @param null|string $defaultNamespace namespace of enclosing schema * @throws AvroSchemaParseException + * @return array */ public static function parseFields( array $fieldsDefinitions, - ?string $default_namespace, + ?string $defaultNamespace, AvroNamedSchemata $schemata ): array { $fields = []; @@ -78,7 +88,7 @@ public static function parseFields( ); } - $newField = AvroField::fromFieldDefinition($fieldDefinition, $default_namespace, $schemata); + $newField = AvroField::fromFieldDefinition($fieldDefinition, $defaultNamespace, $schemata); $fieldNames[] = $name; if ($newField->hasAliases() && array_intersect($aliasNames, $newField->getAliases())) { @@ -93,26 +103,29 @@ public static function parseFields( return $fields; } + /** + * @return array|list>|string the Avro representation of this AvroRecordSchema + */ public function toAvro(): string|array { $avro = parent::toAvro(); - $fields_avro = []; + $fieldsAvro = []; foreach ($this->fields as $field) { - $fields_avro[] = $field->toAvro(); + $fieldsAvro[] = $field->toAvro(); } if (AvroSchema::REQUEST_SCHEMA === $this->type) { - return $fields_avro; + return $fieldsAvro; } - $avro[AvroSchema::FIELDS_ATTR] = $fields_avro; + $avro[AvroSchema::FIELDS_ATTR] = $fieldsAvro; return $avro; } /** - * @returns array the schema definitions of the fields of this AvroRecordSchema + * @return array the schema definitions of the fields of this AvroRecordSchema */ public function fields(): array { @@ -120,7 +133,7 @@ public function fields(): array } /** - * @return array a hash table of the fields of this AvroRecordSchema fields + * @return array a hash table of the fields of this AvroRecordSchema fields * keyed by each field's name */ public function fieldsHash(): array @@ -136,6 +149,9 @@ public function fieldsHash(): array return $this->fieldsHash; } + /** + * @return array + */ public function fieldsByAlias(): array { $hash = []; diff --git a/lang/php/lib/Schema/AvroSchema.php b/lang/php/lib/Schema/AvroSchema.php index 22cc8a5f30d..9471acd56d1 100644 --- a/lang/php/lib/Schema/AvroSchema.php +++ b/lang/php/lib/Schema/AvroSchema.php @@ -48,6 +48,12 @@ * qualified). It also has additional attributes such as doc, which named schemas * enum and record have (though not fixed schemas, which also have names), and * fields also have default and order attributes, shared by no other schema type. + * + * @phpstan-type AvroPrimitiveType 'null'|'boolean'|'int'|'long'|'float'|'double'|'string'|'bytes' + * @phpstan-type AvroComplexType 'array'|'map'|'union'|'enum'|'fixed'|'record'|'error'|'request' + * @phpstan-type AvroSchemaType AvroPrimitiveType|AvroComplexType + * @phpstan-type AvroSchemaDefinitionArray array + * @phpstan-type AvroSchemaUnionDefinitionArray list */ class AvroSchema implements \Stringable { @@ -264,9 +270,9 @@ class AvroSchema implements \Stringable protected ?AvroLogicalType $logicalType = null; /** - * @var array list of primitive schema type names + * @var array list of primitive schema type names */ - private static $primitiveTypes = [ + private static array $primitiveTypes = [ self::NULL_TYPE, self::BOOLEAN_TYPE, self::STRING_TYPE, @@ -278,18 +284,19 @@ class AvroSchema implements \Stringable ]; /** - * @var array list of named schema type names + * @var array list of named schema type names */ - private static $namedTypes = [ + private static array $namedTypes = [ self::FIXED_SCHEMA, self::ENUM_SCHEMA, self::RECORD_SCHEMA, self::ERROR_SCHEMA, ]; /** - * @var array list of names of reserved attributes + * @var array list of names of reserved attributes + * @phpstan-ignore property.onlyWritten */ - private static $reservedAttrs = [ + private static array $reservedAttrs = [ self::TYPE_ATTR, self::NAME_ATTR, self::NAMESPACE_ATTR, @@ -307,12 +314,13 @@ class AvroSchema implements \Stringable * a class which extends AvroSchema */ public function __construct( - public readonly string|AvroSchema $type + public readonly string|self $type ) { } /** - * @returns string the JSON-encoded representation of this Avro schema. + * @throws \JsonException + * @return string the JSON-encoded representation of this Avro schema. */ public function __toString(): string { @@ -322,7 +330,7 @@ public function __toString(): string /** * @uses self::realParse() */ - public static function parse(string $json): AvroSchema + public static function parse(string $json): self { $schemata = new AvroNamedSchemata(); @@ -333,15 +341,15 @@ public static function parse(string $json): AvroSchema } /** - * @param null|array|string $avro JSON-decoded schema - * @param null|string $default_namespace namespace of enclosing schema + * @param null|AvroSchemaDefinitionArray|AvroSchemaUnionDefinitionArray|string $avro JSON-decoded schema + * @param null|string $defaultNamespace namespace of enclosing schema * @param AvroNamedSchemata $schemata reference to named schemas * @throws AvroSchemaParseException * @throws AvroException */ public static function realParse( array|string|null $avro, - ?string $default_namespace = null, + ?string $defaultNamespace = null, AvroNamedSchemata $schemata = new AvroNamedSchemata() ): AvroSchema { if (is_array($avro)) { @@ -377,7 +385,7 @@ public static function realParse( if (self::isNamedType($type)) { $name = $avro[self::NAME_ATTR] ?? null; $namespace = $avro[self::NAMESPACE_ATTR] ?? null; - $new_name = new AvroName($name, $namespace, $default_namespace); + $new_name = new AvroName($name, $namespace, $defaultNamespace); $doc = $avro[self::DOC_ATTR] ?? null; $aliases = $avro[self::ALIASES_ATTR] ?? null; @@ -441,7 +449,7 @@ public static function realParse( doc: $doc, fields: $fields, schemata: $schemata, - schema_type: $type, + schemaType: $type, aliases: $aliases ); default: @@ -451,12 +459,12 @@ public static function realParse( return match ($type) { self::ARRAY_SCHEMA => new AvroArraySchema( items: $avro[self::ITEMS_ATTR], - defaultNamespace: $default_namespace, + defaultNamespace: $defaultNamespace, schemata: $schemata ), self::MAP_SCHEMA => new AvroMapSchema( values: $avro[self::VALUES_ATTR], - defaultNamespace: $default_namespace, + defaultNamespace: $defaultNamespace, schemata: $schemata ), default => throw new AvroSchemaParseException( @@ -467,7 +475,7 @@ public static function realParse( !array_key_exists(self::TYPE_ATTR, $avro) && AvroUtil::isList($avro) ) { - return new AvroUnionSchema($avro, $default_namespace, $schemata); + return new AvroUnionSchema($avro, $defaultNamespace, $schemata); } else { throw new AvroSchemaParseException(sprintf( 'Undefined type: %s', @@ -506,7 +514,7 @@ public static function isValidType(?string $type): bool /** * @param null|string $type a schema type name - * @returns boolean true if the given type name is a primitive schema type + * @return bool true if the given type name is a primitive schema type * name and false otherwise. */ public static function isPrimitiveType(?string $type): bool @@ -516,7 +524,7 @@ public static function isPrimitiveType(?string $type): bool /** * @param null|string $type a schema type name - * @returns bool true if the given type name is a named schema type name + * @return bool true if the given type name is a named schema type name * and false otherwise. */ public static function isNamedType(?string $type): bool @@ -524,7 +532,7 @@ public static function isNamedType(?string $type): bool return in_array($type, self::$namedTypes, true); } - public static function hasValidAliases($aliases): void + public static function hasValidAliases(mixed $aliases): void { if (null === $aliases) { return; @@ -555,10 +563,10 @@ public static function hasValidDoc(mixed $doc): void } /** - * @returns boolean true if $datum is valid for $expected_schema - * and false otherwise. * @param mixed $datum * @throws AvroSchemaParseException + * @return bool true if $datum is valid for $expected_schema + * and false otherwise. */ public static function isValidDatum(AvroSchema $expected_schema, $datum): bool { @@ -657,7 +665,7 @@ public static function isValidDatum(AvroSchema $expected_schema, $datum): bool case self::RECORD_SCHEMA: case self::ERROR_SCHEMA: case self::REQUEST_SCHEMA: - if (!($expected_schema instanceof AvroRecordSchema)) { + if (!$expected_schema instanceof AvroRecordSchema) { return false; } @@ -678,7 +686,7 @@ public static function isValidDatum(AvroSchema $expected_schema, $datum): bool } /** - * @returns string|AvroNamedSchema schema type name of this schema + * @return AvroNamedSchema|AvroSchemaType schema type name of this schema */ public function type() { @@ -690,6 +698,9 @@ public function logicalType(): ?AvroLogicalType return $this->logicalType; } + /** + * @return AvroSchemaDefinitionArray|string JSON-encodable representation of this Avro schema + */ public function toAvro(): string|array { $avro = [self::TYPE_ATTR => $this->type]; @@ -702,8 +713,8 @@ public function toAvro(): string|array } /** - * @returns mixed value of the attribute with the given attribute name * @param mixed $attribute + * @return mixed value of the attribute with the given attribute name */ public function attribute($attribute) { @@ -711,13 +722,14 @@ public function attribute($attribute) } /** + * @param null|AvroSchemaDefinitionArray|string $avro JSON-decoded sub-schema * @throws AvroSchemaParseException * @uses AvroSchema::realParse() */ - protected static function subparse(array|string|null $avro, ?string $default_namespace, AvroNamedSchemata $schemata): self + protected static function subparse(array|string|null $avro, ?string $defaultNamespace, AvroNamedSchemata $schemata): self { try { - return self::realParse($avro, $default_namespace, $schemata); + return self::realParse($avro, $defaultNamespace, $schemata); } catch (AvroSchemaParseException $e) { throw $e; } catch (\Throwable) { @@ -731,6 +743,7 @@ protected static function subparse(array|string|null $avro, ?string $default_nam } /** + * @param AvroSchemaDefinitionArray $avro * @throws AvroSchemaParseException * @return array{0: int, 1: int} [precision, scale] */ diff --git a/lang/php/lib/Schema/AvroUnionSchema.php b/lang/php/lib/Schema/AvroUnionSchema.php index 8220fb6463b..c6b6cbf279e 100644 --- a/lang/php/lib/Schema/AvroUnionSchema.php +++ b/lang/php/lib/Schema/AvroUnionSchema.php @@ -32,12 +32,12 @@ class AvroUnionSchema extends AvroSchema */ public array $schemaFromSchemataIndices; /** - * @var AvroSchema[] list of schemas of this union + * @var array list of schemas of this union */ private array $schemas; /** - * @param AvroSchema[] $schemas list of schemas in the union + * @param array $schemas list of schemas in the union * @param null|string $defaultNamespace namespace of enclosing schema * @throws AvroSchemaParseException */ @@ -46,26 +46,26 @@ public function __construct(array $schemas, ?string $defaultNamespace, AvroNamed parent::__construct(AvroSchema::UNION_SCHEMA); $this->schemaFromSchemataIndices = []; - $schema_types = []; + $schemaTypes = []; foreach ($schemas as $index => $schema) { - $is_schema_from_schemata = false; - $new_schema = null; + $isSchemaFromSchemata = false; + $newSchema = null; if ( is_string($schema) - && ($new_schema = $schemata->schemaByName( + && ($newSchema = $schemata->schemaByName( new AvroName($schema, null, $defaultNamespace) )) ) { - $is_schema_from_schemata = true; + $isSchemaFromSchemata = true; } else { - $new_schema = self::subparse($schema, $defaultNamespace, $schemata); + $newSchema = self::subparse($schema, $defaultNamespace, $schemata); } - $schemaType = $new_schema->type; + $schemaType = $newSchema->type; if ( self::isValidType($schemaType) && !self::isNamedType($schemaType) - && in_array($schemaType, $schema_types) + && in_array($schemaType, $schemaTypes) ) { throw new AvroSchemaParseException(sprintf('"%s" is already in union', $schemaType)); } @@ -74,16 +74,16 @@ public function __construct(array $schemas, ?string $defaultNamespace, AvroNamed throw new AvroSchemaParseException('Unions cannot contain other unions'); } - $schema_types[] = $schemaType; - $this->schemas[] = $new_schema; - if ($is_schema_from_schemata) { + $schemaTypes[] = $schemaType; + $this->schemas[] = $newSchema; + if ($isSchemaFromSchemata) { $this->schemaFromSchemataIndices[] = $index; } } } /** - * @returns AvroSchema[] + * @return array */ public function schemas(): array { @@ -105,6 +105,9 @@ public function schemaByIndex($index): AvroSchema throw new AvroSchemaParseException('Invalid union schema index'); } + /** + * @return array|string Avro representation of this schema + */ public function toAvro(): string|array { $avro = []; diff --git a/lang/php/phpstan-baseline.neon b/lang/php/phpstan-baseline.neon new file mode 100644 index 00000000000..021062d1d0f --- /dev/null +++ b/lang/php/phpstan-baseline.neon @@ -0,0 +1,148 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +parameters: + ignoreErrors: + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIOBinaryDecoder\:\:skipArray\(\) expects Apache\\Avro\\Schema\\AvroArraySchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIOBinaryDecoder\:\:skipFixed\(\) expects Apache\\Avro\\Schema\\AvroFixedSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIOBinaryDecoder\:\:skipMap\(\) expects Apache\\Avro\\Schema\\AvroMapSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIOBinaryDecoder\:\:skipRecord\(\) expects Apache\\Avro\\Schema\\AvroRecordSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIOBinaryDecoder\:\:skipUnion\(\) expects Apache\\Avro\\Schema\\AvroUnionSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readArray\(\) expects Apache\\Avro\\Schema\\AvroArraySchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writers_schema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readEnum\(\) expects Apache\\Avro\\Schema\\AvroEnumSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writers_schema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readFixed\(\) expects Apache\\Avro\\Schema\\AvroFixedSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readMap\(\) expects Apache\\Avro\\Schema\\AvroMapSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readRecord\(\) expects Apache\\Avro\\Schema\\AvroRecordSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writers_schema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readUnion\(\) expects Apache\\Avro\\Schema\\AvroUnionSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#2 \$readersSchema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readArray\(\) expects Apache\\Avro\\Schema\\AvroArraySchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#2 \$readers_schema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readEnum\(\) expects Apache\\Avro\\Schema\\AvroEnumSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#2 \$readers_schema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readFixed\(\) expects Apache\\Avro\\Schema\\AvroFixedSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#2 \$readersSchema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readMap\(\) expects Apache\\Avro\\Schema\\AvroMapSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#2 \$readersSchema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readRecord\(\) expects Apache\\Avro\\Schema\\AvroRecordSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#2 \$readers_schema of method Apache\\Avro\\Datum\\AvroIODatumReader\:\:readUnion\(\) expects Apache\\Avro\\Schema\\AvroUnionSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumReader.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumWriter\:\:writeEnum\(\) expects Apache\\Avro\\Schema\\AvroEnumSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumWriter.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumWriter\:\:writeRecord\(\) expects Apache\\Avro\\Schema\\AvroRecordSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumWriter.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumWriter\:\:writeUnion\(\) expects Apache\\Avro\\Schema\\AvroUnionSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumWriter.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumWriter\:\:writeArray\(\) expects Apache\\Avro\\Schema\\AvroArraySchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumWriter.php + + - + message: '#^Parameter \#1 \$writersSchema of method Apache\\Avro\\Datum\\AvroIODatumWriter\:\:writeMap\(\) expects Apache\\Avro\\Schema\\AvroMapSchema, Apache\\Avro\\Schema\\AvroSchema given\.$#' + identifier: argument.type + count: 1 + path: lib/Datum/AvroIODatumWriter.php diff --git a/lang/php/phpstan.neon b/lang/php/phpstan.neon index 1eae92a3e47..c1d1b6b807e 100644 --- a/lang/php/phpstan.neon +++ b/lang/php/phpstan.neon @@ -13,10 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +includes: + - phpstan-baseline.neon + parameters: - level: 3 + level: 6 paths: - lib - - test bootstrapFiles: - test/test_helper.php + treatPhpDocTypesAsCertain: false diff --git a/lang/php/test/DataFileTest.php b/lang/php/test/DataFileTest.php index dd8d77b3370..7810c593a78 100644 --- a/lang/php/test/DataFileTest.php +++ b/lang/php/test/DataFileTest.php @@ -397,7 +397,8 @@ protected function add_data_file(string $data_file): string protected function remove_data_files(): void { - if (self::REMOVE_DATA_FILES && $this->dataFiles) { + /** @phpstan-ignore booleanAnd.leftAlwaysTrue */ + if (self::REMOVE_DATA_FILES && [] !== $this->dataFiles) { foreach ($this->dataFiles as $data_file) { self::remove_data_file($data_file); } diff --git a/lang/php/test/DatumIOTest.php b/lang/php/test/DatumIOTest.php index 717ee78a247..8614cf61365 100644 --- a/lang/php/test/DatumIOTest.php +++ b/lang/php/test/DatumIOTest.php @@ -416,10 +416,7 @@ public function test_field_default_value( if (array_key_exists('f', $record)) { $this->assertEquals($default_value, $record['f']); } else { - $this->assertTrue(false, sprintf( - 'expected field record[f]: %s', - print_r($record, true) - )); + $this->fail(sprintf('expected field record[f]: %s', print_r($record, true))); } } diff --git a/lang/php/test/InterOpTest.php b/lang/php/test/InterOpTest.php index 060c6b93a55..e9ab5cb6416 100644 --- a/lang/php/test/InterOpTest.php +++ b/lang/php/test/InterOpTest.php @@ -20,22 +20,22 @@ namespace Apache\Avro\Tests; +use Apache\Avro\AvroIO; use Apache\Avro\DataFile\AvroDataIO; -use Apache\Avro\IO\AvroFile; use Apache\Avro\Schema\AvroSchema; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; class InterOpTest extends TestCase { - private string $projection_json; + private string $projectionJson; private AvroSchema $projection; public function setUp(): void { $interop_schema_file_name = AVRO_INTEROP_SCHEMA; - $this->projection_json = file_get_contents($interop_schema_file_name); - $this->projection = AvroSchema::parse($this->projection_json); + $this->projectionJson = file_get_contents($interop_schema_file_name); + $this->projection = AvroSchema::parse($this->projectionJson); } public static function file_name_provider(): array @@ -69,8 +69,8 @@ public function test_read(string $file_name): void { $dr = AvroDataIO::openFile( $file_name, - AvroFile::READ_MODE, - $this->projection_json + AvroIO::READ_MODE, + $this->projectionJson ); $data = $dr->data(); From fc8392377cc6268a611a872aea01789bafa71c71 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Mon, 26 Jan 2026 15:13:36 +0100 Subject: [PATCH 2/5] update exception messages and fixes check for bz2 --- lang/php/lib/DataFile/AvroDataIOReader.php | 6 +++--- lang/php/lib/DataFile/AvroDataIOWriter.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lang/php/lib/DataFile/AvroDataIOReader.php b/lang/php/lib/DataFile/AvroDataIOReader.php index 177b73b6e27..ec89e9fec3f 100644 --- a/lang/php/lib/DataFile/AvroDataIOReader.php +++ b/lang/php/lib/DataFile/AvroDataIOReader.php @@ -228,7 +228,7 @@ private function gzUncompress(string $compressed): string $datum = gzinflate($compressed); if (false === $datum) { - throw new AvroException('gzip/deflate uncompression failed.'); + throw new AvroException('gzip uncompression failed.'); } return $datum; @@ -261,8 +261,8 @@ private function bzUncompress(string $compressed): string } $datum = bzdecompress($compressed); - if (false === $datum) { - throw new AvroException('bz uncompression failed.'); + if (!is_string($datum)) { + throw new AvroException('bz2 uncompression failed.'); } return $datum; diff --git a/lang/php/lib/DataFile/AvroDataIOWriter.php b/lang/php/lib/DataFile/AvroDataIOWriter.php index 4ad9e2422e6..eda333c1a1b 100644 --- a/lang/php/lib/DataFile/AvroDataIOWriter.php +++ b/lang/php/lib/DataFile/AvroDataIOWriter.php @@ -219,7 +219,7 @@ private function gzCompress(string $data): string { $data = gzdeflate($data); if (false === $data) { - throw new AvroException('Deflate compression failed.'); + throw new AvroException('gzip compression failed.'); } return $data; @@ -270,7 +270,7 @@ private function bzCompress(string $toWrite): string $toWrite = bzcompress($toWrite); if (is_int($toWrite)) { - throw new AvroException("bz compression failed (error: {$toWrite})."); + throw new AvroException("bz2 compression failed (error: {$toWrite})."); } return $toWrite; From 4d09a688b18f333bc04ac0f75a1ee8b750a8c788 Mon Sep 17 00:00:00 2001 From: Mattia Basone Date: Mon, 26 Jan 2026 23:21:57 +0100 Subject: [PATCH 3/5] review, round one --- lang/php/lib/Avro.php | 2 +- lang/php/lib/AvroDebug.php | 1 + lang/php/lib/DataFile/AvroDataIOReader.php | 2 +- lang/php/lib/DataFile/AvroDataIOWriter.php | 9 +++---- lang/php/lib/Datum/AvroIODatumReader.php | 25 ++++++++++--------- lang/php/lib/Datum/AvroIODatumWriter.php | 2 +- lang/php/lib/IO/AvroStringIO.php | 7 +----- lang/php/lib/Protocol/AvroProtocol.php | 4 +-- lang/php/lib/Protocol/AvroProtocolMessage.php | 2 +- lang/php/lib/Schema/AvroField.php | 4 +-- lang/php/lib/Schema/AvroRecordSchema.php | 2 +- 11 files changed, 28 insertions(+), 32 deletions(-) diff --git a/lang/php/lib/Avro.php b/lang/php/lib/Avro.php index 08b68856b8d..28827931108 100644 --- a/lang/php/lib/Avro.php +++ b/lang/php/lib/Avro.php @@ -62,7 +62,7 @@ public static function checkPlatform(): void /** * @return bool true if the PHP GMP extension is used and false otherwise. * @internal Requires Avro::check64Bit() (exposed via Avro::checkPlatform()) - * to have been called to set Avro::$biginteger_mode. + * to have been called to set Avro::$bigIntegerMode. */ public static function usesGmp(): bool { diff --git a/lang/php/lib/AvroDebug.php b/lang/php/lib/AvroDebug.php index eae3ef1daf5..feb74aef5e5 100644 --- a/lang/php/lib/AvroDebug.php +++ b/lang/php/lib/AvroDebug.php @@ -117,6 +117,7 @@ public static function decArray(string $str): array /** * @param string $format one of 'ctrl', 'hex', or 'dec'. * See {@link self::asciiArray()} for more description + * @throws AvroException * @return string of bytes joined by $joiner * @uses asciiArray() */ diff --git a/lang/php/lib/DataFile/AvroDataIOReader.php b/lang/php/lib/DataFile/AvroDataIOReader.php index ec89e9fec3f..eb0188231d1 100644 --- a/lang/php/lib/DataFile/AvroDataIOReader.php +++ b/lang/php/lib/DataFile/AvroDataIOReader.php @@ -211,7 +211,7 @@ private function skipSync(): bool /** * Reads the block header (which includes the count of items in the block * and the length in bytes of the block) - * @return int length in bytes of the block. + * @return int|string length in bytes of the block. It returns a string if AvroGMP is enabled. */ private function readBlockHeader(): string|int { diff --git a/lang/php/lib/DataFile/AvroDataIOWriter.php b/lang/php/lib/DataFile/AvroDataIOWriter.php index eda333c1a1b..c40d83a7ae0 100644 --- a/lang/php/lib/DataFile/AvroDataIOWriter.php +++ b/lang/php/lib/DataFile/AvroDataIOWriter.php @@ -99,9 +99,9 @@ public function __construct( $this->syncMarker = $dfr->sync_marker; $this->metadata[AvroDataIO::METADATA_CODEC_ATTR] = $this->codec = $dfr->metadata[AvroDataIO::METADATA_CODEC_ATTR]; - $schema_from_file = $dfr->metadata[AvroDataIO::METADATA_SCHEMA_ATTR]; - $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = $schema_from_file; - $this->datumWriter->writersSchema = AvroSchema::parse($schema_from_file); + $schemaFromFile = $dfr->metadata[AvroDataIO::METADATA_SCHEMA_ATTR]; + $this->metadata[AvroDataIO::METADATA_SCHEMA_ATTR] = $schemaFromFile; + $this->datumWriter->writersSchema = AvroSchema::parse($schemaFromFile); $this->seek(0, SEEK_END); } } @@ -161,10 +161,9 @@ private function writeHeader(): void } /** - * @param string $bytes * @uses AvroIO::write() */ - private function write($bytes): int + private function write(string $bytes): int { return $this->io->write($bytes); } diff --git a/lang/php/lib/Datum/AvroIODatumReader.php b/lang/php/lib/Datum/AvroIODatumReader.php index 0d7a8bc50f2..75c83b53675 100644 --- a/lang/php/lib/Datum/AvroIODatumReader.php +++ b/lang/php/lib/Datum/AvroIODatumReader.php @@ -127,9 +127,8 @@ public static function schemasMatch( if ( AvroSchema::isPrimitiveType($writersSchemaType) - && AvroSchema::isPrimitiveType($readersSchemaType) ) { - return $writersSchemaType === $readersSchemaType; + return true; } switch ($readersSchemaType) { @@ -478,20 +477,20 @@ public function readDefaultValue(AvroSchema $fieldSchema, mixed $defaultValue): return $this->readBytes($fieldSchema, $fieldSchema, $defaultValue); case AvroSchema::ARRAY_SCHEMA: $array = []; - foreach ($defaultValue as $json_val) { + foreach ($defaultValue as $jsonValue) { /** @phpstan-ignore method.notFound */ - $val = $this->readDefaultValue($fieldSchema->items(), $json_val); + $val = $this->readDefaultValue($fieldSchema->items(), $jsonValue); $array[] = $val; } return $array; case AvroSchema::MAP_SCHEMA: $map = []; - foreach ($defaultValue as $key => $json_val) { + foreach ($defaultValue as $key => $jsonValue) { $map[$key] = $this->readDefaultValue( /** @phpstan-ignore method.notFound */ $fieldSchema->values(), - $json_val + $jsonValue ); } @@ -506,17 +505,19 @@ public function readDefaultValue(AvroSchema $fieldSchema, mixed $defaultValue): case AvroSchema::FIXED_SCHEMA: return $defaultValue; case AvroSchema::RECORD_SCHEMA: + /** @var AvroRecordSchema $fieldSchema */ $record = []; - /** @phpstan-ignore method.notFound */ foreach ($fieldSchema->fields() as $field) { - $field_name = $field->name(); - if (!$json_val = $defaultValue[$field_name]) { - $json_val = $field->default_value(); + $fieldName = $field->name(); + if (!array_key_exists($fieldName, $defaultValue)) { + $jsonValue = $field->defaultValue(); + } else { + $jsonValue = $defaultValue[$fieldName]; } - $record[$field_name] = $this->readDefaultValue( + $record[$fieldName] = $this->readDefaultValue( $field->type(), - $json_val + $jsonValue ); } diff --git a/lang/php/lib/Datum/AvroIODatumWriter.php b/lang/php/lib/Datum/AvroIODatumWriter.php index e4d9be563ea..08760d4c139 100644 --- a/lang/php/lib/Datum/AvroIODatumWriter.php +++ b/lang/php/lib/Datum/AvroIODatumWriter.php @@ -135,7 +135,7 @@ private function writeValidatedData(AvroSchema $writersSchema, mixed $datum, Avr default: throw new AvroException(sprintf( 'Unknown type: %s', - $writersSchema->type + $writersSchema->type() )); } } diff --git a/lang/php/lib/IO/AvroStringIO.php b/lang/php/lib/IO/AvroStringIO.php index 8c55438fe58..9c12b7bfe5a 100644 --- a/lang/php/lib/IO/AvroStringIO.php +++ b/lang/php/lib/IO/AvroStringIO.php @@ -110,16 +110,11 @@ public function length(): int } /** - * @param mixed $offset - * @param mixed $whence * @throws AvroIOException if the seek failed. * @return bool true if successful */ - public function seek($offset, $whence = self::SEEK_SET): bool + public function seek(int $offset, int $whence = self::SEEK_SET): bool { - if (!is_int($offset)) { - throw new AvroIOException('Seek offset must be an integer.'); - } // Prevent seeking before BOF switch ($whence) { case self::SEEK_SET: diff --git a/lang/php/lib/Protocol/AvroProtocol.php b/lang/php/lib/Protocol/AvroProtocol.php index c7e2eba2be5..ef6c09e74df 100644 --- a/lang/php/lib/Protocol/AvroProtocol.php +++ b/lang/php/lib/Protocol/AvroProtocol.php @@ -31,10 +31,10 @@ * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema * @phpstan-import-type AvroProtocolMessageDefinitionArray from AvroProtocolMessage * @phpstan-type AvroProtocolDefinitionArray array{ - * types: AvroSchemaDefinitionArray, + * types?: AvroSchemaDefinitionArray, * protocol: string, * namespace: string, - * messages: array + * messages?: array * } */ class AvroProtocol diff --git a/lang/php/lib/Protocol/AvroProtocolMessage.php b/lang/php/lib/Protocol/AvroProtocolMessage.php index 5546b51e741..5d31ffa8f1a 100644 --- a/lang/php/lib/Protocol/AvroProtocolMessage.php +++ b/lang/php/lib/Protocol/AvroProtocolMessage.php @@ -31,7 +31,7 @@ /** * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema - * @phpstan-type AvroProtocolMessageDefinitionArray array{request: AvroSchemaDefinitionArray, response: string} + * @phpstan-type AvroProtocolMessageDefinitionArray array{request: AvroSchemaDefinitionArray, response?: string} */ class AvroProtocolMessage { diff --git a/lang/php/lib/Schema/AvroField.php b/lang/php/lib/Schema/AvroField.php index 8658cb3d709..eb3e65e9ff4 100644 --- a/lang/php/lib/Schema/AvroField.php +++ b/lang/php/lib/Schema/AvroField.php @@ -79,7 +79,7 @@ class AvroField extends AvroSchema implements AvroAliasedSchema private bool $hasDefault; /** - * @var string field default value + * @var mixed field default value */ private mixed $default; /** @@ -215,7 +215,7 @@ public function name(): string /** * @return mixed the default value of this field */ - public function defaultValue() + public function defaultValue(): mixed { return $this->default; } diff --git a/lang/php/lib/Schema/AvroRecordSchema.php b/lang/php/lib/Schema/AvroRecordSchema.php index f3aa229769e..5987ee90a4e 100644 --- a/lang/php/lib/Schema/AvroRecordSchema.php +++ b/lang/php/lib/Schema/AvroRecordSchema.php @@ -61,7 +61,7 @@ public function __construct( parent::__construct($schemaType, $name, $doc, $schemata, $aliases); } - [$x, $namespace] = $name->nameAndNamespace(); + [, $namespace] = $name->nameAndNamespace(); $this->fields = self::parseFields($fields, $namespace, $schemata); } From e997da5808dce1353dce3e4f576de18b9beabe75 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Tue, 27 Jan 2026 14:38:45 +0100 Subject: [PATCH 4/5] fix bug in AvroStringIO.php --- lang/php/lib/IO/AvroStringIO.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/php/lib/IO/AvroStringIO.php b/lang/php/lib/IO/AvroStringIO.php index 9c12b7bfe5a..e371cc948b4 100644 --- a/lang/php/lib/IO/AvroStringIO.php +++ b/lang/php/lib/IO/AvroStringIO.php @@ -125,7 +125,7 @@ public function seek(int $offset, int $whence = self::SEEK_SET): bool break; case self::SEEK_CUR: - if (0 > $this->currentIndex + $whence) { + if (0 > $this->currentIndex + $offset) { throw new AvroIOException('Cannot seek before beginning of file.'); } $this->currentIndex += $offset; From 53f58943b52453457374540bad977af3dcc34292 Mon Sep 17 00:00:00 2001 From: mattiabasone Date: Tue, 27 Jan 2026 14:49:38 +0100 Subject: [PATCH 5/5] fix check on primitive type in AvroIODatumReader.php --- lang/php/lib/Datum/AvroIODatumReader.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lang/php/lib/Datum/AvroIODatumReader.php b/lang/php/lib/Datum/AvroIODatumReader.php index 75c83b53675..48f193f4c02 100644 --- a/lang/php/lib/Datum/AvroIODatumReader.php +++ b/lang/php/lib/Datum/AvroIODatumReader.php @@ -29,6 +29,7 @@ use Apache\Avro\Schema\AvroLogicalType; use Apache\Avro\Schema\AvroMapSchema; use Apache\Avro\Schema\AvroName; +use Apache\Avro\Schema\AvroPrimitiveSchema; use Apache\Avro\Schema\AvroRecordSchema; use Apache\Avro\Schema\AvroSchema; use Apache\Avro\Schema\AvroSchemaParseException; @@ -126,7 +127,9 @@ public static function schemasMatch( } if ( - AvroSchema::isPrimitiveType($writersSchemaType) + $writersSchema instanceof AvroPrimitiveSchema + && $readersSchema instanceof AvroPrimitiveSchema + && $writersSchemaType === $readersSchemaType ) { return true; }