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//@require 'Swift/Events/SendListener.php';
12//@require 'Swift/Events/SendEvent.php';
13//@require 'Swift/Plugins/Decorator/Replacements.php';
14
15/**
16 * Allows customization of Messages on-the-fly.
17 *
18 * @package Swift
19 * @subpackage Plugins
20 *
21 * @author Chris Corbyn
22 */
23class Swift_Plugins_DecoratorPlugin
24  implements Swift_Events_SendListener, Swift_Plugins_Decorator_Replacements
25{
26
27  /** The replacement map */
28  private $_replacements;
29
30  /** The body as it was before replacements */
31  private $_orginalBody;
32
33  /** The original subject of the message, before replacements */
34  private $_originalSubject;
35
36  /** Bodies of children before they are replaced */
37  private $_originalChildBodies = array();
38
39  /** The Message that was last replaced */
40  private $_lastMessage;
41
42  /**
43   * Create a new DecoratorPlugin with $replacements.
44   *
45   * The $replacements can either be an associative array, or an implementation
46   * of {@link Swift_Plugins_Decorator_Replacements}.
47   *
48   * When using an array, it should be of the form:
49   * <code>
50   * $replacements = array(
51   *  "address1@domain.tld" => array("{a}" => "b", "{c}" => "d"),
52   *  "address2@domain.tld" => array("{a}" => "x", "{c}" => "y")
53   * )
54   * </code>
55   *
56   * When using an instance of {@link Swift_Plugins_Decorator_Replacements},
57   * the object should return just the array of replacements for the address
58   * given to {@link Swift_Plugins_Decorator_Replacements::getReplacementsFor()}.
59   *
60   * @param mixed $replacements
61   */
62  public function __construct($replacements)
63  {
64    if (!($replacements instanceof Swift_Plugins_Decorator_Replacements))
65    {
66      $this->_replacements = (array) $replacements;
67    }
68    else
69    {
70      $this->_replacements = $replacements;
71    }
72  }
73
74  /**
75   * Invoked immediately before the Message is sent.
76   *
77   * @param Swift_Events_SendEvent $evt
78   */
79  public function beforeSendPerformed(Swift_Events_SendEvent $evt)
80  {
81    $message = $evt->getMessage();
82    $this->_restoreMessage($message);
83    $to = array_keys($message->getTo());
84    $address = array_shift($to);
85    if ($replacements = $this->getReplacementsFor($address))
86    {
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      {
95        $this->_originalBody = $body;
96        $message->setBody($bodyReplaced);
97      }
98      $subject = $message->getSubject();
99      $subjectReplaced = str_replace(
100        $search, $replace, $subject
101        );
102      if ($subject != $subjectReplaced)
103      {
104        $this->_originalSubject = $subject;
105        $message->setSubject($subjectReplaced);
106      }
107      $children = (array) $message->getChildren();
108      foreach ($children as $child)
109      {
110        list($type, ) = sscanf($child->getContentType(), '%[^/]/%s');
111        if ('text' == $type)
112        {
113          $body = $child->getBody();
114          $bodyReplaced = str_replace(
115            $search, $replace, $body
116            );
117          if ($body != $bodyReplaced)
118          {
119            $child->setBody($bodyReplaced);
120            $this->_originalChildBodies[$child->getId()] = $body;
121          }
122        }
123      }
124      $this->_lastMessage = $message;
125    }
126  }
127
128  /**
129   * Find a map of replacements for the address.
130   *
131   * If this plugin was provided with a delegate instance of
132   * {@link Swift_Plugins_Decorator_Replacements} then the call will be
133   * delegated to it.  Otherwise, it will attempt to find the replacements
134   * from the array provided in the constructor.
135   *
136   * If no replacements can be found, an empty value (NULL) is returned.
137   *
138   * @param string $address
139   *
140   * @return array
141   */
142  public function getReplacementsFor($address)
143  {
144    if ($this->_replacements instanceof Swift_Plugins_Decorator_Replacements)
145    {
146      return $this->_replacements->getReplacementsFor($address);
147    }
148    else
149    {
150      return isset($this->_replacements[$address])
151        ? $this->_replacements[$address]
152        : null
153        ;
154    }
155  }
156
157  /**
158   * Invoked immediately after the Message is sent.
159   *
160   * @param Swift_Events_SendEvent $evt
161   */
162  public function sendPerformed(Swift_Events_SendEvent $evt)
163  {
164    $this->_restoreMessage($evt->getMessage());
165  }
166
167  // -- Private methods
168
169  /** Restore a changed message back to its original state */
170  private function _restoreMessage(Swift_Mime_Message $message)
171  {
172    if ($this->_lastMessage === $message)
173    {
174      if (isset($this->_originalBody))
175      {
176        $message->setBody($this->_originalBody);
177        $this->_originalBody = null;
178      }
179      if (isset($this->_originalSubject))
180      {
181        $message->setSubject($this->_originalSubject);
182        $this->_originalSubject = null;
183      }
184      if (!empty($this->_originalChildBodies))
185      {
186        $children = (array) $message->getChildren();
187        foreach ($children as $child)
188        {
189          $id = $child->getId();
190          if (array_key_exists($id, $this->_originalChildBodies))
191          {
192            $child->setBody($this->_originalChildBodies[$id]);
193          }
194        }
195        $this->_originalChildBodies = array();
196      }
197      $this->_lastMessage = null;
198    }
199  }
200
201}
202