SimpleMimeEntity.php 23KB

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