DataPart.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Mime\Part;
  11. use Symfony\Component\Mime\Exception\InvalidArgumentException;
  12. use Symfony\Component\Mime\Header\Headers;
  13. /**
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. */
  16. class DataPart extends TextPart
  17. {
  18. /** @internal */
  19. protected array $_parent;
  20. private ?string $filename = null;
  21. private string $mediaType;
  22. private ?string $cid = null;
  23. /**
  24. * @param resource|string|File $body Use a File instance to defer loading the file until rendering
  25. */
  26. public function __construct($body, ?string $filename = null, ?string $contentType = null, ?string $encoding = null)
  27. {
  28. if ($body instanceof File && !$filename) {
  29. $filename = $body->getFilename();
  30. }
  31. $contentType ??= $body instanceof File ? $body->getContentType() : 'application/octet-stream';
  32. [$this->mediaType, $subtype] = explode('/', $contentType);
  33. parent::__construct($body, null, $subtype, $encoding);
  34. if (null !== $filename) {
  35. $this->filename = $filename;
  36. $this->setName($filename);
  37. }
  38. $this->setDisposition('attachment');
  39. }
  40. public static function fromPath(string $path, ?string $name = null, ?string $contentType = null): self
  41. {
  42. return new self(new File($path), $name, $contentType);
  43. }
  44. /**
  45. * @return $this
  46. */
  47. public function asInline(): static
  48. {
  49. return $this->setDisposition('inline');
  50. }
  51. /**
  52. * @return $this
  53. */
  54. public function setContentId(string $cid): static
  55. {
  56. if (!str_contains($cid, '@')) {
  57. throw new InvalidArgumentException(sprintf('Invalid cid "%s".', $cid));
  58. }
  59. $this->cid = $cid;
  60. return $this;
  61. }
  62. public function getContentId(): string
  63. {
  64. return $this->cid ?: $this->cid = $this->generateContentId();
  65. }
  66. public function hasContentId(): bool
  67. {
  68. return null !== $this->cid;
  69. }
  70. public function getMediaType(): string
  71. {
  72. return $this->mediaType;
  73. }
  74. public function getPreparedHeaders(): Headers
  75. {
  76. $headers = parent::getPreparedHeaders();
  77. if (null !== $this->cid) {
  78. $headers->setHeaderBody('Id', 'Content-ID', $this->cid);
  79. }
  80. if (null !== $this->filename) {
  81. $headers->setHeaderParameter('Content-Disposition', 'filename', $this->filename);
  82. }
  83. return $headers;
  84. }
  85. public function asDebugString(): string
  86. {
  87. $str = parent::asDebugString();
  88. if (null !== $this->filename) {
  89. $str .= ' filename: '.$this->filename;
  90. }
  91. return $str;
  92. }
  93. public function getFilename(): ?string
  94. {
  95. return $this->filename;
  96. }
  97. public function getContentType(): string
  98. {
  99. return implode('/', [$this->getMediaType(), $this->getMediaSubtype()]);
  100. }
  101. private function generateContentId(): string
  102. {
  103. return bin2hex(random_bytes(16)).'@symfony';
  104. }
  105. public function __sleep(): array
  106. {
  107. // converts the body to a string
  108. parent::__sleep();
  109. $this->_parent = [];
  110. foreach (['body', 'charset', 'subtype', 'disposition', 'name', 'encoding'] as $name) {
  111. $r = new \ReflectionProperty(TextPart::class, $name);
  112. $this->_parent[$name] = $r->getValue($this);
  113. }
  114. $this->_headers = $this->getHeaders();
  115. return ['_headers', '_parent', 'filename', 'mediaType'];
  116. }
  117. public function __wakeup(): void
  118. {
  119. $r = new \ReflectionProperty(AbstractPart::class, 'headers');
  120. $r->setValue($this, $this->_headers);
  121. unset($this->_headers);
  122. if (!\is_array($this->_parent)) {
  123. throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
  124. }
  125. foreach (['body', 'charset', 'subtype', 'disposition', 'name', 'encoding'] as $name) {
  126. if (null !== $this->_parent[$name] && !\is_string($this->_parent[$name]) && !$this->_parent[$name] instanceof File) {
  127. throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
  128. }
  129. $r = new \ReflectionProperty(TextPart::class, $name);
  130. $r->setValue($this, $this->_parent[$name]);
  131. }
  132. unset($this->_parent);
  133. }
  134. }