SimpleMimeEntity.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. <?php
  2. /*
  3. * This file is part of SwiftMailer.
  4. * (c) 2004-2009 Chris Corbyn
  5. *
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. /**
  10. * A MIME entity, in a multipart message.
  11. *
  12. * @author Chris Corbyn
  13. */
  14. class Swift_Mime_SimpleMimeEntity implements Swift_Mime_CharsetObserver, Swift_Mime_EncodingObserver
  15. {
  16. /** Main message document; there can only be one of these */
  17. const LEVEL_TOP = 16;
  18. /** An entity which nests with the same precedence as an attachment */
  19. const LEVEL_MIXED = 256;
  20. /** An entity which nests with the same precedence as a mime part */
  21. const LEVEL_ALTERNATIVE = 4096;
  22. /** An entity which nests with the same precedence as embedded content */
  23. const LEVEL_RELATED = 65536;
  24. /** A collection of Headers for this mime entity */
  25. private $headers;
  26. /** The body as a string, or a stream */
  27. private $body;
  28. /** The encoder that encodes the body into a streamable format */
  29. private $encoder;
  30. /** Message ID generator */
  31. private $idGenerator;
  32. /** A mime boundary, if any is used */
  33. private $boundary;
  34. /** Mime types to be used based on the nesting level */
  35. private $compositeRanges = [
  36. 'multipart/mixed' => [self::LEVEL_TOP, self::LEVEL_MIXED],
  37. 'multipart/alternative' => [self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE],
  38. 'multipart/related' => [self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED],
  39. ];
  40. /** A set of filter rules to define what level an entity should be nested at */
  41. private $compoundLevelFilters = [];
  42. /** The nesting level of this entity */
  43. private $nestingLevel = self::LEVEL_ALTERNATIVE;
  44. /** A KeyCache instance used during encoding and streaming */
  45. private $cache;
  46. /** Direct descendants of this entity */
  47. private $immediateChildren = [];
  48. /** All descendants of this entity */
  49. private $children = [];
  50. /** The maximum line length of the body of this entity */
  51. private $maxLineLength = 78;
  52. /** The order in which alternative mime types should appear */
  53. private $alternativePartOrder = [
  54. 'text/plain' => 1,
  55. 'text/html' => 2,
  56. 'multipart/related' => 3,
  57. ];
  58. /** The CID of this entity */
  59. private $id;
  60. /** The key used for accessing the cache */
  61. private $cacheKey;
  62. protected $userContentType;
  63. /**
  64. * Create a new SimpleMimeEntity with $headers, $encoder and $cache.
  65. */
  66. public function __construct(Swift_Mime_SimpleHeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_IdGenerator $idGenerator)
  67. {
  68. $this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values
  69. $this->cache = $cache;
  70. $this->headers = $headers;
  71. $this->idGenerator = $idGenerator;
  72. $this->setEncoder($encoder);
  73. $this->headers->defineOrdering(['Content-Type', 'Content-Transfer-Encoding']);
  74. // This array specifies that, when the entire MIME document contains
  75. // $compoundLevel, then for each child within $level, if its Content-Type
  76. // is $contentType then it should be treated as if it's level is
  77. // $neededLevel instead. I tried to write that unambiguously! :-\
  78. // Data Structure:
  79. // array (
  80. // $compoundLevel => array(
  81. // $level => array(
  82. // $contentType => $neededLevel
  83. // )
  84. // )
  85. // )
  86. $this->compoundLevelFilters = [
  87. (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => [
  88. self::LEVEL_ALTERNATIVE => [
  89. 'text/plain' => self::LEVEL_ALTERNATIVE,
  90. 'text/html' => self::LEVEL_RELATED,
  91. ],
  92. ],
  93. ];
  94. $this->id = $this->idGenerator->generateId();
  95. }
  96. /**
  97. * Generate a new Content-ID or Message-ID for this MIME entity.
  98. *
  99. * @return string
  100. */
  101. public function generateId()
  102. {
  103. $this->setId($this->idGenerator->generateId());
  104. return $this->id;
  105. }
  106. /**
  107. * Get the {@link Swift_Mime_SimpleHeaderSet} for this entity.
  108. *
  109. * @return Swift_Mime_SimpleHeaderSet
  110. */
  111. public function getHeaders()
  112. {
  113. return $this->headers;
  114. }
  115. /**
  116. * Get the nesting level of this entity.
  117. *
  118. * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE
  119. *
  120. * @return int
  121. */
  122. public function getNestingLevel()
  123. {
  124. return $this->nestingLevel;
  125. }
  126. /**
  127. * Get the Content-type of this entity.
  128. *
  129. * @return string
  130. */
  131. public function getContentType()
  132. {
  133. return $this->getHeaderFieldModel('Content-Type');
  134. }
  135. /**
  136. * Get the Body Content-type of this entity.
  137. *
  138. * @return string
  139. */
  140. public function getBodyContentType()
  141. {
  142. return $this->userContentType;
  143. }
  144. /**
  145. * Set the Content-type of this entity.
  146. *
  147. * @param string $type
  148. *
  149. * @return $this
  150. */
  151. public function setContentType($type)
  152. {
  153. $this->setContentTypeInHeaders($type);
  154. // Keep track of the value so that if the content-type changes automatically
  155. // due to added child entities, it can be restored if they are later removed
  156. $this->userContentType = $type;
  157. return $this;
  158. }
  159. /**
  160. * Get the CID of this entity.
  161. *
  162. * The CID will only be present in headers if a Content-ID header is present.
  163. *
  164. * @return string
  165. */
  166. public function getId()
  167. {
  168. $tmp = (array) $this->getHeaderFieldModel($this->getIdField());
  169. return $this->headers->has($this->getIdField()) ? current($tmp) : $this->id;
  170. }
  171. /**
  172. * Set the CID of this entity.
  173. *
  174. * @param string $id
  175. *
  176. * @return $this
  177. */
  178. public function setId($id)
  179. {
  180. if (!$this->setHeaderFieldModel($this->getIdField(), $id)) {
  181. $this->headers->addIdHeader($this->getIdField(), $id);
  182. }
  183. $this->id = $id;
  184. return $this;
  185. }
  186. /**
  187. * Get the description of this entity.
  188. *
  189. * This value comes from the Content-Description header if set.
  190. *
  191. * @return string
  192. */
  193. public function getDescription()
  194. {
  195. return $this->getHeaderFieldModel('Content-Description');
  196. }
  197. /**
  198. * Set the description of this entity.
  199. *
  200. * This method sets a value in the Content-ID header.
  201. *
  202. * @param string $description
  203. *
  204. * @return $this
  205. */
  206. public function setDescription($description)
  207. {
  208. if (!$this->setHeaderFieldModel('Content-Description', $description)) {
  209. $this->headers->addTextHeader('Content-Description', $description);
  210. }
  211. return $this;
  212. }
  213. /**
  214. * Get the maximum line length of the body of this entity.
  215. *
  216. * @return int
  217. */
  218. public function getMaxLineLength()
  219. {
  220. return $this->maxLineLength;
  221. }
  222. /**
  223. * Set the maximum line length of lines in this body.
  224. *
  225. * Though not enforced by the library, lines should not exceed 1000 chars.
  226. *
  227. * @param int $length
  228. *
  229. * @return $this
  230. */
  231. public function setMaxLineLength($length)
  232. {
  233. $this->maxLineLength = $length;
  234. return $this;
  235. }
  236. /**
  237. * Get all children added to this entity.
  238. *
  239. * @return Swift_Mime_SimpleMimeEntity[]
  240. */
  241. public function getChildren()
  242. {
  243. return $this->children;
  244. }
  245. /**
  246. * Set all children of this entity.
  247. *
  248. * @param Swift_Mime_SimpleMimeEntity[] $children
  249. * @param int $compoundLevel For internal use only
  250. *
  251. * @return $this
  252. */
  253. public function setChildren(array $children, $compoundLevel = null)
  254. {
  255. // TODO: Try to refactor this logic
  256. $compoundLevel = $compoundLevel ?? $this->getCompoundLevel($children);
  257. $immediateChildren = [];
  258. $grandchildren = [];
  259. $newContentType = $this->userContentType;
  260. foreach ($children as $child) {
  261. $level = $this->getNeededChildLevel($child, $compoundLevel);
  262. if (empty($immediateChildren)) {
  263. //first iteration
  264. $immediateChildren = [$child];
  265. } else {
  266. $nextLevel = $this->getNeededChildLevel($immediateChildren[0], $compoundLevel);
  267. if ($nextLevel == $level) {
  268. $immediateChildren[] = $child;
  269. } elseif ($level < $nextLevel) {
  270. // Re-assign immediateChildren to grandchildren
  271. $grandchildren = array_merge($grandchildren, $immediateChildren);
  272. // Set new children
  273. $immediateChildren = [$child];
  274. } else {
  275. $grandchildren[] = $child;
  276. }
  277. }
  278. }
  279. if ($immediateChildren) {
  280. $lowestLevel = $this->getNeededChildLevel($immediateChildren[0], $compoundLevel);
  281. // Determine which composite media type is needed to accommodate the
  282. // immediate children
  283. foreach ($this->compositeRanges as $mediaType => $range) {
  284. if ($lowestLevel > $range[0] && $lowestLevel <= $range[1]) {
  285. $newContentType = $mediaType;
  286. break;
  287. }
  288. }
  289. // Put any grandchildren in a subpart
  290. if (!empty($grandchildren)) {
  291. $subentity = $this->createChild();
  292. $subentity->setNestingLevel($lowestLevel);
  293. $subentity->setChildren($grandchildren, $compoundLevel);
  294. array_unshift($immediateChildren, $subentity);
  295. }
  296. }
  297. $this->immediateChildren = $immediateChildren;
  298. $this->children = $children;
  299. $this->setContentTypeInHeaders($newContentType);
  300. $this->fixHeaders();
  301. $this->sortChildren();
  302. return $this;
  303. }
  304. /**
  305. * Get the body of this entity as a string.
  306. *
  307. * @return string
  308. */
  309. public function getBody()
  310. {
  311. return $this->body instanceof Swift_OutputByteStream ? $this->readStream($this->body) : $this->body;
  312. }
  313. /**
  314. * Set the body of this entity, either as a string, or as an instance of
  315. * {@link Swift_OutputByteStream}.
  316. *
  317. * @param mixed $body
  318. * @param string $contentType optional
  319. *
  320. * @return $this
  321. */
  322. public function setBody($body, $contentType = null)
  323. {
  324. if ($body !== $this->body) {
  325. $this->clearCache();
  326. }
  327. $this->body = $body;
  328. if (null !== $contentType) {
  329. $this->setContentType($contentType);
  330. }
  331. return $this;
  332. }
  333. /**
  334. * Get the encoder used for the body of this entity.
  335. *
  336. * @return Swift_Mime_ContentEncoder
  337. */
  338. public function getEncoder()
  339. {
  340. return $this->encoder;
  341. }
  342. /**
  343. * Set the encoder used for the body of this entity.
  344. *
  345. * @return $this
  346. */
  347. public function setEncoder(Swift_Mime_ContentEncoder $encoder)
  348. {
  349. if ($encoder !== $this->encoder) {
  350. $this->clearCache();
  351. }
  352. $this->encoder = $encoder;
  353. $this->setEncoding($encoder->getName());
  354. $this->notifyEncoderChanged($encoder);
  355. return $this;
  356. }
  357. /**
  358. * Get the boundary used to separate children in this entity.
  359. *
  360. * @return string
  361. */
  362. public function getBoundary()
  363. {
  364. if (!isset($this->boundary)) {
  365. $this->boundary = '_=_swift_'.time().'_'.bin2hex(random_bytes(16)).'_=_';
  366. }
  367. return $this->boundary;
  368. }
  369. /**
  370. * Set the boundary used to separate children in this entity.
  371. *
  372. * @param string $boundary
  373. *
  374. * @throws Swift_RfcComplianceException
  375. *
  376. * @return $this
  377. */
  378. public function setBoundary($boundary)
  379. {
  380. $this->assertValidBoundary($boundary);
  381. $this->boundary = $boundary;
  382. return $this;
  383. }
  384. /**
  385. * Receive notification that the charset of this entity, or a parent entity
  386. * has changed.
  387. *
  388. * @param string $charset
  389. */
  390. public function charsetChanged($charset)
  391. {
  392. $this->notifyCharsetChanged($charset);
  393. }
  394. /**
  395. * Receive notification that the encoder of this entity or a parent entity
  396. * has changed.
  397. */
  398. public function encoderChanged(Swift_Mime_ContentEncoder $encoder)
  399. {
  400. $this->notifyEncoderChanged($encoder);
  401. }
  402. /**
  403. * Get this entire entity as a string.
  404. *
  405. * @return string
  406. */
  407. public function toString()
  408. {
  409. $string = $this->headers->toString();
  410. $string .= $this->bodyToString();
  411. return $string;
  412. }
  413. /**
  414. * Get this entire entity as a string.
  415. *
  416. * @return string
  417. */
  418. protected function bodyToString()
  419. {
  420. $string = '';
  421. if (isset($this->body) && empty($this->immediateChildren)) {
  422. if ($this->cache->hasKey($this->cacheKey, 'body')) {
  423. $body = $this->cache->getString($this->cacheKey, 'body');
  424. } else {
  425. $body = "\r\n".$this->encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength());
  426. $this->cache->setString($this->cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE);
  427. }
  428. $string .= $body;
  429. }
  430. if (!empty($this->immediateChildren)) {
  431. foreach ($this->immediateChildren as $child) {
  432. $string .= "\r\n\r\n--".$this->getBoundary()."\r\n";
  433. $string .= $child->toString();
  434. }
  435. $string .= "\r\n\r\n--".$this->getBoundary()."--\r\n";
  436. }
  437. return $string;
  438. }
  439. /**
  440. * Returns a string representation of this object.
  441. *
  442. * @see toString()
  443. *
  444. * @return string
  445. */
  446. public function __toString()
  447. {
  448. return $this->toString();
  449. }
  450. /**
  451. * Write this entire entity to a {@see Swift_InputByteStream}.
  452. */
  453. public function toByteStream(Swift_InputByteStream $is)
  454. {
  455. $is->write($this->headers->toString());
  456. $is->commit();
  457. $this->bodyToByteStream($is);
  458. }
  459. /**
  460. * Write this entire entity to a {@link Swift_InputByteStream}.
  461. */
  462. protected function bodyToByteStream(Swift_InputByteStream $is)
  463. {
  464. if (empty($this->immediateChildren)) {
  465. if (isset($this->body)) {
  466. if ($this->cache->hasKey($this->cacheKey, 'body')) {
  467. $this->cache->exportToByteStream($this->cacheKey, 'body', $is);
  468. } else {
  469. $cacheIs = $this->cache->getInputByteStream($this->cacheKey, 'body');
  470. if ($cacheIs) {
  471. $is->bind($cacheIs);
  472. }
  473. $is->write("\r\n");
  474. if ($this->body instanceof Swift_OutputByteStream) {
  475. $this->body->setReadPointer(0);
  476. $this->encoder->encodeByteStream($this->body, $is, 0, $this->getMaxLineLength());
  477. } else {
  478. $is->write($this->encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()));
  479. }
  480. if ($cacheIs) {
  481. $is->unbind($cacheIs);
  482. }
  483. }
  484. }
  485. }
  486. if (!empty($this->immediateChildren)) {
  487. foreach ($this->immediateChildren as $child) {
  488. $is->write("\r\n\r\n--".$this->getBoundary()."\r\n");
  489. $child->toByteStream($is);
  490. }
  491. $is->write("\r\n\r\n--".$this->getBoundary()."--\r\n");
  492. }
  493. }
  494. /**
  495. * Get the name of the header that provides the ID of this entity.
  496. */
  497. protected function getIdField()
  498. {
  499. return 'Content-ID';
  500. }
  501. /**
  502. * Get the model data (usually an array or a string) for $field.
  503. */
  504. protected function getHeaderFieldModel($field)
  505. {
  506. if ($this->headers->has($field)) {
  507. return $this->headers->get($field)->getFieldBodyModel();
  508. }
  509. }
  510. /**
  511. * Set the model data for $field.
  512. */
  513. protected function setHeaderFieldModel($field, $model)
  514. {
  515. if ($this->headers->has($field)) {
  516. $this->headers->get($field)->setFieldBodyModel($model);
  517. return true;
  518. }
  519. return false;
  520. }
  521. /**
  522. * Get the parameter value of $parameter on $field header.
  523. */
  524. protected function getHeaderParameter($field, $parameter)
  525. {
  526. if ($this->headers->has($field)) {
  527. return $this->headers->get($field)->getParameter($parameter);
  528. }
  529. }
  530. /**
  531. * Set the parameter value of $parameter on $field header.
  532. */
  533. protected function setHeaderParameter($field, $parameter, $value)
  534. {
  535. if ($this->headers->has($field)) {
  536. $this->headers->get($field)->setParameter($parameter, $value);
  537. return true;
  538. }
  539. return false;
  540. }
  541. /**
  542. * Re-evaluate what content type and encoding should be used on this entity.
  543. */
  544. protected function fixHeaders()
  545. {
  546. if (\count($this->immediateChildren)) {
  547. $this->setHeaderParameter('Content-Type', 'boundary',
  548. $this->getBoundary()
  549. );
  550. $this->headers->remove('Content-Transfer-Encoding');
  551. } else {
  552. $this->setHeaderParameter('Content-Type', 'boundary', null);
  553. $this->setEncoding($this->encoder->getName());
  554. }
  555. }
  556. /**
  557. * Get the KeyCache used in this entity.
  558. *
  559. * @return Swift_KeyCache
  560. */
  561. protected function getCache()
  562. {
  563. return $this->cache;
  564. }
  565. /**
  566. * Get the ID generator.
  567. *
  568. * @return Swift_IdGenerator
  569. */
  570. protected function getIdGenerator()
  571. {
  572. return $this->idGenerator;
  573. }
  574. /**
  575. * Empty the KeyCache for this entity.
  576. */
  577. protected function clearCache()
  578. {
  579. $this->cache->clearKey($this->cacheKey, 'body');
  580. }
  581. private function readStream(Swift_OutputByteStream $os)
  582. {
  583. $string = '';
  584. while (false !== $bytes = $os->read(8192)) {
  585. $string .= $bytes;
  586. }
  587. $os->setReadPointer(0);
  588. return $string;
  589. }
  590. private function setEncoding($encoding)
  591. {
  592. if (!$this->setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) {
  593. $this->headers->addTextHeader('Content-Transfer-Encoding', $encoding);
  594. }
  595. }
  596. private function assertValidBoundary($boundary)
  597. {
  598. if (!preg_match('/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', $boundary)) {
  599. throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.');
  600. }
  601. }
  602. private function setContentTypeInHeaders($type)
  603. {
  604. if (!$this->setHeaderFieldModel('Content-Type', $type)) {
  605. $this->headers->addParameterizedHeader('Content-Type', $type);
  606. }
  607. }
  608. private function setNestingLevel($level)
  609. {
  610. $this->nestingLevel = $level;
  611. }
  612. private function getCompoundLevel($children)
  613. {
  614. $level = 0;
  615. foreach ($children as $child) {
  616. $level |= $child->getNestingLevel();
  617. }
  618. return $level;
  619. }
  620. private function getNeededChildLevel($child, $compoundLevel)
  621. {
  622. $filter = [];
  623. foreach ($this->compoundLevelFilters as $bitmask => $rules) {
  624. if (($compoundLevel & $bitmask) === $bitmask) {
  625. $filter = $rules + $filter;
  626. }
  627. }
  628. $realLevel = $child->getNestingLevel();
  629. $lowercaseType = strtolower($child->getContentType() ?? '');
  630. if (isset($filter[$realLevel]) && isset($filter[$realLevel][$lowercaseType])) {
  631. return $filter[$realLevel][$lowercaseType];
  632. }
  633. return $realLevel;
  634. }
  635. private function createChild()
  636. {
  637. return new self($this->headers->newInstance(), $this->encoder, $this->cache, $this->idGenerator);
  638. }
  639. private function notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder)
  640. {
  641. foreach ($this->immediateChildren as $child) {
  642. $child->encoderChanged($encoder);
  643. }
  644. }
  645. private function notifyCharsetChanged($charset)
  646. {
  647. $this->encoder->charsetChanged($charset);
  648. $this->headers->charsetChanged($charset);
  649. foreach ($this->immediateChildren as $child) {
  650. $child->charsetChanged($charset);
  651. }
  652. }
  653. private function sortChildren()
  654. {
  655. $shouldSort = false;
  656. foreach ($this->immediateChildren as $child) {
  657. // NOTE: This include alternative parts moved into a related part
  658. if (self::LEVEL_ALTERNATIVE == $child->getNestingLevel()) {
  659. $shouldSort = true;
  660. break;
  661. }
  662. }
  663. // Sort in order of preference, if there is one
  664. if ($shouldSort) {
  665. // Group the messages by order of preference
  666. $sorted = [];
  667. foreach ($this->immediateChildren as $child) {
  668. $type = $child->getContentType();
  669. $level = \array_key_exists($type, $this->alternativePartOrder) ? $this->alternativePartOrder[$type] : max($this->alternativePartOrder) + 1;
  670. if (empty($sorted[$level])) {
  671. $sorted[$level] = [];
  672. }
  673. $sorted[$level][] = $child;
  674. }
  675. ksort($sorted);
  676. $this->immediateChildren = array_reduce($sorted, 'array_merge', []);
  677. }
  678. }
  679. /**
  680. * Empties it's own contents from the cache.
  681. */
  682. public function __destruct()
  683. {
  684. if ($this->cache instanceof Swift_KeyCache) {
  685. $this->cache->clearAll($this->cacheKey);
  686. }
  687. }
  688. /**
  689. * Make a deep copy of object.
  690. */
  691. public function __clone()
  692. {
  693. $this->headers = clone $this->headers;
  694. $this->encoder = clone $this->encoder;
  695. $this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values
  696. $children = [];
  697. foreach ($this->children as $pos => $child) {
  698. $children[$pos] = clone $child;
  699. }
  700. $this->setChildren($children);
  701. }
  702. public function __wakeup()
  703. {
  704. $this->cacheKey = bin2hex(random_bytes(16)); // set 32 hex values
  705. $this->cache = new Swift_KeyCache_ArrayKeyCache(new Swift_KeyCache_SimpleKeyCacheInputStream());
  706. }
  707. }