This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/main by this push:
     new c499eefb48 Improve code quality and increase PHPStan level (#3634)
c499eefb48 is described below

commit c499eefb48aa2db906c7bca14a047223806f36db
Author: Mattia Basone <[email protected]>
AuthorDate: Fri Jan 30 08:17:44 2026 +0100

    Improve code quality and increase PHPStan level (#3634)
    
    * improve code quality and increase phpstan level
    
    * update exception messages and fixes check for bz2
    
    * review, round one
    
    * fix bug in AvroStringIO.php
    
    * fix check on primitive type in AvroIODatumReader.php
---
 lang/php/lib/Avro.php                             |  16 +-
 lang/php/lib/AvroDebug.php                        |  61 +++---
 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        | 174 +++++++++------
 lang/php/lib/Datum/AvroIOBinaryDecoder.php        |  64 +++---
 lang/php/lib/Datum/AvroIOBinaryEncoder.php        |  14 +-
 lang/php/lib/Datum/AvroIODatumReader.php          | 247 ++++++++++++----------
 lang/php/lib/Datum/AvroIODatumWriter.php          |  76 +++----
 lang/php/lib/Datum/AvroIOSchemaMatchException.php |  12 +-
 lang/php/lib/IO/AvroFile.php                      |  38 ++--
 lang/php/lib/IO/AvroStringIO.php                  |  91 +++-----
 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                 |  24 ++-
 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          |  44 ++--
 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, 908 insertions(+), 620 deletions(-)

diff --git a/lang/php/lib/Avro.php b/lang/php/lib/Avro.php
index 778944c6a5..2882793110 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.
+     *           to have been called to set Avro::$bigIntegerMode.
      */
     public static function usesGmp(): bool
     {
-        return self::GMP_BIGINTEGER_MODE === self::$biginteger_mode;
+        return self::GMP_BIGINTEGER_MODE === self::$bigIntegerMode;
     }
 
     /**
@@ -74,17 +74,17 @@ class Avro
      *
      * @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 b64e7b1904..feb74aef5e 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 <code>vprintf</code>.
-     * @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<string> $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 @@ class AvroDebug
      *                  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,36 @@ class AvroDebug
     }
 
     /**
-     * @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
+     * @throws AvroException
+     * @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 +135,16 @@ class AvroDebug
      * 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 +184,7 @@ class AvroDebug
             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 +192,7 @@ class AvroDebug
 
                         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 +211,7 @@ class AvroDebug
 
                                 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 +219,7 @@ class AvroDebug
                         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 44828627a5..cc1cd2a475 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 @@ class AvroGMP
 
     /**
      * @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 @@ class AvroGMP
     }
 
     /**
-     * @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 @@ class AvroGMP
         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 @@ class AvroGMP
     // 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 @@ class AvroGMP
     }
 
     /**
-     * @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 @@ class AvroGMP
     }
 
     /**
-     * @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 @@ class AvroGMP
     }
 
     /**
-     * @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 @@ class AvroGMP
     }
 
     /**
-     * @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 @@ class AvroGMP
     }
 
     /**
-     * @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 4cec58f80f..869c76a0ff 100644
--- a/lang/php/lib/AvroIO.php
+++ b/lang/php/lib/AvroIO.php
@@ -58,8 +58,8 @@ interface AvroIO
     /**
      * 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 @@ interface AvroIO
      *
      * @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 @@ interface AvroIO
      * Note isEof() is <b>not</b> 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 cdd4e2f29c..b32d8a8eed 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 7027664b69..06ca2aee6a 100644
--- a/lang/php/lib/DataFile/AvroDataIO.php
+++ b/lang/php/lib/DataFile/AvroDataIO.php
@@ -24,6 +24,7 @@ use Apache\Avro\AvroIO;
 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<string> 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 @@ class AvroDataIO
     /**
      * @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<string> 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 6990fba8c6..eb0188231d 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<string, mixed> 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 @@ class AvroDataIOReader
         }
         $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 @@ class AvroDataIOReader
     /**
      * @throws AvroException
      * @throws AvroIOException
-     * @return array of data from object container.
+     * @return list<mixed> 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 @@ class AvroDataIOReader
         $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 @@ class AvroDataIOReader
             );
         }
 
-        $this->metadata = $this->datum_reader->readData(
+        $this->metadata = $this->datumReader->readData(
             AvroDataIO::metadataSchema(),
             AvroDataIO::metadataSchema(),
             $this->decoder
@@ -187,19 +174,16 @@ class AvroDataIOReader
 
     /**
      * @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 @@ class AvroDataIOReader
 
     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 @@ class AvroDataIOReader
     /**
      * 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|string length in bytes of the block. It returns a string if 
AvroGMP is enabled.
      */
     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 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 (!is_string($datum)) {
+            throw new AvroException('bz2 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 1b76fd0d15..c40d83a7ae 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<string, mixed> 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);
+            $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);
         }
     }
 
-    /**
-     * @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 @@ class AvroDataIOWriter
 
     /**
      * 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 @@ class AvroDataIOWriter
     }
 
     /**
-     * @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,26 @@ class AvroDataIOWriter
     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(string $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 +179,99 @@ class AvroDataIOWriter
     /**
      * 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('gzip 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("bz2 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 6a7956c2e3..ac5fcf7e92 100644
--- a/lang/php/lib/Datum/AvroIOBinaryDecoder.php
+++ b/lang/php/lib/Datum/AvroIOBinaryDecoder.php
@@ -48,16 +48,13 @@ class AvroIOBinaryDecoder
     }
 
     /**
-     * @returns null
+     * @return null
      */
     public function readNull()
     {
         return null;
     }
 
-    /**
-     * @returns boolean
-     */
     public function readBoolean(): bool
     {
         return 1 === ord($this->nextByte());
@@ -65,7 +62,6 @@ class AvroIOBinaryDecoder
 
     /**
      * @param int $len count of bytes to read
-     * @returns string
      */
     public function read(int $len): string
     {
@@ -77,9 +73,6 @@ class AvroIOBinaryDecoder
         return (int) $this->readLong();
     }
 
-    /**
-     * @returns string|int
-     */
     public function readLong(): string|int
     {
         $byte = ord($this->nextByte());
@@ -133,9 +126,6 @@ class AvroIOBinaryDecoder
         return (float) $float[1];
     }
 
-    /**
-     * @returns double
-     */
     public function readDouble(): float
     {
         return self::longBitsToDouble($this->read(8));
@@ -147,7 +137,7 @@ class AvroIOBinaryDecoder
      * XXX: This is <b>not</b> 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 @@ class AvroIOBinaryDecoder
     /**
      * 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 @@ class AvroIOBinaryDecoder
 
     public function skipNull(): void
     {
-        return;
+
     }
 
     public function skipBoolean(): void
@@ -224,61 +210,61 @@ class AvroIOBinaryDecoder
         $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 6d5aa62430..1ef350809a 100644
--- a/lang/php/lib/Datum/AvroIOBinaryEncoder.php
+++ b/lang/php/lib/Datum/AvroIOBinaryEncoder.php
@@ -45,7 +45,7 @@ class AvroIOBinaryEncoder
      */
     public function writeNull($datum): void
     {
-        return;
+
     }
 
     public function writeBoolean(bool $datum): void
@@ -75,7 +75,7 @@ class AvroIOBinaryEncoder
 
     /**
      * @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 @@ class AvroIOBinaryEncoder
      * 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 @@ class AvroIOBinaryEncoder
      * {@link AvroIOBinaryEncoder::floatToIntBits()} for details.
      *
      * @param float $double
-     * @returns string bytes
+     * @return string bytes
      */
     public static function doubleToLongBits($double): string
     {
@@ -172,9 +172,7 @@ class AvroIOBinaryEncoder
         }
 
         $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 @@ class AvroIOBinaryEncoder
         $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 57768e31e0..48f193f4c0 100644
--- a/lang/php/lib/Datum/AvroIODatumReader.php
+++ b/lang/php/lib/Datum/AvroIODatumReader.php
@@ -29,6 +29,7 @@ use Apache\Avro\Schema\AvroFixedSchema;
 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;
@@ -43,50 +44,50 @@ use Apache\Avro\Schema\AvroUnionSchema;
 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 +95,76 @@ class AvroIODatumReader
             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)) {
+        if (
+            $writersSchema instanceof AvroPrimitiveSchema
+            && $readersSchema instanceof AvroPrimitiveSchema
+            && $writersSchemaType === $readersSchemaType
+        ) {
             return true;
         }
 
-        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 +174,8 @@ class AvroIODatumReader
             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 +185,8 @@ class AvroIODatumReader
         }
 
         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 +196,8 @@ class AvroIODatumReader
         }
 
         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 +205,7 @@ class AvroIODatumReader
             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 +230,7 @@ class AvroIODatumReader
                 if (AvroSchema::FULLNAME_ATTR === $attribute_name) {
 
                     if (
-                        !($schema_two instanceof AvroAliasedSchema)
+                        !$schema_two instanceof AvroAliasedSchema
                     ) {
                         return false;
                     }
@@ -269,37 +273,42 @@ class AvroIODatumReader
         return $bytes;
     }
 
