| @@ -120,8 +120,12 @@ | ||
| 120 | 120 | * @return $this |
| 121 | 121 | */ |
| 122 | 122 | public function from(...$addresses) |
| 123 | 123 | { |
| 124 | + if (!$addresses) { | |
| 125 | + throw new LogicException('"from()" must be called with at least one address.'); | |
| 126 | + } | |
| 127 | + | |
| 124 | 128 | return $this->setListAddressHeaderBody('From', $addresses); |
| 125 | 129 | } |
| 126 | 130 | |
| 127 | 131 | /** |
| @@ -341,9 +345,9 @@ | ||
| 341 | 345 | * @param resource|string $body |
| 342 | 346 | * |
| 343 | 347 | * @return $this |
| 344 | 348 | */ |
| 345 | - public function attach($body, string $name = null, string $contentType = null) | |
| 349 | + public function attach($body, ?string $name = null, ?string $contentType = null) | |
| 346 | 350 | { |
| 347 | 351 | if (!\is_string($body) && !\is_resource($body)) { |
| 348 | 352 | throw new \TypeError(sprintf('The body must be a string or a resource (got "%s").', get_debug_type($body))); |
| 349 | 353 | } |
| @@ -356,9 +360,9 @@ | ||
| 356 | 360 | |
| 357 | 361 | /** |
| 358 | 362 | * @return $this |
| 359 | 363 | */ |
| 360 | - public function attachFromPath(string $path, string $name = null, string $contentType = null) | |
| 364 | + public function attachFromPath(string $path, ?string $name = null, ?string $contentType = null) | |
| 361 | 365 | { |
| 362 | 366 | $this->cachedBody = null; |
| 363 | 367 | $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => false]; |
| 364 | 368 | |
| @@ -369,9 +373,9 @@ | ||
| 369 | 373 | * @param resource|string $body |
| 370 | 374 | * |
| 371 | 375 | * @return $this |
| 372 | 376 | */ |
| 373 | - public function embed($body, string $name = null, string $contentType = null) | |
| 377 | + public function embed($body, ?string $name = null, ?string $contentType = null) | |
| 374 | 378 | { |
| 375 | 379 | if (!\is_string($body) && !\is_resource($body)) { |
| 376 | 380 | throw new \TypeError(sprintf('The body must be a string or a resource (got "%s").', get_debug_type($body))); |
| 377 | 381 | } |
| @@ -384,9 +388,9 @@ | ||
| 384 | 388 | |
| 385 | 389 | /** |
| 386 | 390 | * @return $this |
| 387 | 391 | */ |
| 388 | - public function embedFromPath(string $path, string $name = null, string $contentType = null) | |
| 392 | + public function embedFromPath(string $path, ?string $name = null, ?string $contentType = null) | |
| 389 | 393 | { |
| 390 | 394 | $this->cachedBody = null; |
| 391 | 395 | $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => true]; |
| 392 | 396 | |
| @@ -462,9 +466,9 @@ | ||
| 462 | 466 | } |
| 463 | 467 | |
| 464 | 468 | $this->ensureValidity(); |
| 465 | 469 | |
| 466 | - [$htmlPart, $attachmentParts, $inlineParts] = $this->prepareParts(); | |
| 470 | + [$htmlPart, $otherParts, $relatedParts] = $this->prepareParts(); | |
| 467 | 471 | |
| 468 | 472 | $part = null === $this->text ? null : new TextPart($this->text, $this->textCharset); |
| 469 | 473 | if (null !== $htmlPart) { |
| 470 | 474 | if (null !== $part) { |
| @@ -473,17 +477,17 @@ | ||
| 473 | 477 | $part = $htmlPart; |
| 474 | 478 | } |
| 475 | 479 | } |
| 476 | 480 | |
| 477 | - if ($inlineParts) { | |
| 478 | - $part = new RelatedPart($part, ...$inlineParts); | |
| 481 | + if ($relatedParts) { | |
| 482 | + $part = new RelatedPart($part, ...$relatedParts); | |
| 479 | 483 | } |
| 480 | 484 | |
| 481 | - if ($attachmentParts) { | |
| 485 | + if ($otherParts) { | |
| 482 | 486 | if ($part) { |
| 483 | - $part = new MixedPart($part, ...$attachmentParts); | |
| 487 | + $part = new MixedPart($part, ...$otherParts); | |
| 484 | 488 | } else { |
| 485 | - $part = new MixedPart(...$attachmentParts); | |
| 489 | + $part = new MixedPart(...$otherParts); | |
| 486 | 490 | } |
| 487 | 491 | } |
| 488 | 492 | |
| 489 | 493 | return $this->cachedBody = $part; |
| @@ -496,18 +500,16 @@ | ||
| 496 | 500 | $html = $this->html; |
| 497 | 501 | if (null !== $html) { |
| 498 | 502 | $htmlPart = new TextPart($html, $this->htmlCharset, 'html'); |
| 499 | 503 | $html = $htmlPart->getBody(); |
| 500 | - preg_match_all('(<img\s+[^>]*src\s*=\s*(?:([\'"])cid:([^"]+)\\1|cid:([^>\s]+)))i', $html, $names); | |
| 504 | + preg_match_all('(<img\s+[^>]*src\s*=\s*(?:([\'"])cid:(.+?)\\1|cid:([^>\s]+)))i', $html, $names); | |
| 501 | 505 | $names = array_filter(array_unique(array_merge($names[2], $names[3]))); |
| 502 | 506 | } |
| 503 | 507 | |
| 504 | 508 | // usage of reflection is a temporary workaround for missing getters that will be added in 6.2 |
| 505 | - $dispositionRef = new \ReflectionProperty(TextPart::class, 'disposition'); | |
| 506 | - $dispositionRef->setAccessible(true); | |
| 507 | 509 | $nameRef = new \ReflectionProperty(TextPart::class, 'name'); |
| 508 | 510 | $nameRef->setAccessible(true); |
| 509 | - $attachmentParts = $inlineParts = []; | |
| 511 | + $otherParts = $relatedParts = []; | |
| 510 | 512 | foreach ($this->attachments as $attachment) { |
| 511 | 513 | $part = $this->createDataPart($attachment); |
| 512 | 514 | if (isset($attachment['part'])) { |
| 513 | 515 | $attachment['name'] = $nameRef->getValue($part); |
| @@ -512,26 +514,30 @@ | ||
| 512 | 514 | if (isset($attachment['part'])) { |
| 513 | 515 | $attachment['name'] = $nameRef->getValue($part); |
| 514 | 516 | } |
| 515 | 517 | |
| 518 | + $related = false; | |
| 516 | 519 | foreach ($names as $name) { |
| 517 | 520 | if ($name !== $attachment['name']) { |
| 518 | 521 | continue; |
| 519 | 522 | } |
| 520 | - if (isset($inlineParts[$name])) { | |
| 523 | + if (isset($relatedParts[$name])) { | |
| 521 | 524 | continue 2; |
| 522 | 525 | } |
| 523 | 526 | $part->setDisposition('inline'); |
| 524 | - $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html); | |
| 527 | + $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html, $count); | |
| 528 | + if ($count) { | |
| 529 | + $related = true; | |
| 530 | + } | |
| 525 | 531 | $part->setName($part->getContentId()); |
| 526 | 532 | |
| 527 | 533 | break; |
| 528 | 534 | } |
| 529 | 535 | |
| 530 | - if ('inline' === $dispositionRef->getValue($part)) { | |
| 531 | - $inlineParts[$attachment['name']] = $part; | |
| 536 | + if ($related) { | |
| 537 | + $relatedParts[$attachment['name']] = $part; | |
| 532 | 538 | } else { |
| 533 | - $attachmentParts[] = $part; | |
| 539 | + $otherParts[] = $part; | |
| 534 | 540 | } |
| 535 | 541 | } |
| 536 | 542 | if (null !== $htmlPart) { |
| 537 | 543 | $htmlPart = new TextPart($html, $this->htmlCharset, 'html'); |
| @@ -536,9 +542,9 @@ | ||
| 536 | 542 | if (null !== $htmlPart) { |
| 537 | 543 | $htmlPart = new TextPart($html, $this->htmlCharset, 'html'); |
| 538 | 544 | } |
| 539 | 545 | |
| 540 | - return [$htmlPart, $attachmentParts, array_values($inlineParts)]; | |
| 546 | + return [$htmlPart, $otherParts, array_values($relatedParts)]; | |
| 541 | 547 | } |
| 542 | 548 | |
| 543 | 549 | private function createDataPart(array $attachment): DataPart |
| 544 | 550 | { |