Swift_Transport_MailTransport::send()   F
last analyzed

Complexity

Conditions 12
Paths 513

Size

Total Lines 101
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 48
CRAP Score 14.575

Importance

Changes 0
Metric Value
cc 12
eloc 59
nc 513
nop 2
dl 0
loc 101
ccs 48
cts 65
cp 0.7385
crap 14.575
rs 3.0465
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
 * Sends Messages using the mail() function.
13
 *
14
 * It is advised that users do not use this transport if at all possible
15
 * since a number of plugin features cannot be used in conjunction with this
16
 * transport due to the internal interface in PHP itself.
17
 *
18
 * The level of error reporting with this transport is incredibly weak, again
19
 * due to limitations of PHP's internal mail() function.  You'll get an
20
 * all-or-nothing result from sending.
21
 *
22
 * @author Chris Corbyn
23
 */
24
class Swift_Transport_MailTransport implements Swift_Transport
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
25
{
26
    /** Additional parameters to pass to mail() */
27
    private $_extraParams = '-f%s';
28
29
    /** The event dispatcher from the plugin API */
30
    private $_eventDispatcher;
31
32
    /**
33
     * Create a new MailTransport with the $log.
34
     *
35
     * @param Swift_Events_EventDispatcher $eventDispatcher
36
     */
37 21
    public function __construct(Swift_Events_EventDispatcher $eventDispatcher)
38
    {
39 21
        $this->_eventDispatcher = $eventDispatcher;
40 21
    }
41
42
    /**
43
     * Not used.
44
     */
45
    public function isStarted()
46
    {
47
        return false;
48
    }
49
50
    /**
51
     * Not used.
52
     */
53
    public function start()
54
    {
55
    }
56
57
    /**
58
     * Not used.
59
     */
60
    public function stop()
61
    {
62
    }
63
64
    /**
65
     * Set the additional parameters used on the mail() function.
66
     *
67
     * This string is formatted for sprintf() where %s is the sender address.
68
     *
69
     * @param string $params
70
     *
71
     * @return $this
72
     */
73 2
    public function setExtraParams($params)
74
    {
75 2
        $this->_extraParams = $params;
76
77 2
        return $this;
78
    }
79
80
    /**
81
     * Get the additional parameters used on the mail() function.
82
     *
83
     * This string is formatted for sprintf() where %s is the sender address.
84
     *
85
     * @return string
86
     */
87
    public function getExtraParams()
88
    {
89
        return $this->_extraParams;
90
    }
91
92
    /**
93
     * Send the given Message.
94
     *
95
     * Recipient/sender data will be retrieved from the Message API.
96
     * The return value is the number of recipients who were accepted for delivery.
97
     *
98
     * @param Swift_Mime_Message $message
99
     * @param string[]           $failedRecipients An array of failures (by-reference)
100
     *
101
     * @return int
102
     *
103
     * @throws Swift_TransportException
104
     */
105 20
    public function send(Swift_Mime_Message $message, &$failedRecipients = null)
106
    {
107 20
        $failedRecipients = (array)$failedRecipients;
108
109 20
        $evt = $this->_eventDispatcher->createSendEvent($this, $message);
110 20
        if ($evt) {
111
112
            $this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
113
            if ($evt->bubbleCancelled()) {
114
                return 0;
115
            }
116
        }
117
118
        $count = (
119 20
            count((array)$message->getTo())
120 20
            + count((array)$message->getCc())
121 20
            + count((array)$message->getBcc())
122 20
        );
123
124
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
125
        // TODO: check if we need this check, breaks "Mockery"-Tests
126
        if ($count === 0) {
127
            $this->_throwException(new Swift_TransportException('Cannot send message without a recipient'));
128
        }
129
        */
130
131 20
        $toHeader = $message->getHeaders()->get('To');
0 ignored issues
show
Bug introduced by
The method get does only exist in Swift_Mime_HeaderSet, but not in Swift_Mime_Header.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
132 20
        $subjectHeader = $message->getHeaders()->get('Subject');
133
134 20
        if (0 === $count) {
135 1
            $this->_throwException(new Swift_TransportException('Cannot send message without a recipient'));
136
        }
137
138 19
        $to = $toHeader ? $toHeader->getFieldBody() : '';
139 19
        $subject = $subjectHeader ? $subjectHeader->getFieldBody() : '';
140
141 19
        $reversePath = $this->_getReversePath($message);
142
143
        // Remove headers that would otherwise be duplicated
144 19
        $message->getHeaders()->remove('To');
0 ignored issues
show
Bug introduced by
The method remove does only exist in Swift_Mime_HeaderSet, but not in Swift_Mime_Header.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
145 19
        $message->getHeaders()->remove('Subject');
146
147 19
        $messageStr = $message->toString();
148
149 19
        if ($toHeader) {
150 19
          $message->getHeaders()->set($toHeader);
0 ignored issues
show
Bug introduced by
The method set does only exist in Swift_Mime_HeaderSet, but not in Swift_Mime_Header.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
151 19
        }
152 19
        $message->getHeaders()->set($subjectHeader);
153
154
        // Separate headers from body
155 19
        if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) {
156 3
            $headers = substr($messageStr, 0, $endHeaders) . "\r\n"; // Keep last EOL
157 3
            $body = substr($messageStr, $endHeaders + 4);
158 3
        } else {
159 16
            $headers = $messageStr . "\r\n";
160 16
            $body = '';
161
        }
162
163 19
        unset($messageStr);
164
165 19
        if ("\r\n" !== PHP_EOL) {
166
            // Non-windows (not using SMTP)
167 19
            $headers = str_replace("\r\n", PHP_EOL, $headers);
168 19
            $subject = str_replace("\r\n", PHP_EOL, $subject);
169 19
            $body = str_replace("\r\n", PHP_EOL, $body);
170 19
            $to = str_replace("\r\n", PHP_EOL, $to);
171 19
        } else {
172
            // Windows, using SMTP
173
            $headers = str_replace("\r\n.", "\r\n..", $headers);
174
            $subject = str_replace("\r\n.", "\r\n..", $subject);
175
            $body = str_replace("\r\n.", "\r\n..", $body);
176
            $to = str_replace("\r\n.", "\r\n..", $to);
177
        }
178
179 19
        if ($this->mail($to, $subject, $body, $headers, $this->_formatExtraParams($this->_extraParams, $reversePath))) {
180 1
            if ($evt) {
181
                $evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
182
                $evt->setFailedRecipients($failedRecipients);
183
                $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
184
            }
185 1
        } else {
186 18
            $failedRecipients = array_merge(
187 18
                $failedRecipients,
188 18
                array_keys((array)$message->getTo()),
189 18
                array_keys((array)$message->getCc()),
190 18
                array_keys((array)$message->getBcc())
191 18
            );
192
193 18
            if ($evt) {
194
                $evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
195
                $evt->setFailedRecipients($failedRecipients);
196
                $this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
197
            }
198
199 18
            $count = 0;
200
        }
201
202 19
        $message->generateId();  // Make sure a new Message ID is used
203
204 19
        return $count;
205
    }