+    /**
+     * @throws AvroException
+     * @throws AvroIOSchemaMatchException
+     * @return list<mixed>
+     */
     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<int, mixed>
+     * @return array<string, mixed>
      */
     public function readMap(
-        AvroMapSchema $writers_schema,
-        AvroMapSchema $readers_schema,
+        AvroMapSchema $writersSchema,
+        AvroMapSchema $readersSchema,
         AvroIOBinaryDecoder $decoder
     ): array {
         $items = [];
@@ -314,8 +323,8 @@ class AvroIODatumReader
             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 +392,31 @@ class AvroIODatumReader
     }
 
     /**
-     * @returns array
+     * @return array<string, mixed>
      */
     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 +429,10 @@ class AvroIODatumReader
     }
 
     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,57 +441,59 @@ class AvroIODatumReader
             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<string, mixed>|bool|float|int|list<mixed>|string 
$defaultValue
      *
      * @throws AvroException if $field_schema type is unknown.
-     * @return null|array|bool|float|int|string
+     * @return null|array<string, mixed>|bool|float|int|list<mixed>|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 $jsonValue) {
                     /** @phpstan-ignore method.notFound */
-                    $val = $this->readDefaultValue($field_schema->items(), 
$json_val);
+                    $val = $this->readDefaultValue($fieldSchema->items(), 
$jsonValue);
                     $array[] = $val;
                 }
 
                 return $array;
             case AvroSchema::MAP_SCHEMA:
                 $map = [];
