diff --git a/src/BodyStructurePart.php b/src/BodyStructurePart.php index 4e53d01..ba7e660 100644 --- a/src/BodyStructurePart.php +++ b/src/BodyStructurePart.php @@ -43,18 +43,46 @@ protected static function parse(array $tokens, string $partNumber): static { return new static( partNumber: $partNumber, - type: isset($tokens[0]) ? strtolower($tokens[0]->value) : 'text', - subtype: isset($tokens[1]) ? strtolower($tokens[1]->value) : 'plain', + type: strtolower(static::tokenValueAt($tokens, 0) ?? 'text'), + subtype: strtolower(static::tokenValueAt($tokens, 1) ?? 'plain'), parameters: isset($tokens[2]) && $tokens[2] instanceof ListData ? $tokens[2]->toKeyValuePairs() : [], - id: isset($tokens[3]) && ! $tokens[3] instanceof Nil ? $tokens[3]->value : null, - description: isset($tokens[4]) && ! $tokens[4] instanceof Nil ? $tokens[4]->value : null, - encoding: isset($tokens[5]) && ! $tokens[5] instanceof Nil ? $tokens[5]->value : null, - size: isset($tokens[6]) && ! $tokens[6] instanceof Nil ? (int) $tokens[6]->value : null, - lines: isset($tokens[7]) && ! $tokens[7] instanceof Nil ? (int) $tokens[7]->value : null, + id: static::tokenValueAt($tokens, 3), + description: static::tokenValueAt($tokens, 4), + encoding: static::tokenValueAt($tokens, 5), + size: static::tokenIntValueAt($tokens, 6), + lines: static::tokenIntValueAt($tokens, 7), disposition: ContentDisposition::parse($tokens), ); } + /** + * Safely read a scalar token value from the parsed body structure. + * + * @param array $tokens + */ + protected static function tokenValueAt(array $tokens, int $index): ?string + { + $token = $tokens[$index] ?? null; + + if (! $token instanceof Token || $token instanceof Nil) { + return null; + } + + return $token->value; + } + + /** + * Safely read an integer token value from the parsed body structure. + * + * @param array $tokens + */ + protected static function tokenIntValueAt(array $tokens, int $index): ?int + { + $value = static::tokenValueAt($tokens, $index); + + return $value === null ? null : (int) $value; + } + /** * Get the part number (e.g., "1", "1.2", "2.1.3"). */ diff --git a/tests/Unit/BodyStructureTest.php b/tests/Unit/BodyStructureTest.php index b65baf8..efc9549 100644 --- a/tests/Unit/BodyStructureTest.php +++ b/tests/Unit/BodyStructureTest.php @@ -152,6 +152,26 @@ function parseBodyStructureResponse(string $response): ListData expect($imagePart->id())->toBe(''); }); +test('it does not emit warnings when non-scalar tokens appear in message/rfc822 structure fields', function () { + $listData = parseBodyStructureResponse( + '* 1 FETCH (BODYSTRUCTURE ("MESSAGE" "RFC822" NIL NIL NIL "7BIT" 3456 (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) ("TEXT" "PLAIN" ("charset" "utf-8") NIL NIL "7BIT" 100 10 NIL NIL NIL) 42) UID 1)' + ); + + set_error_handler(static function (int $severity, string $message, string $file, int $line): never { + throw new ErrorException($message, 0, $severity, $file, $line); + }); + + try { + $part = BodyStructurePart::fromListData($listData); + } finally { + restore_error_handler(); + } + + expect($part->contentType())->toBe('message/rfc822'); + expect($part->size())->toBe(3456); + expect($part->lines())->toBeNull(); +}); + test('it makes BodyStructureCollection countable', function () { $listData = parseBodyStructureResponse( '* 1 FETCH (BODYSTRUCTURE (("text" "plain" ("charset" "utf-8") NIL NIL "7bit" 100 5 NIL NIL NIL) ("text" "html" ("charset" "utf-8") NIL NIL "7bit" 200 10 NIL NIL NIL) "alternative" ("boundary" "abc") NIL NIL) UID 1)'