206
207
    /**
208
     * Register a plugin.
209
     *
210
     * @param Swift_Events_EventListener $plugin
211
     */
212
    public function registerPlugin(Swift_Events_EventListener $plugin)
213
    {
214
        $this->_eventDispatcher->bindEventListener($plugin);
215
    }
216
217
    /**
218
     * Throw a TransportException, first sending it to any listeners
219
     *
220
     * @param Swift_TransportException $e
221
     *
222
     * @throws Swift_TransportException
223
     */
224 1 View Code Duplication
    protected function _throwException(Swift_TransportException $e)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
    {
226 1
        $evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e);
227 1
        if ($evt) {
228
229
            $this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
230
            if (!$evt->bubbleCancelled()) {
231
                throw $e;
232
            }
233
234
        } else {
235 1
            throw $e;
236
        }
237
    }
238
239
    /**
240
     * Send mail via the mail() function.
241
     *
242
     * This method takes the same arguments as PHP mail().
243
     *
244
     * @param string $to
245
     * @param string $subject
246
     * @param string $body
247
     * @param string $headers
248
     * @param string $extraParams
249
     *
250
     * @return bool
251
     */
252 3
    public function mail($to, $subject, $body, $headers = null, $extraParams = null)
253
    {
254
        /** @noinspection DeprecatedIniOptionsInspection */
255 3
        if (!ini_get('safe_mode')) {
256
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
257 3
            return @mail($to, $subject, $body, $headers, $extraParams);
258
        }
259
260
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
261
        return @mail($to, $subject, $body, $headers);
262
    }
263
264
    /**
265
     * Determine the best-use reverse path for this message
266
     *
267
     * @param Swift_Mime_Message $message
268
     *
269
     * @return mixed|null|string
270
     */
271 19 View Code Duplication
    private function _getReversePath(Swift_Mime_Message $message)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
272
    {
273 19
        $return = $message->getReturnPath();
274 19
        $sender = $message->getSender();
275 19
        $from = $message->getFrom();
276 19
        $path = null;
277 19
        if (!empty($return)) {
278 4
            $path = $return;
279 19
        } elseif (!empty($sender)) {
280
            $keys = array_keys($sender);
281
            $path = array_shift($keys);
282 15
        } elseif (!empty($from)) {
283
            $keys = array_keys($from);
284
            $path = array_shift($keys);
285
        }
286
287 19
        return $path;
288
    }
289
290
    /**
291
     * Fix CVE-2016-10074 by disallowing potentially unsafe shell characters.
292
     *
293
     * Note that escapeshellarg and escapeshellcmd are inadequate for our purposes, especially on Windows.
294
     *
295
     * @param string $string The string to be validated
296
     *
297
     * @return bool
298
     */
299 3
    private function _isShellSafe($string)
300
    {
301
        // Future-proof
302
        if (
303 3
            escapeshellcmd($string) !== $string
304 3
            ||
305 2
            !in_array(escapeshellarg($string), array("'$string'", "\"$string\""), true)
306 3
        ) {
307 1
            return false;
308
        }
309
310 2
        $length = strlen($string);
311 2
        for ($i = 0; $i < $length; ++$i) {
312 2
            $c = $string[$i];
313
            // All other characters have a special meaning in at least one common shell, including = and +.
314
            // Full stop (.) has a special meaning in cmd.exe, but its impact should be negligible here.
315
            // Note that this does permit non-Latin alphanumeric characters based on the current locale.
316 2
            if (!ctype_alnum($c) && strpos('@_-.', $c) === false) {
317
                return false;
318
            }
319 2
        }
320
321 2
        return true;
322
    }
323
324
    /**
325
     * Return php mail extra params to use for invoker->mail.
326
     *
327
     * @param string $extraParams
328
     * @param string $reversePath
329
     *
330
     * @return null|string
331
     */
332 19
    private function _formatExtraParams($extraParams, $reversePath)
333
    {
334 19
        if (strpos($extraParams, '-f%s') !== false) {
335
            if (
336 18
                empty($reversePath)
337
                ||
338 3
                false === $this->_isShellSafe($reversePath)
339 18
            ) {
340 16
                $extraParams = str_replace('-f%s', '', $extraParams);
341 16
            } else {
342 2
                $extraParams = sprintf($extraParams, $reversePath);
343
            }
344 18
        }
345
346 19
        return !empty($extraParams) ? $extraParams : null;
347
    }
348
}
349