-                foreach ($default_value as $key => $json_val) {
+                foreach ($defaultValue as $key => $jsonValue) {
                     $map[$key] = $this->readDefaultValue(
                         /** @phpstan-ignore method.notFound */
-                        $field_schema->values(),
-                        $json_val
+                        $fieldSchema->values(),
+                        $jsonValue
                     );
                 }
 
@@ -490,30 +501,32 @@ class AvroIODatumReader
             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:
+                /** @var AvroRecordSchema $fieldSchema */
                 $record = [];
-                /** @phpstan-ignore method.notFound */
-                foreach ($field_schema->fields() as $field) {
-                    $field_name = $field->name();
-                    if (!$json_val = $default_value[$field_name]) {
-                        $json_val = $field->default_value();
+                foreach ($fieldSchema->fields() as $field) {
+                    $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
                     );
                 }
 
                 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 3f61c37e38..08760d4c13 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 @@ class AvroIODatumWriter
      * @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 @@ class AvroIODatumWriter
 
                 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 @@ class AvroIODatumWriter
     }
 
     /**
+     * @param list<mixed> $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 @@ class AvroIODatumWriter
     }
 
     /**
+     * @param array<string, mixed> $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 @@ class AvroIODatumWriter
         $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 @@ class AvroIODatumWriter
      * @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 @@ class AvroIODatumWriter
         }
 
         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 0eaa2b225d..8706674bc3 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 @@ use Apache\Avro\Schema\AvroSchema;
 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 7d6209bb6d..b0b6852ce5 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 @@ class AvroFile implements AvroIO
      */
     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 @@ class AvroFile implements AvroIO
     }
 
     /**
-     * @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 @@ class AvroFile implements AvroIO
 
     /**
      * 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 @@ class AvroFile implements AvroIO
     }
 
     /**
-     * @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 @@ class AvroFile implements AvroIO
 
     /**
      * @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 @@ class AvroFile implements AvroIO
             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 @@ class AvroFile implements AvroIO
     }
 
     /**
-     * @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 @@ class AvroFile implements AvroIO
     }
 
     /**
-     * @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 4136f6deb3..e371cc948b 100644
--- a/lang/php/lib/IO/AvroStringIO.php
+++ b/lang/php/lib/IO/AvroStringIO.php
@@ -29,66 +29,45 @@ use Apache\Avro\AvroIO;
  */
 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,72 +75,67 @@ class AvroStringIO implements AvroIO, \Stringable
      */
     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
