Skip to content

Commit bf73205

Browse files
authored
Merge pull request #33 from DirectoryTree/attachment-content-id
Add ability to retrieve attachment Content-ID (cid)
2 parents f54ec70 + 5ae711e commit bf73205

File tree

4 files changed

+128
-78
lines changed

4 files changed

+128
-78
lines changed

src/Attachment.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class Attachment implements Arrayable, JsonSerializable
1414
*/
1515
public function __construct(
1616
protected ?string $filename,
17+
protected ?string $contentId,
1718
protected string $contentType,
1819
protected StreamInterface $contentStream,
1920
) {}
@@ -26,6 +27,14 @@ public function filename(): ?string
2627
return $this->filename;
2728
}
2829

30+
/**
31+
* Get the attachment's content ID.
32+
*/
33+
public function contentId(): ?string
34+
{
35+
return $this->contentId;
36+
}
37+
2938
/**
3039
* Get the attachment's content type.
3140
*/

src/HasParsedMessage.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public function attachments(): array
121121
return array_map(function (MimePart $part) {
122122
return new Attachment(
123123
$part->getFilename(),
124+
$part->getContentId(),
124125
$part->getContentType(),
125126
$part->getContentStream() ?? Utils::streamFor(''),
126127
);

tests/Unit/AttachmentTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
test('extension', function () {
77
$stream = new LazyOpenStream('test.jpg', 'r');
88

9-
$ext = (new Attachment('test.jpg', 'image/jpeg', $stream))->extension();
9+
$ext = (new Attachment('test.jpg', null, 'image/jpeg', $stream))->extension();
1010

1111
expect($ext)->toBe('jpg');
1212
});
1313

1414
test('extension with content type', function () {
1515
$stream = new LazyOpenStream('test', 'r');
1616

17-
$ext = (new Attachment('test', 'image/jpeg', $stream))->extension();
17+
$ext = (new Attachment('test', null, 'image/jpeg', $stream))->extension();
1818

1919
expect($ext)->toBe('jpg');
2020
});

tests/Unit/FileMessageTest.php

Lines changed: 116 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@
1212

1313
test('it can parse a standard EML message and read basic headers', function () {
1414
$contents = <<<'EOT'
15-
From: "John Doe" <[email protected]>
16-
To: "Jane Roe" <[email protected]>
17-
Subject: Test Subject
18-
Date: Wed, 19 Feb 2025 12:34:56 -0500
19-
Message-ID: <[email protected]>
20-
MIME-Version: 1.0
21-
Content-Type: text/plain; charset="UTF-8"
22-
23-
Hello World
24-
EOT;
15+
From: "John Doe" <[email protected]>
16+
To: "Jane Roe" <[email protected]>
17+
Subject: Test Subject
18+
Date: Wed, 19 Feb 2025 12:34:56 -0500
19+
Message-ID: <[email protected]>
20+
MIME-Version: 1.0
21+
Content-Type: text/plain; charset="UTF-8"
22+
23+
Hello World
24+
EOT;
2525

2626
$message = new FileMessage($contents);
2727

@@ -39,26 +39,26 @@
3939

4040
test('it can parse HTML content in a multipart message', function () {
4141
$contents = <<<'EOT'
42-
From: "John Doe" <[email protected]>
43-
To: "Jane Roe" <[email protected]>
44-
Subject: HTML Email
45-
Date: Wed, 19 Feb 2025 12:34:56 -0500
46-
Message-ID: <[email protected]>
47-
MIME-Version: 1.0
48-
Content-Type: multipart/alternative;
49-
boundary="----BOUNDARY-ID----"
50-
51-
------BOUNDARY-ID----
52-
Content-Type: text/plain; charset="UTF-8"
53-
54-
Hello Plain
55-
56-
------BOUNDARY-ID----
57-
Content-Type: text/html; charset="UTF-8"
58-
59-
<html><body><p>Hello <strong>HTML</strong></p></body></html>
60-
------BOUNDARY-ID------
61-
EOT;
42+
From: "John Doe" <[email protected]>
43+
To: "Jane Roe" <[email protected]>
44+
Subject: HTML Email
45+
Date: Wed, 19 Feb 2025 12:34:56 -0500
46+
Message-ID: <[email protected]>
47+
MIME-Version: 1.0
48+
Content-Type: multipart/alternative;
49+
boundary="----BOUNDARY-ID----"
50+
51+
------BOUNDARY-ID----
52+
Content-Type: text/plain; charset="UTF-8"
53+
54+
Hello Plain
55+
56+
------BOUNDARY-ID----
57+
Content-Type: text/html; charset="UTF-8"
58+
59+
<html><body><p>Hello <strong>HTML</strong></p></body></html>
60+
------BOUNDARY-ID------
61+
EOT;
6262

6363
$message = new FileMessage($contents);
6464

@@ -69,29 +69,29 @@
6969
test('it can parse attachments', function () {
7070
// Simple example with one attachment
7171
$contents = <<<'EOT'
72-
From: "John Doe" <[email protected]>
73-
To: "Jane Roe" <[email protected]>
74-
Subject: EML with Attachment
75-
Date: Wed, 19 Feb 2025 12:34:56 -0500
76-
Message-ID: <[email protected]>
77-
MIME-Version: 1.0
78-
Content-Type: multipart/mixed;
79-
boundary="----BOUNDARY-ID----"
80-
81-
------BOUNDARY-ID----
82-
Content-Type: text/plain; charset="UTF-8"
83-
84-
Hello with Attachment
85-
86-
------BOUNDARY-ID----
87-
Content-Type: application/pdf; name="file.pdf"
88-
Content-Disposition: attachment; filename="file.pdf"
89-
Content-Transfer-Encoding: base64
90-
91-
JVBERi0xLjUKJeLjz9MKMyAwIG9iago8PC9MZW5ndGggNCAgIC9GaWx0ZXIvQXNjaWlIYXgg
92-
ICAgPj5zdHJlYW0Kc3R1ZmYKZW5kc3RyZWFtCmVuZG9iajAK
93-
------BOUNDARY-ID------
94-
EOT;
72+
From: "John Doe" <[email protected]>
73+
To: "Jane Roe" <[email protected]>
74+
Subject: EML with Attachment
75+
Date: Wed, 19 Feb 2025 12:34:56 -0500
76+
Message-ID: <[email protected]>
77+
MIME-Version: 1.0
78+
Content-Type: multipart/mixed;
79+
boundary="----BOUNDARY-ID----"
80+
81+
------BOUNDARY-ID----
82+
Content-Type: text/plain; charset="UTF-8"
83+
84+
Hello with Attachment
85+
86+
------BOUNDARY-ID----
87+
Content-Type: application/pdf; name="file.pdf"
88+
Content-Disposition: attachment; filename="file.pdf"
89+
Content-Transfer-Encoding: base64
90+
91+
JVBERi0xLjUKJeLjz9MKMyAwIG9iago8PC9MZW5ndGggNCAgIC9GaWx0ZXIvQXNjaWlIYXgg
92+
ICAgPj5zdHJlYW0Kc3R1ZmYKZW5kc3RyZWFtCmVuZG9iajAK
93+
------BOUNDARY-ID------
94+
EOT;
9595

9696
$message = new FileMessage($contents);
9797

@@ -109,14 +109,14 @@
109109

110110
test('it recognizes when there are no attachments', function () {
111111
$contents = <<<'EOT'
112-
From: "John Doe" <[email protected]>
113-
To: "Jane Roe" <[email protected]>
114-
Subject: No Attachments
115-
Date: Wed, 19 Feb 2025 12:34:56 -0500
116-
Content-Type: text/plain; charset="UTF-8"
117-
118-
Just a plain text email without attachments.
119-
EOT;
112+
From: "John Doe" <[email protected]>
113+
To: "Jane Roe" <[email protected]>
114+
Subject: No Attachments
115+
Date: Wed, 19 Feb 2025 12:34:56 -0500
116+
Content-Type: text/plain; charset="UTF-8"
117+
118+
Just a plain text email without attachments.
119+
EOT;
120120

121121
$message = new FileMessage($contents);
122122

@@ -127,14 +127,14 @@
127127

128128
test('it can parse other header fields like IN-REPLY-TO', function () {
129129
$contents = <<<'EOT'
130-
From: "John Doe" <[email protected]>
131-
132-
Subject: In-Reply-To Check
133-
Date: Wed, 19 Feb 2025 12:34:56 -0500
134-
Content-Type: text/plain; charset="UTF-8"
135-
136-
Check the in-reply-to header
137-
EOT;
130+
From: "John Doe" <[email protected]>
131+
132+
Subject: In-Reply-To Check
133+
Date: Wed, 19 Feb 2025 12:34:56 -0500
134+
Content-Type: text/plain; charset="UTF-8"
135+
136+
Check the in-reply-to header
137+
EOT;
138138

139139
$message = new FileMessage($contents);
140140

@@ -145,15 +145,55 @@
145145

146146
test('it can be cast to a string via __toString()', function () {
147147
$contents = <<<'EOT'
148-
From: "John Doe" <[email protected]>
149-
Subject: Stringable Test
150-
Date: Wed, 19 Feb 2025 12:34:56 -0500
151-
Content-Type: text/plain; charset="UTF-8"
152-
153-
Testing __toString
154-
EOT;
148+
From: "John Doe" <[email protected]>
149+
Subject: Stringable Test
150+
Date: Wed, 19 Feb 2025 12:34:56 -0500
151+
Content-Type: text/plain; charset="UTF-8"
152+
153+
Testing __toString
154+
EOT;
155155

156156
$message = new FileMessage($contents);
157157

158158
expect((string) $message)->toBe($contents);
159159
});
160+
161+
test('it can read inline attachment', function () {
162+
$contents = <<<'EOT'
163+
From: "Example Sender" <[email protected]>
164+
To: "Example Recipient" <[email protected]>
165+
Subject: Test Email With Inline Image
166+
MIME-Version: 1.0
167+
Content-Type: multipart/related; boundary="BOUNDARY_STRING"
168+
169+
--BOUNDARY_STRING
170+
Content-Type: text/html; charset=UTF-8
171+
172+
<html>
173+
<body>
174+
<p>This is a test email with an inline image:</p>
175+
<img src="cid:inline-image-id" alt="Inline Image" />
176+
</body>
177+
</html>
178+
179+
--BOUNDARY_STRING
180+
Content-Type: image/png; name="inline_image.png"
181+
Content-Transfer-Encoding: base64
182+
Content-ID: <inline-image-id>
183+
Content-Disposition: inline; filename="inline_image.png"
184+
185+
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8
186+
z8BQDwABAgEA0xzY2QAAAABJRU5ErkJggg==
187+
188+
--BOUNDARY_STRING--
189+
EOT;
190+
191+
$message = new FileMessage($contents);
192+
193+
$attachments = $message->attachments();
194+
195+
expect($attachments)->toHaveCount(1);
196+
expect($attachments[0]->contentType())->toBe('image/png');
197+
expect($attachments[0]->contentId())->toBe('inline-image-id');
198+
expect($attachments[0]->filename())->toBe('inline_image.png');
199+
});

0 commit comments

Comments
 (0)