1<?php 2 3/* 4 * This file is part of SwiftMailer. 5 * (c) 2004-2009 Chris Corbyn 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11/** 12 * Allows customization of Messages on-the-fly. 13 * 14 * @author Chris Corbyn 15 * @author Fabien Potencier 16 */ 17class Swift_Plugins_DecoratorPlugin implements Swift_Events_SendListener, Swift_Plugins_Decorator_Replacements 18{ 19 /** The replacement map */ 20 private $replacements; 21 22 /** The body as it was before replacements */ 23 private $originalBody; 24 25 /** The original headers of the message, before replacements */ 26 private $originalHeaders = array(); 27 28 /** Bodies of children before they are replaced */ 29 private $originalChildBodies = array(); 30 31 /** The Message that was last replaced */ 32 private $lastMessage; 33 34 /** 35 * Create a new DecoratorPlugin with $replacements. 36 * 37 * The $replacements can either be an associative array, or an implementation 38 * of {@link Swift_Plugins_Decorator_Replacements}. 39 * 40 * When using an array, it should be of the form: 41 * <code> 42 * $replacements = array( 43 * "address1@domain.tld" => array("{a}" => "b", "{c}" => "d"), 44 * "address2@domain.tld" => array("{a}" => "x", "{c}" => "y") 45 * ) 46 * </code> 47 * 48 * When using an instance of {@link Swift_Plugins_Decorator_Replacements}, 49 * the object should return just the array of replacements for the address 50 * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}. 51 * 52 * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements 53 */ 54 public function __construct($replacements) 55 { 56 $this->setReplacements($replacements); 57 } 58 59 /** 60 * Sets replacements. 61 * 62 * @param mixed $replacements Array or Swift_Plugins_Decorator_Replacements 63 * 64 * @see __construct() 65 */ 66 public function setReplacements($replacements) 67 { 68 if (!($replacements instanceof Swift_Plugins_Decorator_Replacements)) { 69 $this->replacements = (array) $replacements; 70 } else { 71 $this->replacements = $replacements; 72 } 73 } 74 75 /** 76 * Invoked immediately before the Message is sent. 77 * 78 * @param Swift_Events_SendEvent $evt 79 */ 80 public function beforeSendPerformed(Swift_Events_SendEvent $evt) 81 { 82 $message = $evt->getMessage(); 83 $this->restoreMessage($message); 84 $to = array_keys($message->getTo()); 85 $address = array_shift($to); 86 if ($replacements = $this->getReplacementsFor($address)) { 87 $body = $message->getBody(); 88 $search = array_keys($replacements); 89 $replace = array_values($replacements); 90 $bodyReplaced = str_replace( 91 $search, $replace, $body 92 ); 93 if ($body != $bodyReplaced) { 94 $this->originalBody = $body; 95 $message->setBody($bodyReplaced); 96 } 97 98 foreach ($message->getHeaders()->getAll() as $header) { 99 $body = $header->getFieldBodyModel(); 100 $count = 0; 101 if (is_array($body)) { 102 $bodyReplaced = array(); 103 foreach ($body as $key => $value) { 104 $count1 = 0; 105 $count2 = 0; 106 $key = is_string($key) ? str_replace($search, $replace, $key, $count1) : $key; 107 $value = is_string($value) ? str_replace($search, $replace, $value, $count2) : $value; 108 $bodyReplaced[$key] = $value; 109 110 if (!$count && ($count1 || $count2)) { 111 $count = 1; 112 } 113 } 114 } elseif (is_string($body)) { 115 $bodyReplaced = str_replace($search, $replace, $body, $count); 116 } 117 118 if ($count) { 119 $this->originalHeaders[$header->getFieldName()] = $body; 120 $header->setFieldBodyModel($bodyReplaced); 121 } 122 } 123 124 $children = (array) $message->getChildren(); 125 foreach ($children as $child) { 126 list($type) = sscanf($child->getContentType(), '%[^/]/%s'); 127 if ('text' == $type) { 128 $body = $child->getBody(); 129 $bodyReplaced = str_replace( 130 $search, $replace, $body 131 ); 132 if ($body != $bodyReplaced) { 133 $child->setBody($bodyReplaced); 134 $this->originalChildBodies[$child->getId()] = $body; 135 } 136 } 137 } 138 $this->lastMessage = $message; 139 } 140 } 141 142 /** 143 * Find a map of replacements for the address. 144 * 145 * If this plugin was provided with a delegate instance of 146 * {@link Swift_Plugins_Decorator_Replacements} then the call will be 147 * delegated to it. Otherwise, it will attempt to find the replacements 148 * from the array provided in the constructor. 149 * 150 * If no replacements can be found, an empty value (NULL) is returned. 151 * 152 * @param string $address 153 * 154 * @return array 155 */ 156 public function getReplacementsFor($address) 157 { 158 if ($this->replacements instanceof Swift_Plugins_Decorator_Replacements) { 159 return $this->replacements->getReplacementsFor($address); 160 } 161 162 return isset($this->replacements[$address]) ? $this->replacements[$address] : null; 163 } 164 165 /** 166 * Invoked immediately after the Message is sent. 167 * 168 * @param Swift_Events_SendEvent $evt 169 */ 170 public function sendPerformed(Swift_Events_SendEvent $evt) 171 { 172 $this->restoreMessage($evt->getMessage()); 173 } 174 175 /** Restore a changed message back to its original state */ 176 private function restoreMessage(Swift_Mime_SimpleMessage $message) 177 { 178 if ($this->lastMessage === $message) { 179 if (isset($this->originalBody)) { 180 $message->setBody($this->originalBody); 181 $this->originalBody = null; 182 } 183 if (!empty($this->originalHeaders)) { 184 foreach ($message->getHeaders()->getAll() as $header) { 185 if (array_key_exists($header->getFieldName(), $this->originalHeaders)) { 186 $header->setFieldBodyModel($this->originalHeaders[$header->getFieldName()]); 187 } 188 } 189 $this->originalHeaders = array(); 190 } 191 if (!empty($this->originalChildBodies)) { 192 $children = (array) $message->getChildren(); 193 foreach ($children as $child) { 194 $id = $child->getId(); 195 if (array_key_exists($id, $this->originalChildBodies)) { 196 $child->setBody($this->originalChildBodies[$id]); 197 } 198 } 199 $this->originalChildBodies = array(); 200 } 201 $this->lastMessage = null; 202 } 203 } 204} 205