+    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:
                 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 + $offset) {
                     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 +150,7 @@ class AvroStringIO implements AvroIO, \Stringable
      */
     public function tell(): int
     {
-        return $this->current_index;
+        return $this->currentIndex;
     }
 
     /**
@@ -184,12 +158,12 @@ class AvroStringIO implements AvroIO, \Stringable
      */
     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 +176,7 @@ class AvroStringIO implements AvroIO, \Stringable
     public function close(): bool
     {
         $this->checkClosed();
-        $this->is_closed = true;
+        $this->isClosed = true;
 
         return true;
     }
@@ -210,19 +184,18 @@ class AvroStringIO implements AvroIO, \Stringable
     /**
      * 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 +221,9 @@ class AvroStringIO implements AvroIO, \Stringable
     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 7a651bfe0c..ef6c09e74d 100644
--- a/lang/php/lib/Protocol/AvroProtocol.php
+++ b/lang/php/lib/Protocol/AvroProtocol.php
@@ -28,6 +28,14 @@ use Apache\Avro\Schema\AvroSchemaParseException;
 
 /**
  * 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<string, AvroProtocolMessageDefinitionArray>
+ * }
  */
 class AvroProtocol
 {
@@ -60,7 +68,7 @@ class AvroProtocol
     }
 
     /**
-     * @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 9677591dd2..5d31ffa8f1 100644
--- a/lang/php/lib/Protocol/AvroProtocolMessage.php
+++ b/lang/php/lib/Protocol/AvroProtocolMessage.php
@@ -29,6 +29,10 @@ use Apache\Avro\Schema\AvroRecordSchema;
 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 @@ class AvroProtocolMessage
             doc: null,
             fields: $avro['request'],
             schemata: $schemata,
-            schema_type: AvroSchema::REQUEST_SCHEMA
+            schemaType: AvroSchema::REQUEST_SCHEMA
         );
 
         $response = null;
@@ -58,7 +63,7 @@ class AvroProtocolMessage
                 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 13ca0a638d..90a33c4fea 100644
--- a/lang/php/lib/Schema/AvroAliasedSchema.php
+++ b/lang/php/lib/Schema/AvroAliasedSchema.php
@@ -22,7 +22,11 @@ declare(strict_types=1);
 
 namespace Apache\Avro\Schema;
 
+/**
+ * @phpstan-type AvroAliases list<string>
+ */
 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 ff8d6563b1..1a7a049a91 100644
--- a/lang/php/lib/Schema/AvroArraySchema.php
+++ b/lang/php/lib/Schema/AvroArraySchema.php
@@ -59,7 +59,7 @@ class AvroArraySchema extends AvroSchema
     }
 
     /**
-     * @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 8159bfead1..6b6fd12c98 100644
--- a/lang/php/lib/Schema/AvroEnumSchema.php
+++ b/lang/php/lib/Schema/AvroEnumSchema.php
@@ -43,7 +43,7 @@ class AvroEnumSchema extends AvroNamedSchema
 
         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 @@ class AvroEnumSchema extends AvroNamedSchema
     }
 
     /**
-     * @returns string[] this enum schema's symbols
+     * @return string[] this enum schema's symbols
      */
     public function symbols()
     {
@@ -69,7 +69,7 @@ class AvroEnumSchema extends AvroNamedSchema
 
     /**
      * @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 @@ class AvroEnumSchema extends AvroNamedSchema
 
     /**
      * @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 @@ class AvroEnumSchema extends AvroNamedSchema
     }
 
     /**
-     * @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 2d1510ffc0..eb3e65e9ff 100644
--- a/lang/php/lib/Schema/AvroField.php
+++ b/lang/php/lib/Schema/AvroField.php
@@ -24,6 +24,9 @@ namespace Apache\Avro\Schema;
 
 /**
  * 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<string> list of valid field sort order values
      */
     private static array $validFieldSortOrders = [
         self::ASC_SORT_ORDER,
@@ -76,13 +79,15 @@ class AvroField extends AvroSchema implements 
AvroAliasedSchema
     private bool $hasDefault;
 
     /**
-     * @var string field default value
+     * @var mixed field default value
      */
     private mixed $default;
     /**
      * @var null|string sort order of this field
      */
     private ?string $order;
+
+    /** @var null|AvroAliases */
     private ?array $aliases;
     private ?string $doc;
 
@@ -114,6 +119,7 @@ class AvroField extends AvroSchema implements 
AvroAliasedSchema
     }
 
     /**
+     * @param AvroSchemaDefinitionArray $avro
      * @throws AvroSchemaParseException
      */
     public static function fromFieldDefinition(array $avro, ?string 
$defaultNamespace, AvroNamedSchemata $schemata): self
@@ -166,6 +172,9 @@ class AvroField extends AvroSchema implements 
AvroAliasedSchema
         );
     }
 
