forked from PHPMailer/PHPMailer
-
Notifications
You must be signed in to change notification settings - Fork 901
/
TestCase.php
391 lines (343 loc) · 12 KB
/
TestCase.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
<?php
/**
* PHPMailer - Base test class.
* PHP version 5.5.
*
* @author Marcus Bointon <[email protected]>
* @author Andy Prevost
* @copyright 2012 - 2020 Marcus Bointon
* @copyright 2004 - 2009 Andy Prevost
* @license http:https://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
*/
namespace PHPMailer\Test;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use ReflectionClass;
use ReflectionProperty;
use Yoast\PHPUnitPolyfills\TestCases\TestCase as PolyfillTestCase;
/**
* PHPMailer - Base test class.
*/
abstract class TestCase extends PolyfillTestCase
{
/**
* Whether or not to initialize the PHPMailer object to throw exceptions.
*
* Overload this constant in a concrete test class and set the value to `true`
* to initialize PHPMailer with Exceptions turned on.
*
* @var bool|null
*/
const USE_EXCEPTIONS = null;
/**
* Property names and their values for the test instance of the PHPMailer class.
*
* These (public) properties will be set in the `set_up()` method.
*
* This property can be enhanced/overloaded in concrete test classes to change the presets
* or add additional properties.
*
* It is the responsibility of the individual test classes to ensure that
* property values of the correct type are passed.
*
* @var array Key is the property name, value the desired value for the PHPMailer instance.
*/
protected $propertyChanges = [
'SMTPDebug' => SMTP::DEBUG_CONNECTION, // Full debug output.
'Debugoutput' => ['PHPMailer\Test\DebugLogTestListener', 'debugLog'],
];
/**
* Holds the PHPMailer instance.
*
* @var PHPMailer
*/
protected $Mail;
/**
* Holds the change log.
*
* @var string[]
*/
private $ChangeLog = [];
/**
* Holds the note log.
*
* @var string[]
*/
private $NoteLog = [];
/*
* List of *public* properties which we don't want listed in the changelog
* as they will already be included in the mail/debug information
* created in `buildBody()` anyway.
*
* Note: no need to include protected or private properties as the tests don't
* have access to those anyway.
*
* @var array Key is the property name, value irrelevant.
*/
private $changelogExclude = [
// These are always set in set_up().
'SMTPDebug' => true,
'Debugoutput' => true,
// These are part of the message body anyway.
'Subject' => true,
'Body' => true,
'AltBody' => true,
'Ical' => true,
// These will always change.
'MessageID' => true,
'MessageDate' => true,
// These are always explicitly added via buildBody() anyway.
'ContentType' => true,
'CharSet' => true,
'Host' => true,
];
/**
* List of *static* properties in the PHPMailer class which _may_ be changed from within a test,
* with their default values.
*
* This list is used by the {@see `TestCase::resetStaticProperties()`} method, as well as
* in the {@see `TestCase::checkChanges()`} method.
*
* {@internal The default values have to be (manually) maintained here as the Reflection
* extension does not provide accurate information on the default values of static properties.}
*
* @var array Key is the property name, value the default as per the PHPMailer class.
*/
private $PHPMailerStaticProps = [
'LE' => PHPMailer::CRLF,
'validator' => 'php',
];
/**
* Run before each test class.
*/
public static function set_up_before_class()
{
if (defined('PHPMAILER_INCLUDE_DIR') === false) {
/*
* Set up default include path.
* Default to the dir above the test dir, i.e. the project home dir.
*/
define('PHPMAILER_INCLUDE_DIR', dirname(__DIR__));
}
}
/**
* Run before each test is started.
*/
protected function set_up()
{
// Initialize the PHPMailer class.
if (is_bool(static::USE_EXCEPTIONS)) {
$this->Mail = new PHPMailer(static::USE_EXCEPTIONS);
} else {
$this->Mail = new PHPMailer();
}
// Set initial property values.
foreach ($this->propertyChanges as $key => $value) {
if ($key === 'to' || $key === 'cc' || $key === 'bcc' || $key === 'ReplyTo') {
if (is_array($value) && isset($value['address'], $value['name'])) {
$this->setAddress($value['address'], $value['name'], $key);
} elseif (is_string($value)) {
$this->setAddress($value, '', $key);
}
continue;
}
$this->Mail->{$key} = $value;
}
if ($this->Mail->Host != '') {
$this->Mail->isSMTP();
} else {
$this->Mail->isMail();
}
}
/**
* Run after each test is completed.
*/
protected function tear_down()
{
// Make sure that any changes to static variables are undone after each test.
$this->resetStaticProperties();
// Clean test class native properties between tests.
$this->Mail = null;
$this->ChangeLog = [];
$this->NoteLog = [];
}
/**
* Reset the static properties in the PHPMailer class to their default values.
*/
protected function resetStaticProperties()
{
$reflClass = new ReflectionClass(PHPMailer::class);
$staticPropValues = $reflClass->getStaticProperties();
foreach ($this->PHPMailerStaticProps as $name => $default) {
if (isset($staticPropValues[$name]) && $staticPropValues[$name] === $default) {
continue;
}
self::updateStaticProperty(PHPMailer::class, $name, $default);
}
}
/**
* Update the value of a - potentially inaccessible - static property in a class.
*
* @param string $className The target class.
* @param string $propertyName The name of the static property.
* @param mixed $value The new value for the property.
*/
public static function updateStaticProperty($className, $propertyName, $value)
{
$reflProp = new ReflectionProperty($className, $propertyName);
if (PHP_VERSION_ID < 80100) {
//setAccessible is only needed in PHP < 8.1
$isPublic = $reflProp->isPublic();
if ($isPublic !== true) {
$reflProp->setAccessible(true);
}
}
$reflProp->setValue(null, $value);
if (PHP_VERSION_ID < 80100) {
if ($isPublic !== true) {
$reflProp->setAccessible(false);
}
}
}
/**
* Build the body of the message in the appropriate format.
*/
protected function buildBody()
{
$this->checkChanges();
// Determine line endings for message.
if ('text/html' === $this->Mail->ContentType || $this->Mail->AltBody !== '') {
$eol = "<br>\r\n";
$bullet_start = '<li>';
$bullet_end = "</li>\r\n";
$list_start = "<ul>\r\n";
$list_end = "</ul>\r\n";
} else {
$eol = "\r\n";
$bullet_start = ' - ';
$bullet_end = "\r\n";
$list_start = '';
$list_end = '';
}
$ReportBody = '';
$ReportBody .= '---------------------' . $eol;
$ReportBody .= 'Unit Test Information' . $eol;
$ReportBody .= '---------------------' . $eol;
$ReportBody .= 'phpmailer version: ' . PHPMailer::VERSION . $eol;
$ReportBody .= 'Content Type: ' . $this->Mail->ContentType . $eol;
$ReportBody .= 'CharSet: ' . $this->Mail->CharSet . $eol;
if ($this->Mail->Host !== '') {
$ReportBody .= 'Host: ' . $this->Mail->Host . $eol;
}
// If attachments then create an attachment list.
$attachments = $this->Mail->getAttachments();
if (count($attachments) > 0) {
$ReportBody .= 'Attachments:' . $eol;
$ReportBody .= $list_start;
foreach ($attachments as $attachment) {
$ReportBody .= $bullet_start . 'Name: ' . $attachment[1] . ', ';
$ReportBody .= 'Encoding: ' . $attachment[3] . ', ';
$ReportBody .= 'Type: ' . $attachment[4] . $bullet_end;
}
$ReportBody .= $list_end . $eol;
}
// If there are changes then list them.
if (count($this->ChangeLog) > 0) {
$ReportBody .= 'Changes' . $eol;
$ReportBody .= '-------' . $eol;
$ReportBody .= $list_start;
foreach ($this->ChangeLog as $iValue) {
$ReportBody .= $bullet_start . $iValue[0] . ' was changed to [' .
$iValue[1] . ']' . $bullet_end;
}
$ReportBody .= $list_end . $eol . $eol;
}
// If there are notes then list them.
if (count($this->NoteLog) > 0) {
$ReportBody .= 'Notes' . $eol;
$ReportBody .= '-----' . $eol;
$ReportBody .= $list_start;
foreach ($this->NoteLog as $iValue) {
$ReportBody .= $bullet_start . $iValue . $bullet_end;
}
$ReportBody .= $list_end;
}
// Re-attach the original body.
$this->Mail->Body .= $eol . $ReportBody;
}
/**
* Check which default settings have been changed for the report.
*/
protected function checkChanges()
{
// Get the default values of all public properties.
$defaults = get_class_vars(PHPMailer::class);
foreach ($defaults as $propertyName => $value) {
if (isset($this->changelogExclude[$propertyName])) {
continue;
}
if (isset($this->PHPMailerStaticProps[$propertyName])) {
// Nested static access is not supported in PHP < 7.0, so we need an interim variable.
$mail = $this->Mail;
if ($mail::${$propertyName} !== $this->PHPMailerStaticProps[$propertyName]) {
$this->addChange($propertyName, var_export($mail::${$propertyName}, true));
}
continue;
}
// Check against the TestCase specific defaults.
if (
isset($this->propertyChanges[$propertyName])
&& $this->Mail->{$propertyName} !== $this->propertyChanges[$propertyName]
) {
$this->addChange($propertyName, var_export($this->Mail->{$propertyName}, true));
continue;
}
// Check against the PHPMailer class defaults.
if ($this->Mail->{$propertyName} !== $value) {
$this->addChange($propertyName, var_export($this->Mail->{$propertyName}, true));
}
}
}
/**
* Add a changelog entry.
*
* @param string $sName
* @param string $sNewValue
*/
protected function addChange($sName, $sNewValue)
{
$this->ChangeLog[] = [$sName, $sNewValue];
}
/**
* Adds a simple note to the message.
*
* @param string $sValue
*/
protected function addNote($sValue)
{
$this->NoteLog[] = $sValue;
}
/**
* Adds all of the addresses.
*
* @param string $sAddress
* @param string $sName
* @param string $sType
*
* @return bool
*/
protected function setAddress($sAddress, $sName = '', $sType = 'to')
{
switch ($sType) {
case 'to':
return $this->Mail->addAddress($sAddress, $sName);
case 'cc':
return $this->Mail->addCC($sAddress, $sName);
case 'bcc':
return $this->Mail->addBCC($sAddress, $sName);
case 'ReplyTo':
return $this->Mail->addReplyTo($sAddress, $sName);
}
return false;
}
}