Headers.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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\Header;
  11. use Symfony\Component\Mime\Address;
  12. use Symfony\Component\Mime\Exception\LogicException;
  13. /**
  14. * A collection of headers.
  15. *
  16. * @author Fabien Potencier <fabien@symfony.com>
  17. */
  18. final class Headers
  19. {
  20. private const UNIQUE_HEADERS = [
  21. 'date', 'from', 'sender', 'reply-to', 'to', 'cc', 'bcc',
  22. 'message-id', 'in-reply-to', 'references', 'subject',
  23. ];
  24. private $headers = [];
  25. private $lineLength = 76;
  26. public function __construct(HeaderInterface ...$headers)
  27. {
  28. foreach ($headers as $header) {
  29. $this->add($header);
  30. }
  31. }
  32. public function __clone()
  33. {
  34. foreach ($this->headers as $name => $collection) {
  35. foreach ($collection as $i => $header) {
  36. $this->headers[$name][$i] = clone $header;
  37. }
  38. }
  39. }
  40. public function setMaxLineLength(int $lineLength)
  41. {
  42. $this->lineLength = $lineLength;
  43. foreach ($this->all() as $header) {
  44. $header->setMaxLineLength($lineLength);
  45. }
  46. }
  47. public function getMaxLineLength(): int
  48. {
  49. return $this->lineLength;
  50. }
  51. /**
  52. * @param array<Address|string> $addresses
  53. *
  54. * @return $this
  55. */
  56. public function addMailboxListHeader(string $name, array $addresses): self
  57. {
  58. return $this->add(new MailboxListHeader($name, Address::createArray($addresses)));
  59. }
  60. /**
  61. * @param Address|string $address
  62. *
  63. * @return $this
  64. */
  65. public function addMailboxHeader(string $name, $address): self
  66. {
  67. return $this->add(new MailboxHeader($name, Address::create($address)));
  68. }
  69. /**
  70. * @param string|array $ids
  71. *
  72. * @return $this
  73. */
  74. public function addIdHeader(string $name, $ids): self
  75. {
  76. return $this->add(new IdentificationHeader($name, $ids));
  77. }
  78. /**
  79. * @param Address|string $path
  80. *
  81. * @return $this
  82. */
  83. public function addPathHeader(string $name, $path): self
  84. {
  85. return $this->add(new PathHeader($name, $path instanceof Address ? $path : new Address($path)));
  86. }
  87. /**
  88. * @return $this
  89. */
  90. public function addDateHeader(string $name, \DateTimeInterface $dateTime): self
  91. {
  92. return $this->add(new DateHeader($name, $dateTime));
  93. }
  94. /**
  95. * @return $this
  96. */
  97. public function addTextHeader(string $name, string $value): self
  98. {
  99. return $this->add(new UnstructuredHeader($name, $value));
  100. }
  101. /**
  102. * @return $this
  103. */
  104. public function addParameterizedHeader(string $name, string $value, array $params = []): self
  105. {
  106. return $this->add(new ParameterizedHeader($name, $value, $params));
  107. }
  108. public function has(string $name): bool
  109. {
  110. return isset($this->headers[strtolower($name)]);
  111. }
  112. /**
  113. * @return $this
  114. */
  115. public function add(HeaderInterface $header): self
  116. {
  117. static $map = [
  118. 'date' => DateHeader::class,
  119. 'from' => MailboxListHeader::class,
  120. 'sender' => MailboxHeader::class,
  121. 'reply-to' => MailboxListHeader::class,
  122. 'to' => MailboxListHeader::class,
  123. 'cc' => MailboxListHeader::class,
  124. 'bcc' => MailboxListHeader::class,
  125. 'message-id' => IdentificationHeader::class,
  126. 'in-reply-to' => UnstructuredHeader::class, // `In-Reply-To` and `References` are less strict than RFC 2822 (3.6.4) to allow users entering the original email's ...
  127. 'references' => UnstructuredHeader::class, // ... `Message-ID`, even if that is no valid `msg-id`
  128. 'return-path' => PathHeader::class,
  129. ];
  130. $header->setMaxLineLength($this->lineLength);
  131. $name = strtolower($header->getName());
  132. if (isset($map[$name]) && !$header instanceof $map[$name]) {
  133. throw new LogicException(sprintf('The "%s" header must be an instance of "%s" (got "%s").', $header->getName(), $map[$name], \get_class($header)));
  134. }
  135. if (\in_array($name, self::UNIQUE_HEADERS, true) && isset($this->headers[$name]) && \count($this->headers[$name]) > 0) {
  136. throw new LogicException(sprintf('Impossible to set header "%s" as it\'s already defined and must be unique.', $header->getName()));
  137. }
  138. $this->headers[$name][] = $header;
  139. return $this;
  140. }
  141. public function get(string $name): ?HeaderInterface
  142. {
  143. $name = strtolower($name);
  144. if (!isset($this->headers[$name])) {
  145. return null;
  146. }
  147. $values = array_values($this->headers[$name]);
  148. return array_shift($values);
  149. }
  150. public function all(string $name = null): iterable
  151. {
  152. if (null === $name) {
  153. foreach ($this->headers as $name => $collection) {
  154. foreach ($collection as $header) {
  155. yield $name => $header;
  156. }
  157. }
  158. } elseif (isset($this->headers[strtolower($name)])) {
  159. foreach ($this->headers[strtolower($name)] as $header) {
  160. yield $header;
  161. }
  162. }
  163. }
  164. public function getNames(): array
  165. {
  166. return array_keys($this->headers);
  167. }
  168. public function remove(string $name): void
  169. {
  170. unset($this->headers[strtolower($name)]);
  171. }
  172. public static function isUniqueHeader(string $name): bool
  173. {
  174. return \in_array(strtolower($name), self::UNIQUE_HEADERS, true);
  175. }
  176. public function toString(): string
  177. {
  178. $string = '';
  179. foreach ($this->toArray() as $str) {
  180. $string .= $str."\r\n";
  181. }
  182. return $string;
  183. }
  184. public function toArray(): array
  185. {
  186. $arr = [];
  187. foreach ($this->all() as $header) {
  188. if ('' !== $header->getBodyAsString()) {
  189. $arr[] = $header->toString();
  190. }
  191. }
  192. return $arr;
  193. }
  194. /**
  195. * @internal
  196. */
  197. public function getHeaderBody(string $name)
  198. {
  199. return $this->has($name) ? $this->get($name)->getBody() : null;
  200. }
  201. /**
  202. * @internal
  203. */
  204. public function setHeaderBody(string $type, string $name, $body): void
  205. {
  206. if ($this->has($name)) {
  207. $this->get($name)->setBody($body);
  208. } else {
  209. $this->{'add'.$type.'Header'}($name, $body);
  210. }
  211. }
  212. /**
  213. * @internal
  214. */
  215. public function getHeaderParameter(string $name, string $parameter): ?string
  216. {
  217. if (!$this->has($name)) {
  218. return null;
  219. }
  220. $header = $this->get($name);
  221. if (!$header instanceof ParameterizedHeader) {
  222. throw new LogicException(sprintf('Unable to get parameter "%s" on header "%s" as the header is not of class "%s".', $parameter, $name, ParameterizedHeader::class));
  223. }
  224. return $header->getParameter($parameter);
  225. }
  226. /**
  227. * @internal
  228. */
  229. public function setHeaderParameter(string $name, string $parameter, ?string $value): void
  230. {
  231. if (!$this->has($name)) {
  232. throw new LogicException(sprintf('Unable to set parameter "%s" on header "%s" as the header is not defined.', $parameter, $name));
  233. }
  234. $header = $this->get($name);
  235. if (!$header instanceof ParameterizedHeader) {
  236. throw new LogicException(sprintf('Unable to set parameter "%s" on header "%s" as the header is not of class "%s".', $parameter, $name, ParameterizedHeader::class));
  237. }
  238. $header->setParameter($parameter, $value);
  239. }
  240. }