+    /**
+     * @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 @@ class AvroField extends AvroSchema implements 
AvroAliasedSchema
     }
 
     /**
-     * @returns string the name of this field
+     * @return string the name of this field
      */
     public function name(): string
     {
@@ -204,21 +213,24 @@ class AvroField extends AvroSchema implements 
AvroAliasedSchema
     }
 
     /**
-     * @returns mixed the default value of this field
+     * @return mixed the default value of this field
      */
-    public function defaultValue()
+    public function defaultValue(): mixed
     {
         return $this->default;
     }
 
     /**
-     * @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 150f179d7f..c432c8511d 100644
--- a/lang/php/lib/Schema/AvroLogicalType.php
+++ b/lang/php/lib/Schema/AvroLogicalType.php
@@ -22,13 +22,22 @@ namespace Apache\Avro\Schema;
 
 use Apache\Avro\AvroException;
 
+/**
+ * @phpstan-import-type AvroSchemaDefinitionArray from AvroSchema
+ * @phpstan-type AvroLogicalTypeAttributes array<string, int>
+ */
 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 @@ class AvroLogicalType
         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 32e9e7ba86..c8f6f76f42 100644
--- a/lang/php/lib/Schema/AvroMapSchema.php
+++ b/lang/php/lib/Schema/AvroMapSchema.php
@@ -23,6 +23,8 @@ namespace Apache\Avro\Schema;
 /**
  * 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 a7869b998c..8b8f48b6a8 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 @@ class AvroName implements \Stringable
             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 @@ class AvroName implements \Stringable
     }
 
     /**
-     * @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 @@ class AvroName implements \Stringable
         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 @@ class AvroName implements \Stringable
     /**
      * @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 4064fbeff8..b34e90f30c 100644
--- a/lang/php/lib/Schema/AvroNamedSchema.php
+++ b/lang/php/lib/Schema/AvroNamedSchema.php
@@ -22,10 +22,12 @@ namespace Apache\Avro\Schema;
 
 /**
  * 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 @@ class AvroNamedSchema extends AvroSchema implements 
AvroAliasedSchema
         }
     }
 
+    /**
+     * @return null|AvroAliases
+     */
     public function getAliases(): ?array
     {
         return $this->aliases;
     }
 
+    /**
+     * @return array<string, mixed>|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 78912d0ffe..643a1876e5 100644
--- a/lang/php/lib/Schema/AvroNamedSchemata.php
+++ b/lang/php/lib/Schema/AvroNamedSchemata.php
@@ -93,7 +93,7 @@ class AvroNamedSchemata
     }
 
     /**
-     * @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 c2337a17e2..5987ee90a4 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<string, mixed> $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,23 +55,25 @@ class AvroRecordSchema extends AvroNamedSchema
             );
         }
 
-        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();
+        [, $namespace] = $name->nameAndNamespace();
         $this->fields = self::parseFields($fields, $namespace, $schemata);
     }
 
     /**
-     * @param null|string $default_namespace namespace of enclosing schema
+     * @param array<string, mixed> $fieldsDefinitions
+     * @param null|string $defaultNamespace namespace of enclosing schema
      * @throws AvroSchemaParseException
+     * @return array<int, AvroField>
      */
     public static function parseFields(
         array $fieldsDefinitions,
-        ?string $default_namespace,
+        ?string $defaultNamespace,
         AvroNamedSchemata $schemata
     ): array {
         $fields = [];
@@ -78,7 +88,7 @@ class AvroRecordSchema extends AvroNamedSchema
                 );
             }
 
-            $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 @@ class AvroRecordSchema extends AvroNamedSchema
         return $fields;
     }
 
+    /**
+     * @return array<string, mixed>|list<array<string, mixed>>|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<int, AvroField> the schema definitions of the fields of 
this AvroRecordSchema
      */
     public function fields(): array
     {
@@ -120,7 +133,7 @@ class AvroRecordSchema extends AvroNamedSchema
     }
 
     /**
-     * @return array a hash table of the fields of this AvroRecordSchema fields
+     * @return array<string, AvroField> a hash table of the fields of this 
AvroRecordSchema fields
      *          keyed by each field's name
      */
     public function fieldsHash(): array
@@ -136,6 +149,9 @@ class AvroRecordSchema extends AvroNamedSchema
         return $this->fieldsHash;
     }
 
+    /**
+     * @return array<string, AvroField>
+     */
     public function fieldsByAlias(): array
     {
         $hash = [];
diff --git a/lang/php/lib/Schema/AvroSchema.php 
b/lang/php/lib/Schema/AvroSchema.php
index 22cc8a5f30..9471acd56d 100644
--- a/lang/php/lib/Schema/AvroSchema.php
+++ b/lang/php/lib/Schema/AvroSchema.php
@@ -48,6 +48,12 @@ use Apache\Avro\Datum\Type\AvroDuration;
  *    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<string, mixed>
+ * @phpstan-type AvroSchemaUnionDefinitionArray list<AvroSchemaDefinitionArray>
  */
 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<AvroPrimitiveType> 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<string> 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<string> 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 @@ class AvroSchema implements \Stringable
     /**
      * @uses self::realParse()
      */
-    public static function parse(string $json): AvroSchema
+    public static function parse(string $json): self
     {
         $schemata = new AvroNamedSchemata();
 
@@ -333,15 +341,15 @@ class AvroSchema implements \Stringable
     }
 
     /**
-     * @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 @@ class AvroSchema implements \Stringable
             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 @@ class AvroSchema implements \Stringable
                             doc: $doc,
                             fields: $fields,
                             schemata: $schemata,
-                            schema_type: $type,
+                            schemaType: $type,
                             aliases: $aliases
                         );
                     default:
@@ -451,12 +459,12 @@ class AvroSchema implements \Stringable
                 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 @@ class AvroSchema implements \Stringable
                 !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 @@ class AvroSchema implements \Stringable
 
     /**
      * @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 @@ class AvroSchema implements \Stringable
 
     /**
      * @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 @@ class AvroSchema implements \Stringable
         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 @@ class AvroSchema implements \Stringable
     }
 
     /**
-     * @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 @@ class AvroSchema implements \Stringable
             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 @@ class AvroSchema implements \Stringable
     }
 
     /**
-     * @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 @@ class AvroSchema implements \Stringable
         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 @@ class AvroSchema implements \Stringable
     }
 
     /**
-     * @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 @@ class AvroSchema implements \Stringable
     }
 
     /**
+     * @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 @@ class AvroSchema implements \Stringable
     }
 
     /**
+     * @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 8220fb6463..c6b6cbf279 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<int, AvroSchema> list of schemas of this union
      */
     private array $schemas;
 
     /**
-     * @param AvroSchema[] $schemas list of schemas in the union
+     * @param array<int, AvroSchema|string> $schemas list of schemas in the 
union
      * @param null|string $defaultNamespace namespace of enclosing schema
      * @throws AvroSchemaParseException
      */
@@ -46,26 +46,26 @@ class AvroUnionSchema extends AvroSchema
         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 @@ class AvroUnionSchema extends AvroSchema
                 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<int, AvroSchema>
      */
     public function schemas(): array
     {
@@ -105,6 +105,9 @@ class AvroUnionSchema extends AvroSchema
         throw new AvroSchemaParseException('Invalid union schema index');
     }
 
+    /**
+     * @return array<int, mixed>|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 0000000000..021062d1d0
--- /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 1eae92a3e4..c1d1b6b807 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 dd8d77b337..7810c593a7 100644
--- a/lang/php/test/DataFileTest.php
+++ b/lang/php/test/DataFileTest.php
@@ -397,7 +397,8 @@ class DataFileTest extends TestCase
 
     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 717ee78a24..8614cf6136 100644
--- a/lang/php/test/DatumIOTest.php
+++ b/lang/php/test/DatumIOTest.php
@@ -416,10 +416,7 @@ class DatumIOTest extends TestCase
         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 060c6b93a5..e9ab5cb641 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 @@ class InterOpTest extends TestCase
     {
         $dr = AvroDataIO::openFile(
             $file_name,
-            AvroFile::READ_MODE,
-            $this->projection_json
+            AvroIO::READ_MODE,
+            $this->projectionJson
         );
 
         $data = $dr->data();


Reply via email to