👉 Important: this is for **version** updates only, not for security updates, which are handled separately and don't depend on this configuration.
---
PR 3229 updated the GitHub Actions workflows used in this repo to use "pinned" versions for external action runners to improve workflow security.
The current "frequency" is weekly. As these updates are rarely time-sensitive, it should be fine to receive them less frequently.
This commit tries to make it so by changing the Dependabot schedule for GitHub Actions to once every two weeks and late in the day when the queue should be mostly empty (as long as it's not a Monday), so the update PR will come in on a more predictable schedule.
The `htmlspecialchars()` function is used to escape arbitrary text strings for display.
Original the default for the `$flags` parameter of that function in PHP was `ENT_COMPAT`, which translates to "convert double quotes to `"` and leave single quotes alone".
As of PHP 8.1, the default value for the `$flags` parameter has been made more robust and was changed to `ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401`, which translates to "convert both double and single quotes, replace invalid code unit sequences with a Unicode Replacement Character and treat code as HTML 4.01".
For code to provide the same/predictable output cross-version PHP, the `$flags` parameter should be explicitly set and what with the new default value being the more robust one, this commit adds that value for `$flags` in all instances of function calls to `htmlspecialchars()`.
Once the application minimum PHP version is PHP 8.1 or higher, the parameter can be removed again (as the value will then be the same as the default parameter value).
Ref: https://www.php.net/manual/en/function.htmlspecialchars.php
Best practice tweak regarding the use of PHPCompatibility.
It is strongly recommended to use inline ignore annotations when a reported issue is not a problem (because it is accompanied by a `function_exists()`, `defined()` or other check), instead of excluding a rule completely via the ruleset.
Blanket code-base wide ignores mean that:
* ... if a PR introduces new code (or changes existing code) which uses a non-cross-version compatible PHP feature...
* ... and the code doesn't have the right safeguards in place for cross-version compatibility...
* ... PHPCompatibility would not flag it because of the codebase wide ignore...
* ... which could cause problems for the end-users.
Selective, inline ignores ensure that only the annotated error is ignored and only for that specific bit of code, preventing the above described problem.
It also means that when something changes in PHP - like a deprecated method being removed -, you will be notified about the issue again so you can review if the current cross-version compatibility tweak is still the most optimal one.
Long anticipated, finally here: PHPCompatibility 10.0.0-alpha1 🎉
PHPCompatibility 10.0.0 brings huge improvements in both what is being detected (> 50 new sniffs), as well as the detection accuracy for pre-existing sniffs.
Even though still "unstable", it is stable enough for our purposes and the advantages of using it outweigh the disadvantage of it being an unstable version. By setting the `minimum-stability` and `prefer-stable` settings in the `composer.json`, we can ensure that we don't get the `dev-develop` branch, but rather get a `10.0.0` tag, unstable or not.
Includes updating the PHPCS version constraints to match.
Includes updating the exclusions in the ruleset for changes in the error codes due to further changes in PHP having been made (and now being detected).
Ref:
* https://github.com/PHPCompatibility/PHPCompatibility/wiki/Upgrading-to-PHPCompatibility-10.0
* https://github.com/PHPCompatibility/PHPCompatibility/releases/tag/10.0.0-alpha1
... which is expected to be released this Thursday.
* Builds against PHP 8.5 are no longer allowed to fail.
* Update PHP version on which code coverage is run (high should now be 8.5).
* Add _allowed to fail_ build against PHP 8.6.
* Update the README.
Note: for some jobs I use "nightly" for the "next" PHP version, for some `8.6`. While it may appear there is no difference and this is true for the better part of the year, there is a difference for about two months.
To illustrate, consider PHP 8.5:
* PHP "nightly" refers to the PHP `master` branch, so was PHP 8.5 until the PHP 8.5 was branched off when the first RC was cut in September.
* As of that moment, "nightly" basically became PHP 8.6, so to test against PHP 8.5, one would need to explicitly request `8.5`.
* As of the release of PHP 8.5, it is expected for "nightly" to be PHP 8.6, so the difference is moot again.
For that reason, the unit test workflow uses the explicit `8.6` version for PHP "next".
As things were, test runs on forks would always fail on the "upload code coverage reports" step, as forks (justifiably) don't have access to the `CODECOV_TOKEN`.
Fixed now by updating the conditions to run that step.
The `roave/security-advisories` package was an inventive method to block installation of known insecure versions of other dependencies (via a `conflict` annotation).
As of Composer 2.9, using the `roave/security-advisories` package for this purpose is no longer needed as Composer will now natively block installation of known insecure versions of dependencies.
And while not all contributors to this repo may be using Composer 2.9+ (yet), Composer 2.9+ **_will_** be used in CI and CI failing on Composer blocking an insecure dependency offers the same level of protection as the package previously offered.
Refs:
* https://blog.packagist.com/composer-2-9/
* https://github.com/composer/composer/releases/tag/2.9.0
If generating an OAuth2 token fails in the provider and an exception is thrown, we never catch that exception, but just let exception propagate up to whatever.
I suggest this change, such that we can handle errors when generating oauth tokens. It is based on the fact that ^League\OAuth2\Client\Provider\AbstractProvider` can throw exceptions during getting the oauth token, but we are nowhere near to knowing why.
I have decided on catching all exception (`\Exception`) because if someone uses another OAuthTokenProvider (it's just an interface, so it's possible), we won't know specifically which exceptions can be thrown here, so we need to catch them all.
By throwing a PHPMailer exception and adding the existing exception as the `$previous` variable, we can show a generic error message to regular users and advice how developers can get actually meaningful error messages from the previous exception.
> Users frequently over-scope their workflow and job permissions, or set broad workflow-level permissions without realizing that all jobs inherit those permissions.
>
> Furthermore, users often don't realize that the _default_ `GITHUB_TOKEN` permissions can be very broad, meaning that workflows that don't configure any permissions at all can _still_ provide excessive credentials to their individual jobs.
>
> **Remediation**
> In general, permissions should be declared as minimally as possible, and as close to their usage site as possible.
>
> In practice, this means that workflows should almost always set `permissions: {}` at the workflow level to disable all permissions by default, and then set specific job-level permissions as needed.
This was already addressed for the other two workflows, just not for the `tests` one.
As far as I can see, the jobs here do not need the `GITHUB_TOKEN` secret and even if they do, only for `content: read`, which for public repos does not need to be set explicitly, though it doesn't do any harm to have that set anyway.
Refs:
* https://docs.zizmor.sh/audits/#excessive-permissions
> By default, using `actions/checkout` causes a credential to be persisted in the checked-out repo's `.git/config`, so that subsequent `git` operations can be authenticated.
>
> Subsequent steps may accidentally publicly persist `.git/config`, e.g. by including it in a publicly accessible artifact via `actions/upload-artifact`.
>
> However, even without this, persisting the credential in the `.git/config` is non-ideal unless actually needed.
>
> **Remediation**
>
> Unless needed for `git` operations, `actions/checkout` should be used with `persist-credentials: false`.
>
> If the persisted credential is needed, it should be made explicit with `persist-credentials: true`.
This has now been addressed in all workflows.
Refs:
* https://unit42.paloaltonetworks.com/github-repo-artifacts-leak-tokens/
* https://docs.zizmor.sh/audits/#artipacked
Recently there has been more and more focus on securing GH Actions workflows - in part due to some incidents.
The problem with "unpinned" action runners is as follows:
* Tags are mutable, which means that a tag could point to a safe commit today, but to a malicious commit tomorrow.
Note that GitHub is currently beta-testing a new "immutable releases" feature (= tags and release artifacts can not be changed anymore once the release is published), but whether that has much effect depends on the ecosystem of the packages using the feature.
Aside from that, it will likely take years before all projects adopt _immutable releases_.
* Action runners often don't even point to a tag, but to a branch, making the used action runner a moving target.
_Note: this type of "floating major" for action runners used to be promoted as good practice when the ecosystem was "young". Insights have since changed._
While it is convenient to use "floating majors" of action runners, as this means you only need to update the workflows on a new major release of the action runner, the price is higher risk of malicious code being executed in workflows.
Dependabot, by now, can automatically submit PRs to update pinned action runners too, as long as the commit-hash pinned runner is followed by a comment listing the released version the commit is pointing to.
So, what with Dependabot being capable of updating workflows with pinned action runners, I believe it is time to update the workflows to the _current_ best practice of using commit-hash pinned action runners.
The downside of this change is that there will be more frequent Dependabot PRs.
If this would become a burden/irritating, the following mitigations can be implemented:
1. Updating the Dependabot config to group updates instead of sending individual PRs per action runner.
2. A workflow to automatically merge Dependabot PRs as long as CI passes.
Includes updating the version for `ossf/scorecard-action` as it was a couple of version behind.
Ref: https://docs.github.com/en/actions/reference/security/secure-use#using-third-party-actions
As per the options described in https://github.com/PHPMailer/PHPMailer/pull/3202#issuecomment-3212478928.
Note: the linting ignore comment triggers some PHPCS errors (_sigh_), so I'm selectively excluding those.
Alternatively, it could be considered to exclude test fixture files completely from the PHPCS scan.
on applications with a different locale than english, the message "interrupted system call" is not found because it's translated. So we also check for the SOCKET_EINTR constant which is defined under Windows and UNIX-like platforms (if available on the platform).
Since PHP 8.1, calling the `Reflection*::setAccessible()` methods is no longer necessary as reflected properties/methods/etc will always be accessible.
However, the method calls are still needed for PHP < 8.1.
As of PHP 8.5, calling the `Reflection*::setAccessible()` methods is now formally deprecated and will yield a deprecation notice, which will fail test runs.
As of PHP 9.0, the `setAccessible()` method(s) will be removed.
With the latter in mind, this commit prevents the deprecation notice by making the calls to `setAccessible()` conditional.
Silencing the deprecation would mean, this would need to be "fixed" again come PHP 9.0, while the current solution should be stable, including for PHP 9.0.
Ref: https://wiki.php.net/rfc/deprecations_php_8_5#extreflection_deprecations
While workflows are disabled by default in forks, it is quite common for contributors to enable them to verify CI will pass before submitting a pull request.
When enabling workflow runs in forks, it's "all or nothing".
This means that:
* All workflows which are only intended to be run on the canonical repo will also be enabled.
These workflows will also often need access to repo-specific secrets and will typically fail when run from a fork.
* Workflows which contain cron jobs will also be enabled.
Depending on the type of account the contributor has, this can burn through their "CI minutes".
This commit is based on a review of workflows containing cron jobs and disables running the jobs when a cron job is triggered in a fork.
Update a few tests to match the recent changes.
Use the eai validator (unless another is being used) for addresses such as
info@müller-schmidt.de, for which PHPMailer may not choose to use SMTPUTF8.
This adds the ability to send email to addresses like grå@grå.org, but
preserves phpmailer's old behaviour for all addresses that worked before
(such as info@grå.org).
PHP 8.4 removes the IMAP extension (moved to PECL).
With this in mind, I've reviewed how the tests are being run versus the extension requirements and recommendations.
As things are, the tests are currently run in the "ideal" environment, i.e. with all required and optional extensions available.
However, the codebase also contains fall-backs for when certain extensions are **_not_** available and for at least some of those fallbacks, there are dedicated tests available, but in an ideal environment those tests will not run and the fall-backs are not tested, which is the case with the current CI setup.
To improve this situation, I'm proposing to keep running the tests against all PHP versions with the "ideal" extension set, but to also have additional test runs with a far more limited set of PHP extensions.
To determine which extensions should be in each set, I've looked at the following:
* `@requires` tags found in the test suite and the conditions for calls to `markTestSkipped()`.
This brought to light that the `openssl` extension was currently not listed in the "ideal" extension set. This has now been fixed.
* The required extensions of PHPUnit - `dom, json, libxml, mbstring, tokenizer, xml, xmlwriter`.
* The required extensions of PHPMailer itself - `ctype, filter, hash`.
* Not strictly required, but more for convenience/workflow speed: `curl` for Composer.
* And `xdebug` will still be enabled/disabled based on the `coverage` setting.
Note: while some tests would benefit from being run _without_ the `mbstring` extension, that's unfortunately not an option as `mbstring` is a requirement of PHPUnit 🤷
Also note, the tests with the "minimal" extension setup needs to run `composer install` with an `--ignore-platform-req` flag to prevent running into the following issue:
```
Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.
```
As this extension "requirement" is for a dependency which is not used in the test run, the extension requirement can be safely ignored.
As per the discussion in 3092, this commit removes the `fakefunctions.php` file and adds explicit requirements for the `PHPMailerTest::testGivenIdnAddress_addAddress_returns_true()` test method.
Closes 3092
As things were, the `DKIMTest::testDKIMSignOpenSSLNotAvailable()` could not pass as the `DKIMTest` class sets the `USE_EXCEPTIONS` class constant to `true`, which means the method would fail on an exception.
As this test is specifically about testing the behaviour when exceptions are _disabled_, the test needs to be in its own test class, which sets `USE_EXCEPTIONS` to `false`.
That should allow the test to run properly and to pass.
As things were, the `DKIMTest::testDKIMSignOpenSSLNotAvailableException()` test _could_ potentially pass even when another `Exception` than the `PHPMailer\PHPMailer\Exception` was being thrown, as _all_ exceptions extend the PHP native `Exception` class.
Now this risk is not that high, as there is also a check on the exception message, but still.
Making the exception expectation more specific (by changing the import `use` statement), should still make the test more stable.
Composer 1.10.0 introduced a `lock` config option, which, when set to `false` will prevent a `composer.lock` file from being created and will ignore it when one exists.
This is a useful option for packages like PHPMailer where the `lock` file has no meaning.
It also makes life more straight-forward for contributors as they don't have to remember that for this repo they should use `composer update` instead of `composer install`. Both will now work the same.
Refs:
https://getcomposer.org/doc/06-config.md#lock
V4.1.0 of the SendOauth2 wrapper introduces two new optional operands for the invocation of both the standard PHPMailer email application and for the complete replacement of a PHPMailer application by the wrapper SendOauth2A front-end. These operands allow the developer to specify a name for the .json credentials file and/or whether the wrapper should dynamically build this .json file or use an already-built one, created either by Google (its standard download) or by the developer.
V4.1.0 of the SendOauth2 wrapper introduces two new optional operands for the invocation of both the standard PHPMailer email application and for the complete replacement of a PHPMailer application by the wrapper SendOauth2A front-end. These operands allow the developer to specify a name for the .json credentials file and/or whether the wrapper should dynamically build this .json file or use an already-built one, created either by Google (its standard download) or by the developer.
Client secrets and X.509 certificates, $_SESSION 'state' and PKCE code exchanges, and creation on the fly of GoogleAPI's .json credentials files are supported.
OAuth2 authentication for both Microsoft 365 Exchange email and Google Gmail. Client secrets and X.509 certificates are supported for Exchange. Client secrets are supported for Gmail. Authorization_code grant flow and client_credentials grant flow for SMTP are supported for Exchange. Authorization_code grant flow is supported for Gmail.
In #2377, the `PHPMailerLangTest` test was renamed to `TranslationCompletenessTest`
along some optimizations. However, the link in `README.md` file still links
to the old file name.
This updates the `README.md` file to use the new file name and URL.
On keep-alive connections, we issue an RSET command in case of errors.
But that RSET command may fail, which causes the error information from
the original error to be replaced by the error information from the
RSET error, which isn't helpful in diagnosing the actual cause. To avoid
this problem, we need to extract the error information first, and only
issue the RSET command afterwards.
Caches used in GH Actions do not get updated, they can only be replaced by a different cache with a different cache key.
Now the predefined Composer install action this repo is using already creates a pretty comprehensive cache key:
> `ramsey/composer-install` will auto-generate a cache key which is composed of
the following elements:
> * The OS image name, like `ubuntu-latest`.
> * The exact PHP version, like `8.1.11`.
> * The options passed via `composer-options`.
> * The dependency version setting as per `dependency-versions`.
> * The working directory as per `working-directory`.
> * A hash of the `composer.json` and/or `composer.lock` files.
This means that aside from other factors, the cache will always be busted when changes are made to the (committed) `composer.json` or the `composer.lock` file (if the latter exists in the repo).
For packages running on recent versions of PHP, it also means that the cache will automatically be busted once a month when a new PHP version comes out.
### The problem
For runs on older PHP versions which don't receive updates anymore, the cache will not be busted via new PHP version releases, so effectively, the cache will only be busted when a change is made to the `composer.json`/`composer.lock` file - which may not happen that frequently on low-traffic repos.
But... packages _in use_ on those older PHP versions - especially dependencies of declared dependencies - may still release new versions and those new versions will not exist in the cache and will need to be downloaded each time the action is run and over time the cache gets less and less relevant as more and more packages will need to be downloaded for each run.
### The solution
To combat this issue, a new `custom-cache-suffix` option has been added to the Composer install action in version 2.2.0.
This new option allows for providing some extra information to add to the cache key, which allows for busting the cache based on your own additional criteria.
This commit implements the use of this `custom-cache-suffix` option for all relevant workflows in this repo.
Refs:
* https://github.com/ramsey/composer-install/#custom-cache-suffix
* https://github.com/ramsey/composer-install/releases/tag/2.2.0
runfakepopserver.sh expects fakepopserver.sh to be in the working
directory, therefore the tests in PopBeforeSmtpTest have to chdir()
to the test directory first.
The line in fakepopserver.sh echoing Bye needed -en arguments for
echo to process character escapes.
The disconnect() method throws a TypeError when the TCP
connection cannot be created. Error and trace:
fgets(): Argument #1 ($stream) must be of type resource, bool given
phpmailer/phpmailer/src/POP3.php(372): fgets()
phpmailer/phpmailer/src/POP3.php(345): PHPMailer\PHPMailer\POP3->getResponse()
phpmailer/phpmailer/src/POP3.php(230): PHPMailer\PHPMailer\POP3->disconnect()
PHPMailer\PHPMailer\POP3->authorise()
Reproduce with:
include __DIR__ . "/POP3.php";
\PHPMailer\PHPMailer\POP3::popBeforeSmtp('doesnotexist', 110);
If there is a ruleset error, the `cs2pr` action doesn't receive an `xml` report and exits with a `0` error code, even though the PHPCS run failed (though not on CS errors, but on a ruleset error).
This changes the GH Actions workflow to allow for that situation and still fail the build in that case.
This is required by some POP3 servers which will wait until
a newline char which is delimiting all commands. Without it,
the login works, but it waits for the full timeout.
Most predefined action runners offer a long-running branch or a tag which gets moved every release to allow staying on the "latest" version of a certain major of their action, without having to update the workflow scripts on every release of the action runner.
This works well for action runners which follow semver.
I've reviewed the existing workflows and tweaked the versions used whenever possible to make optimal use of this.
* For the `ossf/scorecard-action` action runner, I have not been able to find a workable tag/branch to fix this on.
Note: I have remove the "# v1.1.1" comment though as it doesn't get updated by Dependabot and was sorely out of date (you are currently on version `2.0.4`).
With the changes in this PR, Dependabot should become less noisy and will only send in PRs to update the action runner versions when a new major release has been tagged. (save for the one exceptions mentioned above)
I would recommend watching the following repos for new releases:
* https://github.com/actions/checkout
* https://github.com/actions/upload-artifact
* https://github.com/shivammathur/setup-php
* https://github.com/ramsey/composer-install
* https://github.com/nick-fields/retry
* https://github.com/codecov/codecov-action
* https://github.com/JamesIves/github-pages-deploy-action
* https://github.com/ossf/scorecard-action
* https://github.com/github/codeql-action
This will ensure you will get an email with the changelogs for those action runners on all releases, so you can still monitor for changes in the action runners you need to be aware of.
In case of errors, keep-alive SMTP connections are always reset, but
when the error happened while establishing the connection, trying to
reset the connection is pointless and just produces another error.
When using PHP's mail() function, and when there are no To-addressees (e.g. only BCC), then the DKIM-signature was not correct. There was a discrepancy between how mail() was called (empty $to) versus the DKIM-signature ('undisclosed-recipients:;').
While rare, there are some deprecations which PHP can show when a file is being linted.
By default these are ignored by PHP-Parallel-Lint.
Apparently though, there is an option to show them (wasn't documented until recently), so let's turn that option on.
This commit adds an initial Dependabot configuration to:
* Submit pull requests for security updates and version updates for GH Action runner dependencies.
At a later point in time, it could be considered to enable it for Composer dependencies as well.
The configuration has been set up to:
* Run weekly.
* Submit a maximum of 5 pull requests at a time.
If additional pull requests are needed, these will subsequently be submitted the next time Dependabot runs after one or more of the open pull requests have been merged.
* The commit messages for PRs submitted by Dependabot will be prefixed with "GH Actions:" similar to previous PRs I've submitted manually for the same.
Yet another predefined action has had a major release.
This is, again, mostly just a change of the Node version used by the action itself (from Node 12 to Node 16).
Refs:
* https://github.com/codecov/codecov-action/releases
A number of predefined actions have had major release, which warrant an update the workflow(s).
These updates don't actually contain any changed functionality, they are mostly just a change of the Node version used by the action itself (from Node 14 to Node 16).
Refs:
* https://github.com/actions/checkout/releases
The `dealerdirect/phpcodesniffer-composer-installer` Composer plugin is used to register external PHPCS standards with PHPCS.
As of Composer 2.2.0, Composer plugins need to be explicitly allowed to run. This adds the necessary configuration for that.
Refs:
* https://blog.packagist.com/composer-2-2/#more-secure-plugin-execution
The `$CharSet` property name as declared in the class starts with a capital, while there were two places in the code where the property was referred to as `$this->charSet`, which is a non-existent property.
In PHP 8.2, this is regarded as access to a dynamic property and will generate deprecation notices.
In effect, this meant that the `PHPMailer::parseAddresses()` method would be called with `null` as the value for the `$charset` parameter.
This `null` value would then subsequently be passed on to the PHP native `mb_internal_encoding()` function, which now didn't get the correct encoding.
This may have led to mail sending failures due to the address encoding being incorrect. If there are any bug reports open for this, it may be a good idea to evaluate whether they could be related to this bug.
Found by dseguy via Exakat.
Note: this issue was not flagged by the tests as the tests for the `PHPMailer::parseAddresses()` method, only test the method either with a valid value for encoding òr without the `$charset` parameter set, but not with an explicit `null` value..
As the `$charset` parameter for `PHPMailer::parseAddresses()` has a sensible default value and is not explicitly nullable, adding a test for this incorrect use of the method seems over the top.
I have, however, ensured that the non-nullability of the parameter is now documented for the method.
Refs:
* https://wiki.php.net/rfc/deprecate_dynamic_properties
* https://www.php.net/manual/en/function.mb-internal-encoding.php
PHPCS 3.6.2 added a sniff for a PSR-12 rule which was previously not strictly checked: "No blank line after the opening brace of a class".
This fixes the newly flagged issues.
PHPUnit just released version 9.5.10 and 8.5.21.
This contains a particular (IMO breaking) change:
> * PHPUnit no longer converts PHP deprecations to exceptions by default (configure `convertDeprecationsToExceptions="true"` to enable this)
Let's unpack this:
Previously (PHPUnit < 9.5.10/8.5.21), if PHPUnit would encounter a PHP native deprecation notice, it would:
1. Show a test which causes a deprecation notice to be thrown as **"errored"**,
2. Show the **first** deprecation notice it encountered and
3. PHPUnit would exit with a **non-0 exit code** (2), which will fail a CI build.
As of PHPUnit 9.5.10/8.5.21, if PHPUnit encounters a PHP native deprecation notice, it will no longer do so. Instead PHPUnit will:
1. Show a test which causes a PHP deprecation notice to be thrown as **"risky"**,
2. Show the **all** deprecation notices it encountered and
3. PHPUnit will exit with a **0 exit code**, which will show a CI build as passing.
This commit reverts PHPUnit to the previous behaviour by adding `convertDeprecationsToExceptions="true"` to the PHPUnit configuration.
Refs:
* https://github.com/sebastianbergmann/phpunit/blob/9.5/ChangeLog-8.5.md
* https://github.com/sebastianbergmann/phpunit/blob/9.5/ChangeLog-9.5.md
So far, this method did not have dedicated tests.
The test file this commit introduces, tests all aspects of the method as well as documents the current behaviour of the method for specific, outlier situations.
So far, the methods related to text localization - `PHPMailer::setLanguage()`, `PHPMailer::getTranslations()` and `PHPMailer::lang()` - did not have dedicated tests.
The test file this commit introduces, tests all aspects of these methods, including the changes introduced in response to 2418 and 2419, as well as documents the current behaviour of the methods for specific, outlier situations.
Language codes which were "language-script" based were not accepted by the regex used, which meant that the `sr_latn` script could never be loaded.
After discussion in 2418, it was decided to support "script" in a language code and to support it like so:
```
2-character language code [_] optional 4-character script code [_] optional 2-character country code
```
This combines the annotation forms of the following known standards:
* https://unicode-org.github.io/cldr-staging/charts/37/summary/root.html
* https://docs.oracle.com/cd/E23824_01/html/E26033/glset.html
* http://www.loc.gov/standards/iso639-2/php/code_list.php
This means that all of the below codes will now pass the language code validation:
```
sr
sr_latn
sr_rs
sr_latn_rs
```
But not:
```
sr_rs_latn
```
This commit applies the above change and also adjusts the "language code fall back" logic to take language codes with a script code into account.
Note: if the requested full "language-script-country" code is not available, "language-country" will take precedence over "language-script" for the fallback logic to find the appropriate translation file.
Related to 2418 - observation 4
When the `$langCode` is passed as "language code - country code" and no translation is found for the specified country variant, the localization would automatically fall back to English, even when there was a viable translation available in the "parent" language, i.e. just based on the _language code_.
This commit changes the logic in the `PHPMailer::setLanguage()` method for when a "lang-country" code is passed.
It will now try and find a "lang-country" file first, if not found, it will try to find a file for just the "lang" and only if that could also not be found, it will fall back to English.
Related to 2418 - observation 2
Includes using named subpatterns in the regex to make the regex self-documenting.
Includes removing a commented out line of code which is superseded anyway.
For "language code - country code" locale notations, it is common for the "country code" part to be provided in uppercase, like `pt_BR`.
The method currently did not allow for that correctly. The regex check would accept the language code, but then fail to find the file on *nix based systems as the file names are all in lowercase and non-Windows file systems are generally case-sensitive. Which means that in effect, the method, ended up falling back to the default language (English).
The change in this commit makes the handling of the provided `$langcode` case-tolerant.
Note: the language code used in the file names for the language files is still expected to always be lowercase, including for language files in custom paths!
Related to 2418 - observation 1
The return value of the method was inconsistent as it could return `true` even when the requested language was not loaded/set.
Previously, the method would:
* Return `false` when a `$langcode` was matched against the regex, but the file couldn't be loaded.
* Return `true` in all other cases, including when a `$langcode` other than `'en'` was passed, but it didn't match the regex, which meant that effectively the requested language was ignored and English was loaded anyway, but the function would still return `true`.
This has now been changed to:
* Return `true` when the requested language was loaded, whether `'en'` or another language.
* Return `false` when the requested language wasn't loaded and the language defaulted to English.
Related to https://github.com/PHPMailer/PHPMailer/issues/2418#issuecomment-876240778
The `mb_decode_mimeheader()` function uses the Mbstring internal encoding to decode.
In PHP 5.5, the default internal encoding was `ISO-8859-1`.
As of PHP 5.6, the default internal encoding was changed to use the value from the `default_charset` ini setting. Additionally, in UTF-8, the value for `default_charset` was changed to `UTF-8`.
This means that when the charset is not explicitly set, the `mb_decode_mimeheader()` function may return garbled nonsense if the charset used to _encode_ does not match the charset per PHP's `default_charset` or - in PHP 5.5 - the Mbstring internal_encoding default.
So far, this wasn't making tests fail because of some hard-coded ini settings being passed in the CI.
However, changing the default ini values creates an assumption that that configuration will be used on all servers on which the PHPMailer code will be run.
This assumption is undocumented (not in the Readme or mentioned elsewhere) and will in most cases be incorrect.
The non-default ini values change the behaviour of PHP and were the cause of test failures against PHP 5.5 which I've been seeing for some of the new tests I've been creating.
Removing the changes fixes those errors, but exposes failing tests in the existing tests for `PHPMailer::parseAddresses()`.
These undocumented _changes_ to the default PHP configuration were **required** for PHPMailer to be able to parse the addresses successfully. As this library is open source and used in a wide variety of environments, those kind of assumptions can not safely be made.
So.... the hard-coded ini settings in the CI configuration ought to be removed.
This then causes the tests for the `PHPMailer::parseAddresses()` function to start failing on PHP 5.5.
> Note: the tests are only failing on PHP 5.5 as the test case causing the failure uses a UTF-8 encoded name and as of PHP 5.6, the default encoding used in PHP is UTF-8, which matches.
> If a test case would be added with a name encoded in a different charset, the tests would also start failing on PHP 5.6+.
To fix those failures and to make the code PHP cross-version compatible, including with PHP installs configured to use a different `default_encoding`:
* We need to make sure that the Mbstring "internal encoding" is set correctly based on the Charset used for PHPMailer.
* And then need to _reset_ the internal encoding after the use of the `mb_decode_mimeheader()` to prevent any impact of this change on the wider application context in which PHPMailer may be used.
As the `PHPMailer::parseAddresses()` method is `static`, it does not have access to the (non-static) `PHPMailer::$charSet` variable.
Knowing that, I've elected to add an additional, optional variable to the `PHPMailer::parseAddresses()` method to allow for passing in the charset and have set the default value for the parameter to be in line with the default value of the `PHPMailer::$charSet` variable.
I have adjusted existing method calls to this method to explicitly pass the charset.
Both of the adjusted function calls are in the "postSend" part of the PHPMailer logic when the charset will be known and final, so can be safely passed.
I've also made minimal changes to the unit test file to allow for passing the charset in the tests.
This implementation is based on the assumption that names can be encoded in different charsets.
If the name encoding only happens when the charset is UTF-8, the new function parameter can be removed and the charset can be set to UTF-8 directly.
As I'm not completely read-in on the RFC specs for the address header being parsed and when encoding happens, I'd like a second opinion on the currently chosen implementation.
If this is the correct way to go, then additional tests need to be added to safeguard that things works correctly when a different encoding is used.
If the encoding only happens for UTF-8, the implementation can be simplified.
Update: the current implementation is correct and a `@todo` note has been added to add more tests with different encodings during a next iteration on these tests.
Refs:
* https://www.php.net/manual/en/migration56.deprecated.php#migration56.deprecated.iconv-mbstring-encoding
* https://php-legacy-docs.zend.com/manual/php5/en/ini.core#ini.default-charset
As noted in 2380, the addressing tests do not belong in the mail transport tests and as the `ReplyToGetSetClearTest` now covers this extensively, the assertions can be removed from the `MailTransportTest`.
The "fakefunctions" are all nice and dandy to get past the `idnSupported()` check, but if either of these functions is not _really_ available, the actual behaviour of the `PHPMailer::addReplyTo()` function is to _fail_ (on the address validation call in `PHPMailer::addAnAddress()`) and return `false`.
In other words, this test was nonsensical as it tested for something which can, and should, never happen.
With this in mind, the test has been rewritten to reflect the *real* behaviour when either Mbstring or the `idn_to_ascii` function is not available.
This commit:
* Renames the `testDuplicateIDNRemoved()` method to `testNoDuplicateReplyToAddresses()`.
* Removes the call to `clearReplyTos()` at the start of the function as it is redundant. Each test gets a fresh instance of the PHPMailer class.
* Adds a number of additional assertions to the method to test the enqueuing and duplication removal in more depth.
* Ensures that all assertions are accompanied by a "failure" message so it can more easily be determined which assertion failed (in case of failure).
Includes adding `@covers` tags for the test.
This commit:
* Renames the `testConvertEncoding()` method to `testEnqueueAndAddIdnAddress()`.
* Removes the call to `clearReplyTos()` at the start of the function as it is redundant. Each test gets a fresh instance of the PHPMailer class.
* Adds a number of additional assertions to the method to test the enqueuing in more depth.
Includes adding `@covers` tags for the test.
... to test that the header gets set correctly when multiple reply-to addresses have been set, as well as when a reply-to address has been set without a name.
This commit:
* Renames the `testLowPriority()` method to `testReplyToInMessageHeader()`.
This test method was originally basically testing two things: the priority and the reply to header being set. For this class, it now just focusses on the reply to header, so letting the name reflect that.
* Enhances the test by actually testing that the reply-to header is correctly found in the fully composed message.
* Breaks out the test case to a data provider to allow for adding additional test cases more easily.
... to test that passing an invalid email address in combination with an instance of the `PHPMailer` class which was instantiated with `$exceptions = true` results in an exception.
Includes adding `@covers` tags for this specific method.
This commit:
* Adds two extra test cases.
* Adds handling to allow the "expected" registered values (and key) being different from the original input values.
This commit:
* Splits the primary tests previously contained in the `testAddressing()` method into two distinct test methods, each using a data provider to allow for adding additional test cases more easily.
* Enhances the tests by not only testing the return value of the `addReplyTo()` method, but also verifying that the `ReplyTo property has been set correctly and contains the expected information.
Includes adding `@covers` tags for these specific methods.
This commit moves the "set a property to null and then back" test to a separate test method.
Note: based on the code in the method, this test doesn't really add any value, but I also see no reason to remove it.
The `extension_loaded()` PHP function is sometimes disabled by shared hosts as a form of "security by obscurity".
This would lead to a fatal "Function not available" error.
With that in mind, replacing the `extension_loaded()` checks in the `PHPMailer::parseAddresses()` function with a check for the `MB_CASE_UPPER` constant being defined. This _should_ be reliable enough to determine whether the Mbstring extension is available, though has its own drawbacks as when the extension is not available, the constant _could_ be polyfilled by userland code (and sometimes is).
The `MB_CASE_UPPER` constant was chosen as it is one of the few Mbstring constants which is actually available cross-version in PHP 5.5-current.
Refs:
* https://www.php.net/manual/en/mbstring.constants.php
* https://php-legacy-docs.zend.com/manual/php5/en/mbstring.constants
... to test that passing an invalid email address in combination with an instance of the `PHPMailer` class which was instantiated with `$exceptions = true` results in an exception.
Includes reworking the `testSetFromFail()` method to a data provider and letting both the `testSetFromFail()` and the new `testInvalidAddressException()` method use the same data provider.
Make the failure test more comprehensive by verifying that when the method fails, the values for the `From`, `FromName` and `Sender` properties, _really_ haven't changed.
Based on the code in the method, any existing, previously set `Sender` should not be overruled, even when the `$auto` parameter is set to `true`.
This method tests that specific situation.
* Merges the two "success" tests from the `testAddressing()` and the `testAddressing2()` methods into one `testSetFromSuccess()` method.
* Adds additional assertions to more comprehensively verify that the method did what was expected, i.e. set the `From`, `FromName` and `Sender` properties.
* Maintains the same test cases.
* Makes it easier to add additional test cases in the future.
So far, these methods were only tested in the most perfunctory manner.
The additional tests this commit introduces, test all aspects of the methods as well as documents the current behaviour of the methods.
Take note of the "text merging"/readability issues for the SMTP error messages. There may be room for improvement there.
... by a much simpler test which effectively tests the same thing, i.e.:
* No errors to start with.
* Trigger an error.
* Verify that `PHPMailer::isError()` returns `true`.
* Verify that the error message is as expected.
What with the previous commit adding a requirement for the `Mbstring` extension to the existing tests, it becomes clear that the "Mbstring extension not available" code path was not covered by the tests.
This commit fixes that by:
* Adding two new test methods which explicitly expect the Mbstring extension to **not** be available.
* Changing the data provider "expected(Imap)" keys.
The `expected` key in the array will now be an array of arrays.
A `default` key can be used for when the output across configurations will be the same.
Any differences across configurations can be provided in separate sub-keys of the `expected` array, using the `native+mbstring`, `imap+mbstring`, `native--mbstring` and/or `imap--mbstring` keys, which match the four test methods which are now in place.
Additionally, an `native` and/or `imap` key can be used for setting the output expectations for the two native implementation or the two IMAP implementation tests, if the Mbstring extension makes no difference.
Follow up to #2266
Bug fix
In both "arms" (imap vs native implementation) of the `PHPMailer::parseAddresses()` method, the `mb_decode_mimeheader()` function is used to decoded a (utf-8) encoded name.
In the IMAP "arm", a check was in place for the Mbstring extension being available before using it. This check was missing from the "native implementation" "arm".
Existing Tests
This also means that both currently existing tests have a requirement for the MbString extension being available. This was previously not made explicit in the tests.
Fixed now.
... which should be handled correctly based on the code in the method under test.
With these additional test cases, the method now has 100% code coverage and is fully tested.
Using a regex assertion with arbitrary input data which is not regex escaped, makes this test suspect.
From the looks of it, the test _should_ be testing that the output is the _same_, so let's use that assertion.
This commit:
* Adds a new `testFailToAttach()` test method to test the behaviour of the `PHPMailer::AddStringEmbeddedImageTest()` method when the `PHPMailer` class has been instantiated with `$exceptions` disabled.
* This new test method uses the same data provider - introduced in the previous commit - as the `testFailToAttachException()` method.
This commit:
* Renames the `testStringEmbeddedImageEncodingException()` test to `testFailToAttachException()`.
* Reworks the test to use a data provider.
* Adds testing of the exception message to the `testFailToAttachException()` method.
This commit:
* Improves the test name and the description in the docblock.
* Replace a redundant condition and "forced" failure assertion with an assertion actually testing the result of the method call.
* Removes the redundant `return` - if an assertion fails, the rest of the code within the test method will not be executed anyway.
* Minor inline comment tweaks.
The actual "attaching" of the string attachment happens within `createBody()` which is called from `preSend()`, so this test doesn't actually need to call `send()`.
In a number of places in the code base I came across hard-code error messages, which were not set up to be translatable.
For those I found, I've now fixed this.
Note: I've not added the labels used for logging errors via the `PHPMailer:edebug()` method. If so desired, that could be added in a future iteration.
Existing translation files have not been updated. Translators may need to be pinged before the next release to update the translation(s) they maintain.
Includes a minor tweak to the PHPCS configuration to allow lines in language files to exceed the "120 chars line max" as using concatenation for text strings in translation files will not work with the current way of loading these files and the new "buggy PHP" message is a long text which would result in nearly all translation files throwing the warning.
So far, this method did not have dedicated tests.
The test file this commit introduces, tests nearly all aspects of the method as well as documents the current behaviour of the method.
There is one particular test case missing. This is annotated in the class docblock.
While this method is quite simple, testing it separately means that the tests for methods _using_ this method don't have to _also_ test the functioning of this method, which means they can be more focussed on their own logic.
So far, this method did not have dedicated tests.
The test file this commit introduces, tests all aspects of the method as well as documents the current behaviour of the method.
While this method is quite simple, testing it separately means that the tests for methods _using_ this method don't have to _also_ test the functioning of this method, which means they can be more focussed on their own logic.
This commit:
* Adds a new `testFailToAttach()` test method to test the behaviour of the `PHPMailer::addStringAttachment()` method when the `PHPMailer` class has been instantiated with `$exceptions` disabled.
* This new test method uses the same data provider - introduced in the previous commit - as the `testFailToAttachException()` method.
This commit:
* Renames the `testStringAttachmentEncodingException()` test to `testFailToAttachException()`.
* Reworks the test to use a data provider.
* Adds testing of the exception message to the `testFailToAttachException()` method.
The actual "attaching" of the string attachment happens within `createBody()` which is called from `preSend()`, so this test doesn't actually need to call `send()`.
This commit:
* Adds an `exceptionMessage` index to the `dataFailToAttach()` data provider.
* Renames the `testEmbeddedImageEncodingException()` method to `testFailToAttachException()`.
* Sets the `testFailToAttachException()` method up to use the `dataFailToAttach()` data provider.
* Adds testing of the exception message to the `testFailToAttachException()` method.
With this change, the "fail to attach" test cases are now fully tested for both a `PHPMailer` instance without exceptions enabled, as well as for an instance _with_ exceptions enabled.
The test code remaining in the `testEmbeddedImage()` constitutes one test.
This commit:
* Renames the test method and improves the description in the docblock.
* Removes the redundant call to `PHPMailer::clearAttachments()`.
This call was previously needed as multiple situations were being tested in one test method.
Now each test case has its own test method, the call to `PHPMailer::clearAttachments()` is no longer needed as each test method will receive a fresh, clean instance of the `PHPMailer` class.
* Replace a redundant condition and "forced" failure assertion with an assertion actually testing the result of the method call.
* Removes the redundant `return` - if an assertion fails, the rest of the code within the test method will not be executed anyway.
The test case when a file was attached without explicitly adding a filename wasn't actually being tested at all as no assertion was used.
This commit:
* Moves that particular test case to a separate test method.
* Adds relevant assertions to actually test the test case.
The "failure" case when a non-existent file was being passed, wasn't actually being tested at all as no assertion was used.
This commit:
* Moves the particular failure test case to a separate test method with a data provider (to allow for more failure test cases to be added).
* Uses an assertion on the call to `addEmbeddedImage()` to actually test that the method return a failure state.
* Verifies that no attachment for an inline image was added by adding a second assertion with a call to `PHPMailer::inlineImageExists()`.
The actual "attaching" of the images happens within `createBody()` which is called from `preSend()`, so this test doesn't actually need to call `send()`.
* Maintains (largely) the same test cases.
* Prevent one failing assertion hiding a potential second failure.
* Makes it easier to add additional test cases in the future.
Note:
This removes the intermittent calls to `clearCustomHeaders()` from this test. This is now tested via a separate method and as each test case will receive a fresh instance of the `PHPMailer` class, there is no need to clear the set custom headers between tests.
The line length adjustments are executed within `createBody()` which is called from `preSend()`, so this test doesn't actually need to call `send()`.
I also wonder if this test can be further improved by having more targeted tests for `PHPMailer::hasLineLongerThanMax()` and whether these more extensive tests can be included in a test class which is more targeted at the `createBody()` and/or `preSend()` methods, but that is for later.
Previously, the test didn't actually test whether the wordwrapping had been applied, just that the message was (pre)send successfully.
Changing the assertions to actually test that the wordwrapping has been correctly applied.
For this test, there is no need to actually try to _send_ the message, we just need to make sure that `setWordWrap()` is triggered, which can be done by calling `preSend()` instead of `send()`.
This new test method covers a range of cases where the `PHPMailer::punyencodeAddress()` method should (and does) return the original input value unchanged.
This test does not require the `mbstring` extension or `idn_to_ascii()` function to be available, which is why it has been set up as a separate test with a separate data provider.
The "fakefunctions" are all nice and dandy to get past the `idnSupported()` check, but if either of these functions is not _really_ available and therefore doesn't behave as expected, the test would still fail as the expected output of the `PHPMailer::punyencodeAddress()` function would not match.
In other words, this test should not use the `fakefunctions`, but should have a hard requirement for the `mbstring` extension (for the `mb_check_encoding()` and the `mb_convert_encoding()` function calls) and a check for the `idn_to_ascii()` function.
Follow up on 2389 and 2412
Now the base `TestCase` has been simplified and only presets the bare minimum of properties in the `PHPMailer` class, the `Utf8CharBoundaryTest` can actually use it.
The `PHPMailerTest` class contained a `testBootstrap()` method to verify that the `testbootstrap.php` file exists as the first test in the class.
As the order in which tests are run is not predefined, this is not reliable.
Additionally, the check for the `testbootstrap.php` file is checking a pre-requisite for tests using the `PHPMailer::send()` method, so it would be better to verify via a condition in the `set_up()`.
Now, using such a condition, there is choice: the test can either be marked as "skipped" when the `testbootstrap.php` file can not be found, or be marked as an "error".
As the tests _should_ run, skipping them would hide an error in the dev-user test setup, so showing these tests as errors seems more appropriate.
To that end, the `testBootstrap()` method has been removed and a check for the `testbootstrap.php` file has been added to the `SendTestCase::set_up()` which will now throw an appropriate `Exception` when the file is not found.
Oops... also removes a stray `parent::set_up()` at the start of the local `set_up()` which should have been removed in 218fd13c88
Note: as the `PHPMailer::generateID()` method is `protected`, a round-about way of testing this is needed, but this test does verify the functioning of the method.
Take note of the notes in the test docblock - the GH Actions scripts running CI, should make sure that each of these scenarios is encountered/tested.
This will be addressed in a separate PR at the end of this round of test changes.
Minor test tweaks:
* Add `@coversNothing` tag.
* Rename the first test (add a number).
* Use `@link` instead of `@see` for links.
* Minor comment punctuation.
The `TestCase::checkChanges()` method is a way of exposing what properties in the `PHPMailer` class have a changed value compared to their default value in a particular test situation. The method is used for debugging tests.
As things were, the `TestCase::checkChanges()` method would check against a limited set of hard-coded values to determine whether the default value of a property has been updated.
This is unstable as:
1. Default values may change in the `PHPMailer` class and the values within this method would need to be updated to match, which is easily forgotten.
2. New properties may be introduced in the `PHPMailer` class and be relevant to this debug changelog.
Again, it would require manually adding these new properties to this method to start tracking them.
3. Property values may be changed in the `set_up()` method, which would be a "known change" for a certain test.
In part such "expected" changes were taken into account in this method based on the previously hard-coded setting changes in `set_up()`.
With the logic for the property setting from the `set_up()` method now being more flexible, the pre-setting of properties having been reduced to the bare minimum, but also allowing individual test clases to set their own additional changes, keeping track of what is a "known" change by checking against hard-coded values is no longer stable.
With this in mind, I propose to make the `TestCase::checkChanges()` method dynamic.
To that end, this commit:
* Retrieves the default values of all properties of the `PHPMailer` class via the PHP native `get_class_vars()` function.
* Will automatically check for changes in *all* properties, with only a limited set of _exclusions_, effectively changing the changelog from an "inclusion list" to an "exclusion list".
A select list of properties is excluded from being listed in the changelog via the `TestCase::$changelogExclude` property.
See the inline documentation in the property for the reasoning behind excluding certain properties from the changelog.
* The value of static properties will always be compared to their default value as registered in the `TestCase::$PHPMailerStaticProps` method and will be listed when different.
_Note: as documented, this list has to be hard-coded due to Reflection (as well as `get_class_vars()`) not handling default values for static properties correctly._
* The value of non-static properties will be compared to both the known changes made in the `set_up()` method and if the property was not changed in `set_up()`, to their default value. The property will be listed in the changelog when the value is different from the "expected" value, i.e. not a known change from `set_up()` and not the default value.
In addition to this, the representation of the properties will now be created via `var_export()`, which allows for listing `null` and boolean values as well.
The `SendTestCase` gets the values of the properties to be set from the `testbootstrap.php` file.
This introduces a `private` property to map the field names used in `$_REQUEST` to the properties in the `PHPMailer` class and adds logic to the overloaded `set_up()` method to fill the `$propertyChanges` TestCase property. The actual setting of the properties in the `PHPMailer` instance is deferred to the underlying `TestCase` parent class.
Includes adding support for presetting the `bcc` value for feature completeness.
Overloading and/or adding to the `$propertyChanges` array from concrete test cases is, of course, supported, so if individual tests need additional presetting of properties, the same logic as mentioned in the previous commit can be used.
After some investigation, it turns out that barely any of these properties are actually needed for the `PHPMailer::preSend()` method to succeed.
This commit removes all presetting of properties for the PHPMailer instance created by the `PreSendTestCase`, save for the bare minimum.
Overloading and/or adding to the `$propertyChanges` array from concrete test cases is, of course, supported, so if individual tests need additional presetting of properties, the same logic as mentioned in the previous commit can be used.
This commit makes the property setting in the `TestCase::set_up()` more flexible by combining an overloadable property `$propertyChanges` and a `foreach` loop to set the actual property values.
Concrete test classes can either overload the `$propertyChanges` property with their own version or can add to the default setup using the following pattern:
```php
protected function set_up()
{
$this->propertyChanges['additional_key'] = 'value';
// Add more properties...
parent::set_up();
}
```
The `TestCase::set_up()` was setting quite a number of properties in the `PHPMailer` class.
This makes testing more difficult for the following reasons:
1. The tests can no longer presume the properties in the `PHPMailer` class will have their default values
This means that tests are not "transparent" (clearly show what is being tested), nor isolated (only target what is specifically being tested).
2. Any changes to the values set in the `set_up()` method may have a ripple effect and create a need for individual test expectations to be adjusted.
3. As the `set_up()` is changing a number of the properties using methods in the `PHPMailer()` class and methods called during the `set_up()` are included in code coverage visualizations, code coverage cannot fully be trusted and it is more difficult to verify that each piece of code has tests covering that code path.
With this in mind, I'm proposing splitting the `TestCase` into three distinct abstract `TestCase`s:
* A basic `TestCase` containing the utility methods and a minimal `set_up()` and `tear_down()`.
* A `PreSendTestCase` for use with tests using the `preSend()` method which requires a few properties to be set.
* A `SendTestCase` for use with tests actually testing the sending of mail using the `send()` method, which needs yet more properties and uses the `testbootstrap.php` file to retrieve the values of those variables.
This commit executes the initial split. Follow-on commits will streamline this further.
Includes adjusting the `TestCase` being extended for select existing unit test classes.
A `]` is a special character in a regex and should be escaped when the literal is intended. While it was not problematic in this case, it is still recommended to escape it.
... which should be handled correctly based on the code in the method under test.
With these additional test cases, the method now has 100% code coverage and is actually tested quite fully.
* Split the test into two tests with each a dedicated data provider
* Maintains the same test code and test cases.
* Makes it easier to add additional test cases in the future.
**Note**: the data set description may need some improvement, I've described them based on my best guess of what they were testing.
As this test does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
The postfix installation step fails regularly, resulting in failed CI builds which have to be restarted, while the failure is not due to anything in the PR.
This commit introduces a new action runner for the postfix install, which will automatically retry the install up to 3 times.
If it works as I expect it to, this should eliminate failed CI builds due to postfix installs erroring out.
Ref: https://github.com/marketplace/actions/retry-step
So far, this method did not have dedicated tests, though the `PHPMailerTest::testAttachmentNaming()` test covered this partially.
The test file this commit introduces, tests all aspects of the method as well as documents the current behaviour of the method.
Test cases largely inspired by the tests in the `PHPMailerTest::testAttachmentNaming()` method.
As this test does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
Note: this doesn't move the complete test from the original test file, just select parts of the test method.
The Codecov service is a way to monitor test vs code coverage of a project over time and allows for the code coverage % + delta to be reported in each PR.
This commits:
* Adds a Codecov configuration.
* Adds a convenience script to the `composer.json` file to run the tests with or without code coverage.
* Adds a new matrix variable to the GH Actions `test` workflow to run the tests with code coverage and send the results to the Codecov service.
Notes:
- This disables the code coverage reporting in the "normal" test runs, including disabling `xdebug` for those runs which should make them slightly faster.
- This splits the test runs into two sets:
* High/low PHP are being run with code coverage (and have been removed from the "normal" test run matrix).
* For all other PHP versions, the tests are being run without code coverage.
* Adds a badge to the README to show the current code coverage %.
PR 2373 changed the CI in such a way that coding standards errors will now be shown inline in the code of PRs.
With that change in place, having the information about running PHPCS in the pull request template has become redundant.
Follow up on 2384.
1. This test does not actually need a regular expression to test the mime message, an `assertStringContainsString()` assertion is sufficient.
2. Remove duplicate information from the `$expected` parameter. The `Content-Type: text/calendar; method=` part will always be the same. Only the method name will change.
Including test cases with:
* Spaces in the paths.
* Incomplete file paths.
Note: the "empty string" test may need looking at.... should this return an empty array instead ?
* Replace the existing test code with two new test methods using data providers.
* Maintains the same test code and test cases.
* Makes it easier to add additional test cases in the future.
As this test does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
Note: this doesn't move the complete test from the original test file, just select parts of the test method.
This commit:
* Add a new dependency on the PHP Parallel Lint package for fast PHP linting.
The PHP Parallel Lint package, in combination with the PHP Console Highlighter provides the following advantages in comparison with "plain" PHP linting:
- Higher speed due to the parallel processes.
- Improved usability by providing color coded syntax highlighting of found errors on the command-line.
- Integration with the `cs2pr` tool, allowing for the results of the lint command to be shown in-line in PRs.
* Adds a Composer `lint` script for easy access to the tool for devs, while making sure the correct command line parameters will be used.
The linting command as currently set up, will also check the example files for linting errors.
* Adds a GH Actions job for linting the code on the high/low supported PHP versions, one arbitrary interim version + an experimental build against PHP 8.1.
The `cs2pr` tool has been enabled and will show the results of the non-experimental lint runs in-line in PRs.
**Note**: For PHP 8.1, the `cs2pr` tool is not used as there is a known issue in the Parallel Lint tool with PHP 8.1 which breaks on the checkstyle reporting. There is already a [PR open](https://github.com/php-parallel-lint/PHP-Parallel-Lint/pull/64) to fix this upstream. Once this PR has been merged and a new version of Parallel Lint has been released, the separate step for PHP 8.1 linting can be removed.
* Makes the `test` job in the GHA workflow dependent on the `lint` job having passed...
... as the tests would fail anyway if there are linting errors.
Also adjusts the name of the `test` jobs to include the word "Test" so they can be easily distinguished from the Lint jobs.
Refs:
* https://github.com/php-parallel-lint/PHP-Parallel-Lint
The `xdebug` extension is already tagged as needed via the `coverage` setting, no need to add it to the `extensions` list.
---
Note: generally speaking, I personally normally don't pass an `extensions` list and allow the `setup-php` action to run with the default extensions, which is sufficient in most cases and would be sufficient here as well.
More than anything, I use the `extensions` key to _disable_ extensions for certain test runs, rather than enable them. Just something to consider.
The below documentation should give more insight.
Refs:
* https://github.com/shivammathur/setup-php/wiki/Php-extensions-loaded-on-ubuntu-18.04
* https://github.com/shivammathur/setup-php#heavy_plus_sign-php-extension-support
It is generally speaking a good idea to cache downloaded Composer packages between runs for performance reasons.
Now, this can be set up manually and would add two more steps to the scripts, or Ben's `composer-install` action can be used which will handle it all for you. The `composer-install` action is versatile and allows for passing additional parameters, so is perfectly suitable for this.
Ref: https://github.com/marketplace/actions/install-composer-dependencies
Running against stable/lowest dependencies is relevant when a package has runtime (non-dev) dependencies.
However, PHPMailer does not have runtime dependencies.
In other words, the `dependency-version` matrix key is redundant and unused, so we may as well remove it.
As this test does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
Note: this doesn't move the complete test from the original test file, just select parts of the test method.
As this test does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
Note: this doesn't move the complete tests from the original test file, just select parts of two test methods.
The `PHPMailer::preSend()` method may alter the value of the _`protect static`_ `PHPMailer::$LE` property.
a9978d2079/src/PHPMailer.php (L1494-L1504)
This means that tests which rely on the value of the property being the default, may start to fail unexpectedly depending on the _order_ in which tests are being run.
I.e. if the test relying on the default value is run first, all will be fine, however, if that same test is run _after_ a test which calls `preSend()` on a non-Windows box with PHP < 8.0 and with `$this->Mailer` set to `mail`, the test may fail unexpectedly.
This commit fixes that by expanding the previously introduced "resetting of static properties" logic.
This commit:
* Expands the list of static properties in the `TestCase` class to include the `PHPMailer::$LE` property.
* Introduces two new methods to the `TestCase` class:
- `resetStaticProperties()` to reset all static properties listed in the `TestCase::$PHPMailerStaticProps` property to their default values.
- `updateStaticProperty()` to change the value of one individual static property in a class.
* The `updateStaticProperty()` method was made `public static` to allow for tests which do not need an instantiated PHPMailer object and thus extend the `Yoast\PHPUnitPolyfills\TestCases\TestCase` directly to be able to use the method as well.
If any such tests would use the method, that particular test will be responsible for resetting the value back to the default after the test.
* The resetting of the static properties has also been moved from the `set_up()` to the `tear_down()` method.
As mentioned above, these static properties may be used by both tests extending this `TestCase` as well as test extending the `Yoast\PHPUnitPolyfills\TestCases\TestCase`. With that in mind, resetting _after_ each test will be more stable, as long as tests which extend the `Yoast\PHPUnitPolyfills\TestCases\TestCase` and would change static properties clean up after themselves.
**Note**: I'm not including the `PHPMailer::$IcalMethods` property in this change as AFAICS, this property is not being changed anywhere in the package code and as the property is `protected`, it cannot easily be changed from within a test either (and if it would be changed by a test, that test would be responsible for resetting it).
Also note: due to limitations in the `Reflection` extension, a hard-coded list of the default values of the static properties will need to be maintained in the `TestCase`. This cannot be helped as the `Reflection` extension does not have a way to accurately retrieve the default value of static properties in a PHP cross-version compatible manner.
The `PHPMailer::__construct()` method has an optional `$exceptions` property to throw exceptions on errors.
Up to now, the `PHPMailer` instance created by the `set_up()` would not pass any parameters, effectively instantiating `PHPMailer` without turning on exceptions.
In a test situation it may be useful for tests to test the behaviour of a method _with_ and _without_ the exceptions option and to test that certain exceptions are thrown and throw the correct message.
With that in mind, I'm introducing a `USE_EXCEPTIONS` class constant to the `TestCase` which can be overloaded in individual test classes and will be used by the `set_up()` method to determine whether it will be instantiated with exceptions or not.
Includes introducing an overload of the class constant in one of the test class for which it would seem appropriate at this time.
Note: this does mean that the `testDKIMSigningMail()` test will show as errored instead of failed if no SMTP connection could be made, but that IMO is the more correct status anyhow.
This allows for using the `setAddress()` method in a more consistent manner (where appropriate).
Includes introducing the use of the `setAddress()` function in a few select places.
Note: I do wonder whether this method should ever be used outside of `set_up()` and `tear_down()`, but that is for further discussion and outside the scope of this commit.
While this class does not (currently) _change_ the `PHPMailer::$validator` property, it does rely on the default value being the expected default.
By resetting the value of the property before and after the class, this is safeguarded for the current tests.
Public static properties changed by individual tests were not being reset to their default value prior to the next test being run.
This could influence the test results of subsequent tests as static properties are not automatically reset when a new instance of a class is instantiated. See: https://3v4l.org/8s1RB
Luckily this didn't aversely affect the tests so far, but should be safeguarded for the future.
There are only three static properties in the `PHPMailer` class, with only one of these being `public`.
This commit introduces a code snippet which will reset any static properties as listed in the `$PHPMailerStaticProps` property of the `TestCase` to the default value, as also set in the `$PHPMailerStaticProps` property, at the start of the `set_up()` which is run before each test using the `TestCase`.
For now, this is only relevant for the `ValidateAddressCustomValidatorTest` test and only when the `testSetDefaultValidatorToCustom()` test would fail before resetting the property. All the same, having the reset in place by default will ensure that future tests which change this property won't introduce side-effects to other tests.
... in part to document the behaviour of the function, in part to actually test it.
By the looks of it more tests are still needed though to raise the code coverage of this function to 100%.
I'd also recommend adding tests with different charset settings as the current tests are all based on the default charset (`CHARSET_ISO88591`).
Also note: the "empty string" test may need looking at.... is this a bug ?
* Maintains the existing test cases.
* Prevent one failing assertion hiding a potential second failure.
* Makes it easier to add additional test cases in the future.
As this test is marked _incomplete_, no further review of the test has been done and no `@covers` tag has been added.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move utf8CharBoundary test to own file
* Utf8CharBoundaryTest: reorganize to use data providers
This:
* Renames the test method and moves the description to the docblock.
* Decouples the test from the PHPMailer `TestCase` as it doesn't need the complete `set_up()` and `tear_down()`.
* Moves the test cases to a data provider.
* Adds a `@covers` tag.
* Utf8CharBoundaryTest: add `@todo` reminder
This really could do with some more test cases to properly cover all paths and branching in the method.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move address splitting tests to own file
As this test does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
* ParseAddressesTest: reorganize the testAddressSplitting() test
This commit:
* Adds two new test methods, one for testing address splitting using the PHPMailer native implementation, one for testing address splitting using the IMAP implementation.
Both these methods use the same data provider.
* Adds a PHPUnit `@requires` tag for the IMAP extension to the IMAP version of the test.
* Adds a type check `assertIsArray()` before doing the content checks on the method return value.
* Changes the tests to do a more detailed `assertSame()` test instead of the previous `assertCount()` and `assert[Not]Empty()` checks.
* Sets up the data provider to have the detailed output arrays for comparison.
The actual test cases in the rewritten test are the same as were previously being tested.
* ParseAddressesTest: replace remaining test by dataprovider entry
The `testImapParsedAddressList_parseAddress_returnsAddressArray()` and the `testImapParsedAddressList_parseAddress_returnsAddressArray_usingImap()` tests were largely duplicates of each other, with the only real difference being the value of the second parameter passed to the `PHPMailer::parseAddresses()` method (`$useimap`).
This commit:
* Removes these two tests in favour of adding the test case to the data provider added in the previous commit.
In effect, the same test case is still being tested just as thoroughly now, just with less (test) code.
* ParseAddressesTest: add two more (invalid) test cases to `dataAddressSplitting()`
* PHPMailer::parseAddresses(): bug fix
If invalid email addresses are passed to any of the IMAP functions - in this case to `imap_rfc822_parse_adrlist()` - IMAP may generate error notices or warnings.
If nothing is done with these, they will be displayed at PHP shutdown.
The notice will look something like this, for instance:
```
Notice: Unknown: Missing or invalid host name after @ (errflg=3) in Unknown on line 0
```
This bug was exposed by the newly added unit tests. You should be able to see the issue if you run the tests over the previous commit.
As PHPMailer is not interested in invalid addresses, we can just ignore these notices and errors, but we *do* have to _clear_ them to prevent the notices from being thrown.
Refs:
* https://www.php.net/manual/en/function.imap-rfc822-parse-adrlist.php
* https://www.php.net/manual/en/function.imap-errors.php
* https://github.com/ddeboer/imap/issues/308
* https://stackoverflow.com/questions/3378469/how-to-get-rid-of-error-messages-with-phps-imap-fetchstructure
* ParseAddressesTest: reorder the test cases in `dataAddressSplitting()`
... to a slightly more sane order for humans to follow and see at a glance what is being tested..
* ParseAddressesTest: add `@covers` tags
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move message ID test to own file
* GetLastMessageIDTest: use the correct parameter order
For PHPUnit assertions which expect an `$expected` and a `$result` parameter, the parameter order is always `( $expected, $result, ...).
While it may not seem important to use the correct parameter order for assertions doing a straight comparison, in actual fact, it is.
The PHPUnit output when the assertions fail expects this order and the failure message will be reversed if the parameters are passed in reversed order which leads to confusion and makes it more difficult to debug a failing test.
* GetLastMessageIDTest: split into separate tests
* GetLastMessageIDTest: reorganize to use data providers
While initially still only addressing the original test cases, using a data provider here will allow for adding additional test cases more easily.
* GetLastMessageIDTest: add additional tests
Adding additional tests based on the code this is supposed to be testing.
**Note**: I've put the test cases in the "valid"/"invalid" data providers based on the current reality.
Some of the test cases I've added to the "valid" data provider _might_ actually be **_invalid_**. If that's the case, the regex used on line 2554 needs adjusting to account for them (after which the test cases can be moved to the "invalid" data provider).
* GetLastMessageIDTest: add `@covers` tags
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move line length detected tests to own file
* HasLineLongerThanMaxTest: various test tweaks
Minor test tweaks:
* Add `@covers` tag.
* Inline comment punctuation.
* Minor code readability tweaks.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move ICal tests to own file
* ICalTest: reorganize to use data providers
These three tests were 99% duplicate code. Using a data provider removes the duplication, while the actual tests being run are still 100% the same, including same test count and assertion count.
* ICalTest: add additional tests
... to ensure all valid methods are checked.
Adding these extra tests is a breeze now with the data provider setup.
* ICalTest: various test tweaks
Minor test tweaks:
* Add `@covers` tag. **_<- is this correct ?_**
* Minor comment punctuation.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move OAuth test to own file
This test uses the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase` base class as it doesn't need the `set_up()` or `tear_down()` methods from the PHPMailer base test class.
* OAuthTest: various test tweaks
Minor test tweaks:
* Add `@covers` tags.
* Add "failure" messages to assertions.
* Minor comment punctuation.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move POP before SMTP tests to own file
This also removed the `$pids` property and handling from the PHPMailer base `TestCase` and moves this to the POP test class.
As this test now does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
* PopBeforeSmtpTest: move `@group` tags
... to the class level and remove them from the individual test functions.
* PopBeforeSmtpTest: various test tweaks
Minor test tweaks:
* Add `@covers` tag at class level.
* Inline comment punctuation.
* Minor code readability tweaks.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move DKIM related tests to own file
* DKIMTest: stabilize removal of private key file
When an assertion in a test fails, the rest of the code within the test method is no longer executed.
This means that for a test which - like three out of the five DKIM test - creates a file, the test "clean up" via `unlink()` is also no longer executed.
It also means that one test may - unintentionally - interfere with the results of another test due to the file still existing, while it is expected to have been removed.
Aside from that, the `$privatekeyfile` variable used in the various test is the same everywhere, but not consistently used in the tests, as the `'dkim_private.pem'` is hard-coded in multiple places, which decreases maintainability.
This commit fixes both these issues by:
* Declaring a class constant with the target private key file name for use in the test method.
* Implements use of this constant throughout the tests.
* Removes the `unlink()` call from the individual tests, in favour of executing it via the test `tear_down()`, which should still be executed, even when a test has failed.
Note: as the file also contains two tests which do not create the private key file, but for which the `tear_down()` would also be executed, the `unlink()` call is wrapped in a `file_exists()`.
* DKIMTest: move `@group` tags
... to the class level and remove them from the individual test functions.
* DKIMTest: add missing `@requires` tags
Three out of the five tests actually require the `openssl` extension. Only one was so marked.
* DKIMTest: various test tweaks
Minor test tweaks:
* Rename a test to a more specific name to allow for easier test filtering via PHPUnit.
* Add `@covers` tags (Needs review!)
* The `@see` tag is indented for code elements. For external links, the `@link` tag should be used.
* Inline comment punctuation.
* Minor code readability tweaks.
* DKIMTest: add two additional tests
... to cover the "open SSL" not available case.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move email transport tests to own file
* MailTransportTest: various test tweaks
Minor test tweaks:
* Add `@covers` tags (Needs review! - especially the `testMailSend()` method seems to do more than it should)
* Check if test skipping is necessary at the start of a test method.
* Add "failure message" for each assertion in tests with multiple assertions.
* Tidy up inline comments.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move email validation using custom validator test to own file
* ValidateAddressCustomValidatorTest: reorganize test
Split the test into three tests, each testing a specific situation and use a data provider for one of the tests.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: move email validation test to own file
As this test does not actually need an instantiated PHPMailer object, this class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` instead of the `PHPMailer\Test\TestCase`.
* ValidateAddressTest: reorganize to use data providers
The original test as was, would run through a number of arrays and keep track of fails/passes, only to use an assertion at the end to check that the list of "fails" was empty.
In addition to this, the original test also contained some additional assertions which would never be run if the earlier assertion would fail. (failing assertion possibly hiding more failing (or passing) assertions).
Using data providers for these kind of data array based tests, has a couple of advantages:
1. Each data set is counted as an individual test.
2. Each test can be set up to have only one assertion.
3. When a test for a data set fails, PHPUnit just moves on to the next data set, instead of failing the test and not examining the rest of the test cases.
With that in mind, this test has now been reorganized into multiple test functions, each with one or more data providers.
In addition to that:
* Each data set in a test provider is named after the email address it provides, with optionally a prefix to show which data provider it came from.
This has two advantages:
1. When using the `--testdox` runner, the output will list each test case by name.
2. When a test fails, instead of getting a "failed with data set 65" message, you now get a "failed with data set _data set name_" message, and as the data set name is the same as the email address value, it's easy to see which test case failed.
* Each assertion now has a "failure message" attached, as the default "true does not match false" message from PHPUnit is not very descriptive.
* ValidateAddressTest: enable two out of three of the unused data sets
The original test contained three additional data sets which were *not* being tested:
* `$invalidphp`
* `$validqandc`
* `$validipv6`
The `$invalidphp` data set has now been set up as a data provider and has been added to the `testInvalidAddresses()` test.
The `$validipv6` data set has now been set up as a data provider and has been added to the `testValidAddresses()` test.
And the `$validqandc` data set has been removed after consultation with synchro.
Note: there are six test in the `$validipv6` array which are currently failing. Those have been commented out to be addressed later.
* ValidateAddressTest: add `@todo`
While the tests in this class will show that the `PHPMailer::validateAddress()` is 100% covered by tests, the tests do **not** in actual fact test all functionality properly.
To that end, I've added a recommendation in a `@todo` at the top of the class to document how these tests could be further improved in a future iteration.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* PHPMailerLangTest: rename test class to `TranslationCompletenessTest`
As the test class has been moved to a separate directory, we may as well make the class name more descriptive of what the test class actually does.
* TranslationCompletenessTest: various test tweaks
Minor test tweaks:
* Move `@group` tag up to class level.
* Add a `@coversNothing` tag as this test is more a maintainer utility/package test than a test to cover functionality in code.
* Tidy up inline comments.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests/reorganize: add an abstract base testcase
As a first step towards reorganizing the tests, this commit:
* Creates an abstract base `TestCase` class which can be extended by concrete child test classes and holds the generic properties and helper methods for use throughout the tests.
Based on their use, the visibility of properties and methods have been adjusted for the new setup.
* Removes the generic property and helper method declarations from the concrete test class.
* Moves the `require` statement for the `validators.php` file to a `set_up_before_class()` method in the base `TestCase`.
* Tests/reorganize: define base directory in set_up_before_class
The `$this->INCLUDE_DIR` property which points to the project root directory does not change at any time during the test run, but was being redefined for every test in the `set_up()` method.
As this is in effect a _constant_ value, let's define it as a constant in the TestCase `set_up_before_class()` method instead.
Notes:
Both actions executed in the `set_up_before_class()` method are typically things for a test bootstrap file.
However, to allow for PHPUnit to be able to run from both a Composer install as well as a Phar file, without having to create custom autoloaders, it is simpler to have the `vendor/autoload.php` file as the bootstrap file as, in that case, PHPUnit will handle the loading order and prevent loading conflicting PHPUnit files from a Composer install when running via the Phar.
With this in mind, putting these actions in a `set_up_before_class()` method is a valid compromise.
* Tests/reorganize: move actual test files to subdirectories
... leaving the test root directory to only contain test utility files.
Note: I've added a second entry for the test generated files to the `.gitignore`. Adding this entry instead of replacing the entry allows for any existing generated files in contributor clones to continue to be ignored.
At a later point in time, it could be elected to remove the original entry, once all active contributors have updated their installs and removed any stray generated files from their `test` root directories.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* GH Actions: run on PRs and allow for manually triggering
Currently the workflow only ran on `push` events, which - as forks have to enable the workflows - means that PRs could be submitted without CI having been run and you'd only see the CI results on merge.
By adding the `pull_request` event, it is ensured that CI is always run within the main repo for pull requests. This also allows for branch protection to be enabled with "required statuses".
Additionally, triggering a workflow for a branch manually is not supported by default in GH Actions, but has to be explicitly allowed.
This is useful if, for instance, an external action script or composer dependency has broken.
Once a fix is available, failing builds for `master` or open PRs can be retriggered manually instead of having to be re-pushed to retrigger the workflow.
Ref: https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/
* GH Actions: report CS violations in the PR
Currently the PR template asks for people to run the CS tooling.
As the PHPCS tool is also run in the test workflow and this workflow - per the previous commit - will now also be run on pull requests, we can make life easier on contributors.
The cs2pr tool allows to display the results from an action run in checkstyle format in-line in the PR code view.
This commit enables this for PHPCS, which means that the code view will now show CS violations in the PR.
Ref: https://github.com/staabm/annotate-pull-request-from-checkstyle
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Tests: remove unused test bootstrap file
The PHPUnit config file requires the `vendor/autoload.php` file as the test bootstrap and this file is not referenced anywhere in the code base, so this is dead code.
* Tests: apply test method naming conventions
For tests to be picked up by PHPUnit automatically, the method should start with the prefix `test`.
For differently named tests, the `@test` annotation can be used to still mark a method as a test and get PHPUnit to run it.
As the vast majority of tests use the "prefix the method with `test`" convention, this changes the names of the few tests which did not comply with that convention and removes the `@test` annotations.
* Tests: use test skipping where appropriate
In this case, the condition being tested should never be `false`, so could possibly be removed.
All the same, if the condition _would_ result in a `false`, the test would be marked as "risky" as no assertions would be run by it.
This can be avoided by using the condition to set a test skip annotation, instead of wrapping the actual test code in the condition.
* Tests: use strict assertions
PHPUnit contains a variety of assertions and the ones available has been extended hugely over the years.
To have the most reliable tests, the most specific assertion should be used.
Most notably, this changes calls to `assertEquals()` to `assertSame()`, where `assertEquals()` does a loose type comparison `==` and `assertSame()` does a strict type `===` comparison.
The only real exception to this is when comparing two objects, as in that case, the objectID will not be the same, so those should still use `assertEquals()` - or the PHPUnit 9.4.0 `assertObjectEquals()` method for comparing value objects using a callback method in the ValueObject class.
* Tests: use the correct parameter order
For PHPUnit assertions which expect an `$expected` and a `$result` parameter, the parameter order is always `( $expected, $result, ...).
While it may not seem important to use the correct parameter order for assertions doing a straight comparison, in actual fact, it is.
The PHPUnit output when the assertions fail expects this order and the failure message will be reversed if the parameters are passed in reversed order which leads to confusion and makes it more difficult to debug a failing test.
* Tests: use static closures
... when the closure doesn't use `$this`.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
The `docs` workflow to deploy the GH Pages website is run on pushes to `master`, but that includes pushes to `master` in forks, which obviously can't deploy to the GH Pages site.
This means that in forks (and there are nearly 9000 of them), this workflow will always fail, while in reality, it shouldn't be run in the first place.
So, I'd like to propose making this small change, which _should_ prevent the `docs` workflow from being run on forks.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
From the PHP 8.1 changelog:
> `htmlspecialchars()`, `htmlentities()`, `htmlspecialchars_decode()`,
>` html_entity_decode()` and `get_html_translation_table()` now use
> `ENT_QUOTES | ENT_SUBSTITUTE` rather than `ENT_COMPAT` by default. This means
> that `'` is escaped to `'` while previously it was left alone.
> Additionally, malformed UTF-8 will be replaced by a Unicode substitution
> character, instead of resulting in an empty string.
Ref: 28a1a6be08/UPGRADING (L149-L154)
If effect this means that the output of the above mentioned functions may be different depending on the PHP version and the passed text string, unless the `$flags` parameter is explicitly passed.
This patch proposes to add the old default to the one affected function call in PHPMailer, though most other flag combination choices would be just as valid, as long as a `$flags` parameter is passed.
Ref: https://www.php.net/manual/en/function.htmlspecialchars.php
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
On PHP 8.1, passing `null` to `trim()` generates a `trim(): Passing null to parameter #1 ($string) of type string is deprecated` notice.
As the `$value` is optional and may not even get set via the `name:value` parsing, the code as was, was causing this notice to be thrown.
This fix is covered by the existing unit tests and was exposed when running the tests on PHP 8.1 with `error_reporting` set to `E_ALL`.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
While working on 2363, I noticed that the code sniffer run was using PHP 8.0, but Composer still allowed for a PHP_CodeSniffer version to be installed which is not fully compatible with PHP 8.0.
Note: there are still two known incompatibilities with PHP 8.0 in PHPCS 3.6.0, but everything else has been fixed.
Ref: https://github.com/squizlabs/php_codesniffer/releases
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* GH Actions: start testing against PHP 8.1
The first alpha of PHP 8.1 has been released, so now seems like a good time to start running the tests against PHP 8.1.
For now, I've configured it to allow builds against PHP 8.1 to fail, while PHP 8.1 is still unstable.
Also: PHPUnit doesn't officially support PHP 8.1 yet, so to install PHPUnit 9.x on PHP 8.1, we need to use `--ignore-platform-reqs`, as otherwise PHPUnit 4.8 would be installed (last PHPUnit version without strict PHP version constraints).
* GH Actions: set error reporting to E_ALL
Turns out the default setting for `error_reporting` used by the SetupPHP action is `error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT` and `display_errors` is set to `Off`.
For the purposes of CI, I'd recommend running with `E_ALL` and `display_errors=On` to ensure **all** PHP notices are shown.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
A typical SMTP transaction ID for Haraka looks like this:
```
250 Message Queued (14490C56-76FB-4932-A59B-A8299DB2B693.1)
```
This regex will detect and extract this transaction ID
* Add travis jobs on ppc64le
* Make the tests PHPUnit cross version compatible + test on PHP 8 (#2202)
* .gitignore: ignore files created during a test run
* Tests: make config cross-version compatible
PHPUnit config file:
* Add `backupGlobals="true"`.
The default value for this setting changed in PHPUnit 6 from `true` to `false`. By explicitly setting it to `true`, the existing behaviour is maintained.
* Remove the `logIncompleteSkipped` directive which is no longer supported.
* Remove a number of directives which use the default values and for which the defaults have not changed across PHPUnit versions.
* Remove the space in the testsuite name to make it more easily usable on the command line.
* Make sure that all src files are taken into consideration when calculating code coverage.
* Add XSD schema reference.
Note: the config as-is will now validate for PHPUnit 4.4-9.2. For PHPUnit 9.3, the code coverage terminology has changed, though the "old" configuration is still supported.
If needs be (PHPUnit 10), the config can be updated on the fly by using `--migrate-configuration`.
Other:
* Ignore a locally overloaded PHPUnit config file using the standard `phpunit.xml` file name.
Question: why does the config file not use the standard `phpunit.xml.dist` file name ? That would allow for running the tests without command line arguments.
That file can still be overloaded locally by a `phpunit.xml` file.
* Composer: add dependency on the PHPUnit Polyfills package
* Adds a dev dependency to the `yoast/phpunit-polyfills` package.
* As that package already requires and manages the installable versions for PHPUnit, remove this as an explicit requirement from `require-dev` in favour of letting the PHPUnit Polyfills package manage the versions.
This will update the supported PHPUnit versions from `^4.8 || ^5.7` to `^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0`.
Note: the supported versions for PHPUnit 4.x and 5.x are very specific and restrictive as it specifically targets the versions in which the "forward compatible" alias files for the namespaced classes are available.
This new package adds the following features:
* Polyfills for various PHPUnit cross-version changes.
* Basic test case and test listener.
Refs:
* https://github.com/Yoast/PHPUnit-Polyfills/
Includes adding the PHPUnit cache file, which is automatically created in PHPUnit 8 and 9, to the `.gitignore` file.
* Tests: switch over to use the Yoast\PHPUnitPolyfills\TestCases\TestCase
This switches the parent class of the test classes over `TestCase` from the PHPUnit native TestCase to the `Yoast\PHPUnitPolyfills\TestCases\TestCase`.
The Yoast `TestCase`:
* Provides cross-version compatibility using snake case fixture method names instead of the PHPUnit native camelCase names.
This switch over includes:
* Renaming the `setUp()` and `tearDown()` methods to, respectively, `set_up()` and `tear_down()` in various test classes for PHPUnit cross-version compatibility.
* Tests: switch over to use the Yoast Polyfill TestListenerDefaultImplementation
This switches `DebugLogTestListener` class over to using the PHPUnit 9 default implementation pattern in combination with using the `TestListenerDefaultImplementation` from the PHPUnit Polyfill library for the cross-version compatibility layer for the TestListener.
The Yoast `TestListenerDefaultImplementation`:
* Provides cross-version compatibility using snake case method names instead of the PHPUnit native camelCase names.
This switch over includes:
* Renaming the template methods used in the `DebugLogTestListener` to use their snake_case variant and removes the type declarations.
* Tests: switch out `assertInternalType()`
... in favour of the more specific assertion(s) as introduced in PHPUnit 7.5.0.
The new assertions are automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: switch out `assert[Not]Contains()` with string haystacks
... in favour of the string specific `assertString[Not]ContainsString() assertion(s) as introduced in PHPUnit 7.5.0.
The new assertions are automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: switch out `@expectException` annotations
... in favour of method calls to the `TestCase::expectException()` method as introduced in PHPUnit 5.2.0.
The new method and its variants are automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: switch out `assertRegExp()`
... in favour of the renamed `Assert::assertMatchesRegularExpression() as introduced in PHPUnit 9.1.0.
The new assertion is automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: mark a test as incomplete
As per the comment in the docblock:
> Needs a connection to a server that supports this auth mechanism, so commented out by default.
As the test cannot currently be executed succesfully, we may as well skip it with a meaningful message.
* Tests: mark a test as not performing assertions
... to prevent it from being marked as risky.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* CS
* 6.2.0
* Use fully-qualified constant names for PHP versions (#2203)
This is a micro performance improvement. When PHP opcode generates opcodes, it can remove PHP version-specific `if` blocks if it sees `PHP_MAJOR_VERSION` or `PHP_VERSION_ID`. However, if the code belongs to a namespace, it cannot make that optimization because the code under namespace can declare the same constants.
This PR updates such PHP constants to be fully-qualified (with back-slash), which enables PHP to make the improvement.
To compare, this is the VLD opcode without fully-qualified constant names:
```php
namespace X;
if (\PHP_VERSION_ID < 80000) {
echo "hi";
}
```
-->
```
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
5 0 E > FETCH_CONSTANT ~0 'X%5CPHP_MAJOR_VERSION'
1 IS_SMALLER_OR_EQUAL ~1 ~0, 8
2 > JMPZ ~1, ->4
6 3 > ECHO 'hi'
7 4 > > RETURN 1
```
The same snippet, with the fully-qualified constants, PHP can simply eliminate and optimize the `if` block:
```
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
5 0 E > > JMPZ <true>, ->2
6 1 > ECHO 'hi'
7 2 > > RETURN 1
```
* Update Slovak translations (#2204)
* Update phpmailer.lang-sk.php
* Update phpmailer.lang-sk.php
* Update phpmailer.lang-cs.php (#2205)
Co-authored-by: Juliette <663378+jrfnl@users.noreply.github.com>
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
Co-authored-by: Marcus Bointon <marcus@synchromedia.co.uk>
Co-authored-by: Ayesh Karunaratne <Ayesh@users.noreply.github.com>
Co-authored-by: Róbert Kelčák <RobiNN1@users.noreply.github.com>
This is a micro performance improvement. When PHP opcode generates opcodes, it can remove PHP version-specific `if` blocks if it sees `PHP_MAJOR_VERSION` or `PHP_VERSION_ID`. However, if the code belongs to a namespace, it cannot make that optimization because the code under namespace can declare the same constants.
This PR updates such PHP constants to be fully-qualified (with back-slash), which enables PHP to make the improvement.
To compare, this is the VLD opcode without fully-qualified constant names:
```php
namespace X;
if (\PHP_VERSION_ID < 80000) {
echo "hi";
}
```
-->
```
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
5 0 E > FETCH_CONSTANT ~0 'X%5CPHP_MAJOR_VERSION'
1 IS_SMALLER_OR_EQUAL ~1 ~0, 8
2 > JMPZ ~1, ->4
6 3 > ECHO 'hi'
7 4 > > RETURN 1
```
The same snippet, with the fully-qualified constants, PHP can simply eliminate and optimize the `if` block:
```
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
5 0 E > > JMPZ <true>, ->2
6 1 > ECHO 'hi'
7 2 > > RETURN 1
```
* .gitignore: ignore files created during a test run
* Tests: make config cross-version compatible
PHPUnit config file:
* Add `backupGlobals="true"`.
The default value for this setting changed in PHPUnit 6 from `true` to `false`. By explicitly setting it to `true`, the existing behaviour is maintained.
* Remove the `logIncompleteSkipped` directive which is no longer supported.
* Remove a number of directives which use the default values and for which the defaults have not changed across PHPUnit versions.
* Remove the space in the testsuite name to make it more easily usable on the command line.
* Make sure that all src files are taken into consideration when calculating code coverage.
* Add XSD schema reference.
Note: the config as-is will now validate for PHPUnit 4.4-9.2. For PHPUnit 9.3, the code coverage terminology has changed, though the "old" configuration is still supported.
If needs be (PHPUnit 10), the config can be updated on the fly by using `--migrate-configuration`.
Other:
* Ignore a locally overloaded PHPUnit config file using the standard `phpunit.xml` file name.
Question: why does the config file not use the standard `phpunit.xml.dist` file name ? That would allow for running the tests without command line arguments.
That file can still be overloaded locally by a `phpunit.xml` file.
* Composer: add dependency on the PHPUnit Polyfills package
* Adds a dev dependency to the `yoast/phpunit-polyfills` package.
* As that package already requires and manages the installable versions for PHPUnit, remove this as an explicit requirement from `require-dev` in favour of letting the PHPUnit Polyfills package manage the versions.
This will update the supported PHPUnit versions from `^4.8 || ^5.7` to `^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0`.
Note: the supported versions for PHPUnit 4.x and 5.x are very specific and restrictive as it specifically targets the versions in which the "forward compatible" alias files for the namespaced classes are available.
This new package adds the following features:
* Polyfills for various PHPUnit cross-version changes.
* Basic test case and test listener.
Refs:
* https://github.com/Yoast/PHPUnit-Polyfills/
Includes adding the PHPUnit cache file, which is automatically created in PHPUnit 8 and 9, to the `.gitignore` file.
* Tests: switch over to use the Yoast\PHPUnitPolyfills\TestCases\TestCase
This switches the parent class of the test classes over `TestCase` from the PHPUnit native TestCase to the `Yoast\PHPUnitPolyfills\TestCases\TestCase`.
The Yoast `TestCase`:
* Provides cross-version compatibility using snake case fixture method names instead of the PHPUnit native camelCase names.
This switch over includes:
* Renaming the `setUp()` and `tearDown()` methods to, respectively, `set_up()` and `tear_down()` in various test classes for PHPUnit cross-version compatibility.
* Tests: switch over to use the Yoast Polyfill TestListenerDefaultImplementation
This switches `DebugLogTestListener` class over to using the PHPUnit 9 default implementation pattern in combination with using the `TestListenerDefaultImplementation` from the PHPUnit Polyfill library for the cross-version compatibility layer for the TestListener.
The Yoast `TestListenerDefaultImplementation`:
* Provides cross-version compatibility using snake case method names instead of the PHPUnit native camelCase names.
This switch over includes:
* Renaming the template methods used in the `DebugLogTestListener` to use their snake_case variant and removes the type declarations.
* Tests: switch out `assertInternalType()`
... in favour of the more specific assertion(s) as introduced in PHPUnit 7.5.0.
The new assertions are automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: switch out `assert[Not]Contains()` with string haystacks
... in favour of the string specific `assertString[Not]ContainsString() assertion(s) as introduced in PHPUnit 7.5.0.
The new assertions are automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: switch out `@expectException` annotations
... in favour of method calls to the `TestCase::expectException()` method as introduced in PHPUnit 5.2.0.
The new method and its variants are automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: switch out `assertRegExp()`
... in favour of the renamed `Assert::assertMatchesRegularExpression() as introduced in PHPUnit 9.1.0.
The new assertion is automatically polyfilled via the PHPUnit Polyfill repo as this test class extends the `Yoast\PHPUnitPolyfills\TestCases\TestCase` test case.
* Tests: mark a test as incomplete
As per the comment in the docblock:
> Needs a connection to a server that supports this auth mechanism, so commented out by default.
As the test cannot currently be executed succesfully, we may as well skip it with a meaningful message.
* Tests: mark a test as not performing assertions
... to prevent it from being marked as risky.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Travis: reorder the config
No functional changes.
* Travis: remove redundant env variable
The "CS check" is being run in its own stage with its own script, so there is no need for this environment variable anymore.
* Travis: disable Xdebug before running Composer
Composer can be slower when Xdebug is enabled, so when Xdebug isn't needed, let's disable it _before_ running Composer.
* Travis: remove duplicate build definitions
The default stage is the `test` stage and all builds defined in the `php` key, possibly combined with an `env` key, will automatically be build in the `test` stage.
No need to define them twice.
The only builds to declare explicitly in `jobs` are those which need custom `env`, `dist`, `script`s etc.
So:
* Remove those builds which don't need anything "custom" from the `jobs` key.
* Remove the one build which _does_ need something "custom" from the `php` key.
* Travis: re-enable testing against PHP 5.5
PHP 5.5 is not available on the default `xenial` distro, but by explicitly telling Travis to use the `trusty` distro, we can still run the tests against PHP 5.5.
* Travis: run code coverage in a separate stage
Code coverage builds are "expensive" builds and if any of the tests would be failing, we are wasting time and resources by running them.
So, let's move the coverage build(s) to a separate stage, which will only run once the `test` stage has passed.
As this is now a separate stage, we don't need to set an environment variable to toggle code coverage on/off anymore. We can base conditions on the Travis stage name.
Let's also run code coverage for PHP 5.5, so both high and low PHP code coverage output can be combined for the most realistic results, taking PHP version specific conditions into account.
To make this work, we do need to tell Scrutinizer to expect reports from two separate code coverage runs.
And if I'm editing that file anyway, let's remove the redundant `filter` setting for a Scrutinizer native code coverage run which is turned off anyway.
* Tests: fix test failing on PHP 5.5
... due to the use of a PHPUnit method which is not available on PHPUnit 4.x.
These kind of things will be addressed better in a future PR, but this will get the build passing for now.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* PHPCS: rename config file
... to `phpcs.xml.dist` to allow devs to locally overload the file by using a `.phpcs.xml` or `phpcs.xml` file, to, for instance, test out some new rules.
Includes:
* Adding the local overload files to `.gitignore`.
* Adding the standard config file to `.gitattributes`.
* PHPCS: scan missing file
The `get_oauth_token.php` file in the project root seems to have been overlooked when configuring the PHPCS ruleset.
Fixed now by adding it to the file to be scanned.
Includes minor fixes to make the file comply with the configured standard.
* CS: fix two files
Two minor CS fixes.
* PHPCS: miscellaneous changes
* Don't fix the PHPCS/external standards version restraints.
* Add the PHPCS cache file to `.gitignore`.
* Removing the no longer existent `.php_cs` file from `.gitattributes`.
Co-authored-by: jrfnl <jrfnl@users.noreply.github.com>
* Add ajax contact form
* Allow ajax contact form to work if javascript is disabled.
Co-authored-by: Suhayb Alghutaymil <s@sgh.sa>
Co-authored-by: Sohib AlGotimel <sgotimel@citc.gov.sa>
FakeSMTP's repo is read-only and hasn't been updated since 2016, so I removed it. The link to FakeEmail's repo was pointing at a fork that also hasn't been updated since 2016, so i changed the link to one that has more activity. Also smtp4dev has been moved to Github and works on Linux, so I updated its entry to reflect that.
* DKIM tweaks, see #1962, #1964, #1965
* Don't use the `l` tag in DKIM signature, fixes#1964
* CS
* CS
* Fix order of operations, see #1962
* Remove trailing line break from output of `DKIM_Add()`, see #1962
* Let isValidHost() determine validness of host
- PHP way of doing so (https://github.com/php/php-src/pull/826)
- Improved and shortened regexp a bit
* Give more specific debug message in case host(entry) is invalid
* Rewrite filter_var checks
* Host [[<ipv6>]] is not valid
INTL_IDNA_VARIANT_UTS46 exists from ICU 4.6, which has been released in
March 2011. However, some recent installations might have older ICU
versions (even with PHP 7+). In this situation `INTL_IDNA_VARIANT_UTS46`
will not exist and PHP will transform the constant into a string,
resulting in the following error:
Warning: idn_to_ascii() expects parameter 3 to be integer, string
given in /some/path/to/a/file.php on line XX
This commit allows to fallback to INTL_IDNA_VARIANT_2003 if it's needed
and possible. This variable is deprecated from PHP 7.2 and will be
removed in PHP 8, so we take care to test its existence as well.
References:
- https://github.com/FreshRSS/FreshRSS/pull/2680
- https://github.com/PrestaShop/PrestaShop/pull/11995
- https://wiki.php.net/rfc/deprecate-and-remove-intl_idna_variant_2003
* Always Q-encode headers exceeding maximum length
Previously, headers exceeding the maximum line length without
any special characters were only folded. This lead to problems
with long filenames (#1469) and long headers in general (#1525).
Now, long headers are always Q-encoded (and still folded).
* Use ASCII as Q-encoding charset if applicable
Previously, headers were Q-encoded using the message
charset, e.g. UTF-8. This is excessive for ASCII
values, as it requires a unicode engine.
Now, we use ASCII if we only find 7-bit characters.
* Separate header encoding from encoding selection
* Use ASCII for B-encoding as well
* Refactor max line length calculation
Previously, we calculated the maximum
line length for header encoding both
for B- and Q-encoding, even though
they share the same limits.
Now, we calculate these once for both.
* Create phpmailer.lang-af.php
Afrikaans PHPMailer language file this can be easily verified using google translate except 'connect_host' => 'SMTP-fout: kon nie aan SMTP-gasheer koppel nie.', which is better said like 'SMTP-fout: kon nie aan SMTP-verbind nie.'
* Update phpmailer.lang-af.php
addOrEnqueueAnAddress() throws a PHPMailer\Exception, but addAddress(),
addCC(), addBCC() and addReplyTo() are neither catching it nor
indicating that they are throwing it.
This causes IDE inspections to flag a warning.
* php://temp streams instead of temp files
@TODO would be nice to use php://temp streams here > done
* remove space
* Update PHPMailer.php
remove space
* [fix] fix test cas
fix test case
- testBCCAddressing
* [fix] fix test case testSmtpConnect
* [fix] improve test case about coverage
* [fix] invalid address
* [add] test case for setSMTPInstance
* [add] Add OAuth test case
* [add] createbody test case added
* [add] add case validateaddress test
* [add] attachmentExists test case
* [add] add test case getMailMIME
* [fix] remove not need line
* [fix] fix codes for github comment
* [fix] fix code style for travis.
* [fix] fix code style for travis.
* [fix] fix code style for travis.
* [fix] fix code style for travis.
* [fix] fix code style for travis.
* [fix] fix miss type.
* [fix] fix test case of testMailSend
* [fix] fix test case of testSmtpConnect
* [fix] remove not needed line.
* [fix] remove not need space.
* [fix] fix test case of sendMail
* [fix] fix test case
* [fix] fix test case smtpconnect
* [fix] fix test case of smtpconnect
* [fix] fix test case of smtpconnect
* [fix] fix test case of smtpconnect
* Changed in DKIM signature
- Add flag for using copiedHeaderFields
- config for using extra headers in DKIM signature
* Cleanups after Review
- Don't use masked ampersands in url
- Remove test related artifacts after run
* Complete docs
* Hindi translations for php mailer
I directly uploaded to GitHub, because my fork is a legacy version now and I am having merge conflicts now
* updated hindi language
updated last missing translation and removed comment
* Update README.md
Added links to the recommended optional libraries.
Changed order of "Upgrading from 5.2" and "Legacy versions", i think this way is more logical to read.
Updated language quantity in the "Localization" section.
* Update README.md
Corrected review
* Update README.md
* Update phpmailer.lang-es.php
Added missing point to make grammatically correct and consistent with the other translations.
Many of the PHP extensions used by PHPMailer are optional, in one of
two senses. Either there is a fallback if the extension is not
present, or the use of the extension is avoidable entirely. For
example, there is a fallback parser if "imap_rfc822_parse_adrlist" is
not available, and OpenSSL is not needed unless encryption is used;
therefore neither are hard dependencies.
The filter extension, on the other hand, is unavoidable. It is used
unconditionally, with no fallbacks, in (for example) the "isValidHost"
function. This commit adds "ext-filter" to composer.json, to document
the dependency and to help out composer users.
Closes: https://github.com/PHPMailer/PHPMailer/issues/1298
Adding the missing translation.
Due to the flexibility of Esperanto, there is actually many ways of saying "Extension missing". I selected this one, even tho it's not the literal translation (what would rather be more like "Manki etendo" as the "missing" is used as an adjective, not as a verb), because I think this way is much more understandable.
* use instanceof instead of is_a
is_a is deprecated since 5.0.0 in favour of instanceof operator
http://php.net/is_a
this allows using class aliases instead of strings
* do not assign null to class properties
that is default behaviour of php engine
* use stripos instead of stristr
* use faster $array[]= $value instead of array_push
* add SuspiciousAssignmentsInspection noinspection to shut up ide
* use call_user_func with defined args
* avoid overwriting $error parameter
* apply more yoda-style
Adds the address kind (From, Sender, ConfirmReadingTo) instead of word punyEncode which is internal to PHPMailer and not so helpful.
Similarly, removes word addAnAddress (internal) but keeps only address kind (to, cc, bcc, Reply-To).
Upgrade guide and changelog say that idnSupported() is now static, but it actually isn't.
Probably this PR should wait v6.1 or other version where breaking changes are OK.
* SMTP Callback
Add an additional parameter to doCallback, to allow providing additional
data to caller.
Use this feature in the case of smtp send to return the
smtp_transaction_id in the callback.
* Add a case in smtp_transaction_id_patterns for Amazon SES
* textual adjustment
* Add PHP-CS-Fixer checks
* Remove composer.lock to early catch FC break on CI
* Travis: cache dependencies
* Enable code-coverage only for latest build
* Remove Symfony-specific PHP-Doc rules
* Apply coding-standards fix
Improve & fix data URL handling
Remove old class loader from phpunit bootstrap
Improve test coverage
Remove some unnecessary trailing line breaks from attachment MIME structure
Non-security issues and pull requests are no longer being accepted for the legacy PHPMailer 5.2 branch. Migrate to PHPMailer 6.0 (or later) and report your issue there.
about: If you've found a bug in PHPMailer, this is the right place to report it
title: ''
labels: ''
assignees: ''
---
Please check these things before submitting your issue:
- [ ] *Read the error message* you're seeing - it often tells you what is wrong, and may contain useful links & instructions
- [ ] Make sure you're using the latest version of PHPMailer
- [ ] Check that your problem is not dealt with in [the troubleshooting guide](https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting), especially if you're having problems connecting to Gmail or GoDaddy
- [ ] Include sufficient code to reproduce your problem
- [ ] If you're having an SMTP issue, include the debug output generated with `SMTPDebug = 2` set
- [ ] If you have a question about how to use PHPMailer (rather than reporting a bug in it), tag a question on Stack Overflow with `phpmailer`, but [**search first**](https://stackoverflow.com/questions/tagged/phpmailer)!
Non-security issues and pull requests are no longer being accepted for the legacy PHPMailer 5.2 branch. Migrate to PHPMailer 6.0 (or later) and report your issue there.
- Probably the world's most popular code for sending email from PHP!
- Used by many open-source projects: WordPress, Drupal, 1CRM, SugarCRM, Yii, Joomla! and many more
- Integrated SMTP support - send without a local mail server
- Send emails with multiple TOs, CCs, BCCs and REPLY-TOs
- Integrated SMTP support – send without a local mail server
- Send emails with multiple To, CC, BCC, and Reply-to addresses
- Multipart/alternative emails for mail clients that do not read HTML email
- Add attachments, including inline
- Support for UTF-8 content and 8bit, base64, binary, and quoted-printable encodings
- SMTP authentication with LOGIN, PLAIN, NTLM, CRAM-MD5 and Google's XOAUTH2 mechanisms over SSL and TLS transports
- Error messages in 47 languages!
- Full UTF-8 support when using servers that support `SMTPUTF8`.
- Support for iCal events in multiparts and attachments
- SMTP authentication with `LOGIN`, `PLAIN`, `CRAM-MD5`, and `XOAUTH2` mechanisms over SMTPS and SMTP+STARTTLS transports
- Validates email addresses automatically
- Protects against header injection attacks
- Error messages in over 50 languages!
- DKIM and S/MIME signing support
- Compatible with PHP 5.0 and later
- Compatible with PHP 5.5 and later, including PHP 8.5
- Namespaced to prevent name clashes
- Much more!
## Why you might need it
Many PHP developers need to send email from their code. The only PHP function that supports this directly is [`mail()`](https://www.php.net/manual/en/function.mail.php). However, it does not provide any assistance for making use of popular features such as authentication, HTML messages, and attachments.
Many PHP developers utilize email in their code. The only PHP function that supports this is the `mail()` function. However, it does not provide any assistance for making use of popular features such as HTML-based emails and attachments.
Formatting email correctly is surprisingly difficult. There are myriad overlapping (and conflicting) standards, requiring tight adherence to horribly complicated formatting and encoding rules – the vast majority of code that you'll find online that uses the `mail()` function directly is just plain wrong, if not unsafe!
Formatting email correctly is surprisingly difficult. There are myriad overlapping RFCs, requiring tight adherence to horribly complicated formatting and encoding rules - the vast majority of code that you'll find online that uses the `mail()` function directly is just plain wrong!
*Please* don't be tempted to do it yourself - if you don't use PHPMailer, there are many other excellent libraries that you should look at before rolling your own - try SwiftMailer, Zend_Mail, eZcomponents etc.
The PHP `mail()` function usually sends via a local mail server, typically fronted by a `sendmail` binary on Linux, BSD, and macOS platforms, however, Windows usually doesn't include a local mail server; PHPMailer's integrated SMTP client allows email sending on all platforms without needing a local mail server. Be aware though, that the `mail()` function should be avoided when possible; it's both faster and [safer](https://exploitbox.io/paper/Pwning-PHP-Mail-Function-For-Fun-And-RCE.html) to use SMTP to localhost.
The PHP `mail()` function usually sends via a local mail server, typically fronted by a `sendmail` binary on Linux, BSD and OS X platforms, however, Windows usually doesn't include a local mail server; PHPMailer's integrated SMTP implementation allows email sending on Windows platforms without a local mail server.
*Please* don't be tempted to do it yourself – if you don't use PHPMailer, there are many other excellent libraries that
you should look at before rolling your own. Try [Symfony Mailer](https://symfony.com/doc/current/mailer.html), [Laminas/Mail](https://docs.laminas.dev/laminas-mail/), [ZetaComponents](https://github.com/zetacomponents/Mail), etc.
## License
This software is distributed under the [LGPL 2.1](http://www.gnu.org/licenses/lgpl-2.1.html) license. Please read LICENSE for information on the
software availability and distribution.
This software is distributed under the [LGPL 2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) license, along with the [GPL Cooperation Commitment](https://gplcc.github.io/gplcc/). Please read [LICENSE](https://github.com/PHPMailer/PHPMailer/blob/master/LICENSE) for information on the software availability and distribution.
## Installation & loading
PHPMailer is available via [Composer/Packagist](https://packagist.org/packages/phpmailer/phpmailer) (using semantic versioning), so just add this line to your `composer.json` file:
PHPMailer is available on [Packagist](https://packagist.org/packages/phpmailer/phpmailer) (using semantic versioning), and installation via [Composer](https://getcomposer.org) is the recommended way to install PHPMailer. Just add this line to your `composer.json` file:
```json
"phpmailer/phpmailer": "~5.2"
"phpmailer/phpmailer": "^7.0.0"
```
or
or run
```sh
composer require phpmailer/phpmailer
```
If you want to use the Gmail XOAUTH2 authentication class, you will also need to add a dependency on the `league/oauth2-google` package.
Note that the `vendor` folder and the `vendor/autoload.php` script are generated by Composer; they are not part of PHPMailer.
Alternatively, copy the contents of the PHPMailer folder into one of the `include_path` directories specified in your PHP configuration. If you don't speak git or just want a tarball, click the 'zip' button at the top of the page in GitHub.
If you want to use XOAUTH2 authentication, you will also need to add a dependency on the `league/oauth2-client` and appropriate service adapters package in your `composer.json`, or take a look at
by @decomplexity's [SendOauth2 wrapper](https://github.com/decomplexity/SendOauth2), especially if you're using Microsoft services.
If you're not using composer's autoloader, PHPMailer provides an SPL-compatible autoloader, and that is the preferred way of loading the library - just `require '/path/to/PHPMailerAutoload.php';` and everything should work. The autoloader does not throw errors if it can't find classes so it prepends itself to the SPL list, allowing your own (or your framework's) autoloader to catch errors. SPL autoloading was introduced in PHP 5.1.0, so if you are using a version older than that you will need to require/include each class manually.
Alternatively, if you're not using Composer, you
can [download PHPMailer as a zip file](https://github.com/PHPMailer/PHPMailer/archive/master.zip), (note that docs and examples are not included in the zip file), then copy the contents of the PHPMailer folder into one of the `include_path` directories specified in your PHP configuration and load each class file manually:
PHPMailer does *not* declare a namespace because namespaces were only introduced in PHP 5.3.
```php
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
If you want to use Google's XOAUTH2 authentication mechanism, you need to be running at least PHP 5.4, and load the dependencies listed in `composer.json`.
require 'path/to/PHPMailer/src/Exception.php';
require 'path/to/PHPMailer/src/PHPMailer.php';
require 'path/to/PHPMailer/src/SMTP.php';
```
If you're not using the `SMTP` class explicitly (you're probably not), you don't need a `use` line for it. Even if you're not using exceptions, you do still need to load the `Exception` class as it is used internally.
## Legacy versions
PHPMailer 5.2 (which is compatible with PHP 5.0 — 7.0) is no longer supported, even for security updates. You will find the latest version of 5.2 in the [5.2-stable branch](https://github.com/PHPMailer/PHPMailer/tree/5.2-stable). If you're using PHP 5.5 or later (which you should be), switch to the 6.x releases.
### Upgrading from 5.2
The biggest changes are that source files are now in the `src/` folder, and PHPMailer now declares the namespace `PHPMailer\PHPMailer`. This has several important effects – [read the upgrade guide](https://github.com/PHPMailer/PHPMailer/tree/master/UPGRADING.md) for more details.
### Minimal installation
While installing the entire package manually or with composer is simple, convenient and reliable, you may want to include only vital files in your project. At the very least you will need [class.phpmailer.php](https://github.com/PHPMailer/PHPMailer/tree/master/class.phpmailer.php). If you're using SMTP, you'll need [class.smtp.php](https://github.com/PHPMailer/PHPMailer/tree/master/class.smtp.php), and if you're using POP-before SMTP, you'll need [class.pop3.php](https://github.com/PHPMailer/PHPMailer/tree/master/class.pop3.php). For all of these, we recommend you use [the autoloader](https://github.com/PHPMailer/PHPMailer/tree/master/PHPMailerAutoload.php) too as otherwise you will either have to `require` all classes manually or use some other autoloader. You can skip the [language](https://github.com/PHPMailer/PHPMailer/tree/master/language/) folder if you're not showing errors to users and can make do with English-only errors. You may need the additional classes in the [extras](extras/) folder if you are using those features, including NTLM authentication and ics generation. If you're using Google XOAUTH2 you will need `class.phpmaileroauth.php` and `class.oauth.php` classes too, as well as the composer dependencies.
While installing the entire package manually or with Composer is simple, convenient, and reliable, you may want to include only vital files in your project. At the very least you will need [src/PHPMailer.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/PHPMailer.php). If you're using SMTP, you'll need [src/SMTP.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/SMTP.php), and if you're using POP-before SMTP (*very* unlikely!), you'll need [src/POP3.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/POP3.php). You can skip the [language](https://github.com/PHPMailer/PHPMailer/tree/master/language/) folder if you're not showing errors to users and can make do with English-only errors. If you're using XOAUTH2 you will need [src/OAuth.php](https://github.com/PHPMailer/PHPMailer/tree/master/src/OAuth.php) as well as the Composer dependencies for the services you wish to authenticate with. Really, it's much easier to use Composer!
## A Simple Example
```php
<?php
require 'PHPMailerAutoload.php';
//Import PHPMailer classes into the global namespace
//These must be at the top of your script, not inside a function
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
$mail = new PHPMailer;
//Load Composer's autoloader (created by composer, not included with PHPMailer)
$mail->addAttachment('/tmp/image.jpg', 'new.jpg'); //Optional name
$mail->Subject = 'Here is the subject';
$mail->Body = 'This is the HTML message body <b>in bold!</b>';
$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
//Content
$mail->isHTML(true); //Set email format to HTML
$mail->Subject = 'Here is the subject';
$mail->Body = 'This is the HTML message body <b>in bold!</b>';
$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';
if(!$mail->send()) {
echo 'Message could not be sent.';
echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
$mail->send();
echo 'Message has been sent';
} catch (Exception $e) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
```
You'll find plenty more to play with in the [examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) folder.
You'll find plenty to play with in the [examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) folder, which covers many common scenarios including sending through Gmail, building contact forms, sending to mailing lists, and more.
If you are re-using the instance (e.g. when sending to a mailing list), you may need to clear the recipient list to avoid sending duplicate messages. See [the mailing list example](https://github.com/PHPMailer/PHPMailer/blob/master/examples/mailing_list.phps) for further guidance.
That's it. You should now be ready to use PHPMailer!
## Localization
PHPMailer defaults to English, but in the [language](https://github.com/PHPMailer/PHPMailer/tree/master/language/) folder you'll find numerous (46 at the time of writing!) translations for PHPMailer error messages that you may encounter. Their filenames contain [ISO 639-1](http://en.wikipedia.org/wiki/ISO_639-1) language code for the translations, for example `fr` for French. To specify a language, you need to tell PHPMailer which one to use, like this:
PHPMailer defaults to English, but in the [language](https://github.com/PHPMailer/PHPMailer/tree/master/language/) folder, you'll find many translations for PHPMailer error messages that you may encounter. Their filenames contain [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code for the translations, for example `fr` for French. To specify a language, you need to tell PHPMailer which one to use, like this:
We welcome corrections and new languages - if you're looking for corrections to do, run the [phpmailerLangTest.php](https://github.com/PHPMailer/PHPMailer/tree/master/test/phpmailerLangTest.php) script in the tests folder and it will show any missing translations.
We welcome corrections and new languages – if you're looking for corrections, run the [Language/TranslationCompletenessTest.php](https://github.com/PHPMailer/PHPMailer/blob/master/test/Language/TranslationCompletenessTest.php) script in the tests folder and it will show any missing translations.
## Documentation
Start reading at the [GitHub wiki](https://github.com/PHPMailer/PHPMailer/wiki). If you're having trouble, head for [the troubleshooting guide](https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting) as it's frequently updated.
Examples of how to use PHPMailer for common scenarios can be found in the [examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) folder. If you're looking for a good starting point, we recommend you start with [the Gmail example](https://github.com/PHPMailer/PHPMailer/tree/master/examples/gmail.phps).
There are tips and a troubleshooting guide in the [GitHub wiki](https://github.com/PHPMailer/PHPMailer/wiki). If you're having trouble, this should be the first place you look as it's the most frequently updated.
To reduce PHPMailer's deployed code footprint, examples are not included if you load PHPMailer via Composer or via [GitHub's zip file download](https://github.com/PHPMailer/PHPMailer/archive/master.zip), so you'll need to either clone the git repository or use the above links to get to the examples directly.
Complete generated API documentation is [available online](http://phpmailer.github.io/PHPMailer/).
Complete generated API documentation is [available online](https://phpmailer.github.io/PHPMailer/).
You'll find some basic user-level docs in the [docs](docs/) folder, and you can generate complete API-level documentation using the [generatedocs.sh](https://github.com/PHPMailer/PHPMailer/tree/master/docs/generatedocs.sh) shell script in the docs folder, though you'll need to install [PHPDocumentor](http://www.phpdoc.org) first. You may find [the unit tests](https://github.com/PHPMailer/PHPMailer/tree/master/test/phpmailerTest.php) a good source of how to do various operations such as encryption.
You can generate complete API-level documentation by running `phpdoc` in the top-level folder, and documentation will appear in the `docs` folder, though you'll need to have [PHPDocumentor](https://www.phpdoc.org) installed. You may find [the unit tests](https://github.com/PHPMailer/PHPMailer/blob/master/test/PHPMailer/PHPMailerTest.php) a good reference for how to do various operations such as encryption.
If the documentation doesn't cover what you need, search the [many questions on Stack Overflow](http://stackoverflow.com/questions/tagged/phpmailer), and before you ask a question about "SMTP Error: Could not connect to SMTP host.", [read the troubleshooting guide](https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting).
If the documentation doesn't cover what you need, search the [many questions on Stack Overflow](https://stackoverflow.com/questions/tagged/phpmailer), and before you ask a question about "SMTP Error: Could not connect to SMTP host.", [read the troubleshooting guide](https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting).
## Tests
[PHPMailer tests](https://github.com/PHPMailer/PHPMailer/tree/master/test/) use PHPUnit 9, with [a polyfill](https://github.com/Yoast/PHPUnit-Polyfills) to let 9-style tests run on older PHPUnit and PHP versions.
There is a PHPUnit test script in the [test](https://github.com/PHPMailer/PHPMailer/tree/master/test/) folder.
If this isn't passing, is there something you can do to help?
## Security
Please disclose any vulnerabilities found responsibly – report security issues to the maintainers privately.
Please disclose any vulnerabilities found responsibly - report any security problems found to the maintainers privately.
PHPMailer versions prior to 5.2.22 (released January 9th 2017) have a local file disclosure vulnerability, [CVE-2017-5223](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-5223). If content passed into `msgHTML()` is sourced from unfiltered user input, relative paths can map to absolute local file paths and added as attachments. Also note that `addAttachment` (just like `file_get_contents`, `passthru`, `unlink`, etc) should not be passed user-sourced params either! Reported by Yongxiang Li of Asiasecurity.
PHPMailer versions prior to 5.2.20 (released December 28th 2016) are vulnerable to [CVE-2016-10045](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10045) a remote code execution vulnerability, responsibly reported by [Dawid Golunski](https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10045-Vuln-Patch-Bypass.html), and patched by Paul Buonopane (@Zenexer).
PHPMailer versions prior to 5.2.18 (released December 2016) are vulnerable to [CVE-2016-10033](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10033) a critical remote code execution vulnerability, responsibly reported by [Dawid Golunski](http://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html).
See [SECURITY](https://github.com/PHPMailer/PHPMailer/tree/master/SECURITY.md) for more detail on security issues.
See [SECURITY](https://github.com/PHPMailer/PHPMailer/tree/master/SECURITY.md) and [PHPMailer's security advisories on GitHub](https://github.com/PHPMailer/PHPMailer/security).
## Contributing
Please submit bug reports, suggestions, and pull requests to the [GitHub issue tracker](https://github.com/PHPMailer/PHPMailer/issues).
Please submit bug reports, suggestions and pull requests to the [GitHub issue tracker](https://github.com/PHPMailer/PHPMailer/issues).
We're particularly interested in fixing edge cases, expanding test coverage, and updating translations.
We're particularly interested in fixing edge-cases, expanding test coverage and updating translations.
If you found a mistake in the docs, or want to add something, go ahead and amend the wiki – anyone can edit it.
With the move to the PHPMailer GitHub organisation, you'll need to update any remote URLs referencing the old GitHub location with a command like this from within your clone:
If you have git clones from prior to the move to the PHPMailer GitHub organisation, you'll need to update any remote URLs referencing the old GitHub location with a command like this from within your clone:
Please *don't* use the SourceForge or Google Code projects any more.
Please *don't* use the SourceForge or Google Code projects any more; they are obsolete and no longer maintained.
## Sponsorship
Development time and resources for PHPMailer are provided by [Smartmessages.net](https://info.smartmessages.net/), the world's only privacy-first email marketing system.
Development time and resources for PHPMailer are provided by [Smartmessages.net](https://info.smartmessages.net/), a powerful email marketing system.
Donations are very welcome, whether in beer 🍺, T-shirts 👕, or cold, hard cash 💰. Sponsorship through GitHub is a simple and convenient way to say "thank you" to PHPMailer's maintainers and contributors – just click the "Sponsor" button [on the project page](https://github.com/PHPMailer/PHPMailer). If your company uses PHPMailer, consider taking part in Tidelift's enterprise support programme.
Other contributions are gladly received, whether in beer 🍺, T-shirts 👕, Amazon wishlist raids, or cold, hard cash 💰.
## PHPMailer For Enterprise
Available as part of the Tidelift Subscription.
The maintainers of PHPMailer and thousands of other packages are working with Tidelift to deliver commercial
support and maintenance for the open-source packages you use to build your applications. Save time, reduce risk, and
improve code health, while paying the maintainers of the exact packages you
Please disclose any vulnerabilities found responsibly - report any security problems found to the maintainers privately.
Please disclose any security issues or vulnerabilities found through [Tidelift's coordinated disclosure system](https://tidelift.com/security) or to the maintainers privately.
PHPMailer versions prior to 5.2.24 (released July 26th 2017) have an XSS vulnerability in one of the code examples, [CVE-2017-11503](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-11503). The `code_generator.phps` example did not filter user input prior to output. This file is distributed with a `.phps` extension, so it it not normally executable unless it is explicitly renamed, so it is safe by default. There was also an undisclosed potential XSS vulnerability in the default exception handler (unused by default). Patches for both issues kindly provided by Patrick Monnerat of the Fedora Project.
PHPMailer 6.4.1 and earlier contain a vulnerability that can result in untrusted code being called (if such code is injected into the host project's scope by other means). If the `$patternselect` parameter to `validateAddress()` is set to `'php'` (the default, defined by `PHPMailer::$validator`), and the global namespace contains a function called `php`, it will be called in preference to the built-in validator of the same name. Mitigated in PHPMailer 6.5.0 by denying the use of simple strings as validator function names. Recorded as [CVE-2021-3603](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2021-3603). Reported by [Vikrant Singh Chauhan](mailto:vi@hackberry.xyz) via [huntr.dev](https://www.huntr.dev/).
PHPMailer versions 6.4.1 and earlier contain a possible remote code execution vulnerability through the `$lang_path` parameter of the `setLanguage()` method. If the `$lang_path` parameter is passed unfiltered from user input, it can be set to [a UNC path](https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths), and if an attacker is also able to persuade the server to load a file from that UNC path, a script file under their control may be executed. This vulnerability only applies to systems that resolve UNC paths, typically only Microsoft Windows.
PHPMailer 6.5.0 mitigates this by no longer treating translation files as PHP code, but by parsing their text content directly. This approach avoids the possibility of executing unknown code while retaining backward compatibility. This isn't ideal, so the current translation format is deprecated and will be replaced in the next major release. Recorded as [CVE-2021-34551](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2021-34551). Reported by [Jilin Diting Information Technology Co., Ltd](https://listensec.com) via Tidelift.
PHPMailer versions between 6.1.8 and 6.4.0 contain a regression of the earlier CVE-2018-19296 object injection vulnerability as a result of [a fix for Windows UNC paths in 6.1.8](https://github.com/PHPMailer/PHPMailer/commit/e2e07a355ee8ff36aba21d0242c5950c56e4c6f9). Recorded as [CVE-2020-36326](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-36326). Reported by Fariskhi Vidyan via Tidelift. 6.4.1 fixes this issue, and also enforces stricter checks for URL schemes in local path contexts.
PHPMailer versions 6.1.5 and earlier contain an output escaping bug that occurs in `Content-Type` and `Content-Disposition` when filenames passed into `addAttachment` and other methods that accept attachment names contain double quote characters, in contravention of RFC822 3.4.1. No specific vulnerability has been found relating to this, but it could allow file attachments to bypass attachment filters that are based on matching filename extensions. Recorded as [CVE-2020-13625](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-13625). Reported by Elar Lang of Clarified Security.
PHPMailer versions prior to 6.0.6 and 5.2.27 are vulnerable to an object injection attack by passing `phar://` paths into `addAttachment()` and other functions that may receive unfiltered local paths, possibly leading to RCE. Recorded as [CVE-2018-19296](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2018-19296). See [this article](https://knasmueller.net/5-answers-about-php-phar-exploitation) for more info on this type of vulnerability. Mitigated by blocking the use of paths containing URL-protocol style prefixes such as `phar://`. Reported by Sehun Oh of cyberone.kr.
PHPMailer versions prior to 5.2.24 (released July 26th 2017) have an XSS vulnerability in one of the code examples, [CVE-2017-11503](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-11503). The `code_generator.phps` example did not filter user input prior to output. This file is distributed with a `.phps` extension, so it is not normally executable unless it is explicitly renamed, and the file is not included when PHPMailer is loaded through composer, so it is safe by default. There was also an undisclosed potential XSS vulnerability in the default exception handler (unused by default). Patches for both issues kindly provided by Patrick Monnerat of the Fedora Project.
PHPMailer versions prior to 5.2.22 (released January 9th 2017) have a local file disclosure vulnerability, [CVE-2017-5223](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-5223). If content passed into `msgHTML()` is sourced from unfiltered user input, relative paths can map to absolute local file paths and added as attachments. Also note that `addAttachment` (just like `file_get_contents`, `passthru`, `unlink`, etc) should not be passed user-sourced params either! Reported by Yongxiang Li of Asiasecurity.
PHPMailer versions prior to 5.2.20 (released December 28th 2016) are vulnerable to [CVE-2016-10045](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10045) a remote code execution vulnerability, responsibly reported by [Dawid Golunski](https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10045-Vuln-Patch-Bypass.html), and patched by Paul Buonopane (@Zenexer).
PHPMailer versions prior to 5.2.18 (released December 2016) are vulnerable to [CVE-2016-10033](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10033) a remote code execution vulnerability, responsibly reported by [Dawid Golunski](http://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html).
PHPMailer versions prior to 5.2.18 (released December 2016) are vulnerable to [CVE-2016-10033](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2016-10033) a remote code execution vulnerability, responsibly reported by [Dawid Golunski](https://legalhackers.com/advisories/PHPMailer-Exploit-Remote-Code-Exec-CVE-2016-10033-Vuln.html).
PHPMailer versions prior to 5.2.14 (released November 2015) are vulnerable to [CVE-2015-8476](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2015-8476) an SMTP CRLF injection bug permitting arbitrary message sending.
For most of its existence, SMTP has been a 7-bit channel, only supporting US-ASCII characters. This has been a problem for many languages, especially those that use non-Latin scripts, and has led to the development of various workarounds.
The first major improvement, introduced in 1994 in [RFC 1652](https://www.rfc-editor.org/rfc/rfc1652) and extended in 2011 in [RFC 6152](https://www.rfc-editor.org/rfc/rfc6152), was the addition of the `8BITMIME` SMTP extension, which allowed raw 8-bit data to be included in message bodies sent over SMTP.
This allowed the message *contents* to contain 8-bit data, including things like UTF-8 text, even though the SMTP protocol itself was still firmly 7-bit. This worked by having the server switch to 8-bit after the headers, and then back to 7-bit after the completion of a `DATA` command.
From 1996, messages could support [RFC 2047 encoding](https://www.rfc-editor.org/rfc/rfc2047), which permitted inserting characters from any character set into header *values* (but not names), but only by encoding them in somewhat unreadable ways to allow them to survive passage through a 7-bit channel. An example with a subject of "Schrödinger's cat" would be:
```
Subject: =?utf-8?Q=Schr=C3=B6dinger=92s_Cat?=
```
Here the accented `ö` is encoded as `=C3=B6`, which is the UTF-8 encoding of the 2-byte character, and the whole thing is wrapped in `=?utf-8?Q?` to indicate that it uses the UTF-8 charset and `quoted-printable` encoding. This is a bit of a hack, and not very human-friendly, but it works.
Similarly, 8-bit message bodies could be encoded using the same `quoted-printable` and `base64` content transfer encoding (CTE) schemes, which preserved the 8-bit content while encoding it in a format that could survive transmission through a 7-bit channel.
Domain names were originally also stuck in a 7-bit world, actually even more constrained to only a subset of the US-ASCII character set. But of course, many people want to have domains in their own language/script. Internationalized domain name (IDN) permitted this, using yet another complex encoding scheme called punycode, defined for domain names in 2003 in [RFC 3492](https://www.rfc-editor.org/rfc/rfc3492). This finally allowed the domain part (after the `@`) of email addresses to contain UTF-8, though it was actually an illusion preserved by email client applications. For example, an address of
`user@café.example.com` translates to
`user@xn--caf-dma.example.com` in punycode, rendering it mostly unreadable, but 7-bit friendly, and remaining compatible with email clients that don't know about IDN.
The one remaining part of email that could not handle UTF-8 is the local part of email addresses (the part before the `@`).
I've only mentioned UTF-8 here, but most of these approaches also allowed other character sets that were popular, such as [the ISO-8859 family](https://en.wikipedia.org/wiki/ISO/IEC_8859). However, UTF-8 solves so many problems that these other character sets are gradually falling out of favour, as UTF-8 can support all languages.
This patchwork of overlapping approaches has served us well, but we have to admit that it's a mess.
## SMTPUTF8
`SMTPUTF8` is another SMTP extension, defined in [RFC 6531](https://www.rfc-editor.org/rfc/rfc6531) in 2012. This essentially solves the whole problem, allowing the entire SMTP conversation — commands, headers, and message bodies — to be sent in raw, unencoded UTF-8.
But there's a problem with this approach: adoption. If you send a UTF-8 message to a recipient whose mail server doesn't support this format, the sender has to somehow downgrade the message to make it survive a transition to 7-bit. This is a hard problem to solve, especially since there is no way to make a 7-bit system support UTF-8 in the local parts of addresses. This downgrade problem is what held up the adoption of `SMTPUTF8` in PHPMailer for many years, but in that time the *de facto* approach has become to simply fail in that situation, and tell the recipient it's time they upgraded their mail server 😅.
The vast majority of large email providers (gmail, Yahoo, Microsoft, etc), mail servers (postfix, exim, IIS, etc), and mail clients (Apple Mail, Outlook, Thunderbird, etc) now all support SMTPUTF8, so the need for backward compatibility is no longer what it was.
## SMTPUTF8 in PHPMailer
Several other PHP email libraries have implemented a halfway solution to `SMTPUTF8`, adding only the ability to support UTF-8 in email addresses, not elsewhere in the protocol. I wanted PHPMailer to do it "the right way", and this has taken much longer. PHPMailer now supports UTF-8 everywhere, and does not need to use transfer or header encodings for UTF-8 text when connecting to an `SMTPUTF8`-capable mail server.
This support is handled automatically: if you add an email address that requires UTF-8, PHPMailer will use UTF-8 for everything. If not, it will fall back to 7-bit and encode the message as necessary.
The one place you will need to be careful is in the selection of the address validator. By default, PHPMailer uses PHP's built-in `filter_var` validator, which does not allow UTF-8 email addresses. When PHPMailer spots that you have submitted a UTF-8 address, but have not altered the default validator, it will automatically switch to using a UTF-8-compatible validator. As soon as you do this, any SMTP connection you make will *require* that the server you connect to supports `SMTPUTF8`. You can select this validator explicitly by setting `PHPMailer::$validator = 'eai'` (an acronym for Email Address Internationalization).
### Postfix gotcha
Postfix has supported `SMTPUTF8` for a long time, but it has a peculiarity that it does not always advertise that it does so. However, rather surprisingly, if you use UTF-8 in the conversation, it will work anyway.
PHPMailer 6.0 is a major update, breaking backward compatibility.
If you're in doubt about how you should be using PHPMailer 6, take a look at the examples as they have all been updated to work in a PHPMailer 6.0 style.
## PHP Version
PHPMailer 6.0 requires PHP 5.5 or later, and is fully compatible with PHP versions all the way up to 8.4. PHPMailer 5.2 supported PHP 5.0 and upwards, so if you need to run on a legacy PHP version, see the [PHPMailer 5.2-stable branch on Github](https://github.com/PHPMailer/PHPMailer/tree/5.2-stable), but bear in mind that this branch is no longer maintained.
## Loading PHPMailer
The single biggest change will be in the way that you load PHPMailer. In earlier versions you may have done this:
```php
require 'PHPMailerAutoload.php';
```
or
```php
require 'class.phpmailer.php';
require 'class.smtp.php';
```
We recommend that you load PHPMailer via composer, using its standard autoloader, which you probably won't need to load if you're using it already, but in case you're not, you will need to do this instead:
```php
require 'vendor/autoload.php';
```
If you're not using composer, you can still load the classes manually, depending on what you're using:
```php
require 'src/PHPMailer.php';
require 'src/SMTP.php';
require 'src/Exception.php';
```
## Namespace
PHPMailer 6 uses a [namespace](https://www.php.net/manual/en/language.namespaces.rationale.php) of `PHPMailer\PHPMailer`, because it's the PHPMailer project within the PHPMailer organisation. You **must** import (with a `use` statement) classes you're using explicitly into your own namespace, or reference them absolutely in the global namespace - all the examples do this. This means the fully-qualified name of the main PHPMailer class is `PHPMailer\PHPMailer\PHPMailer`, which is a bit of a mouthful, but there's no harm in it! If you are using other PHPMailer classes explicitly (such as `SMTP` or `Exception`), you will need to import them into your namespace too.
For example you might create an instance like this:
```php
<?php
namespace MyProject;
use PHPMailer\PHPMailer\PHPMailer;
require 'vendor/autoload.php';
$mail = new PHPMailer;
...
```
or alternatively, using a fully qualified name:
```php
<?php
namespace MyProject;
require 'vendor/autoload.php';
$mail = new PHPMailer\PHPMailer\PHPMailer;
...
```
Note that `use` statements apply *only* to the file they appear in (they are local aliases), so if an included file contains `use` statements, it will not import the namespaced classes into the file you're including from.
## Namespaced exceptions
PHPMailer now uses its own namespaced `Exception` class, so if you were previously catching exceptions of type `phpmailerException` (or subclasses of that), you will need to update them to use the PHPMailer namespace, and make any existing `Exception` references use the global namespace, i.e. `\Exception`. If your original code was:
```php
try {
...
} catch (phpmailerException $e) {
echo $e->errorMessage();
} catch (Exception $e) {
echo $e->getMessage();
}
```
Convert it to:
```php
use PHPMailer\PHPMailer\Exception;
...
try {
...
} catch (Exception $e) {
echo $e->errorMessage();
} catch (\Exception $e) {
echo $e->getMessage();
}
```
## OAuth2 Support
The OAuth2 implementation has been completely redesigned using the [OAuth2 packages](https://oauth2-client.thephpleague.com) from the [League of extraordinary packages](https://thephpleague.com), providing support for many more OAuth services, and you'll need to update your code if you were using OAuth in 5.2. See [the examples](https://github.com/PHPMailer/PHPMailer/tree/master/examples) and documentation in the [PHPMailer wiki](https://github.com/PHPMailer/PHPMailer/wiki).
## Extras
Additional classes previously bundled in the `Extras` folder (such as htmlfilter and EasyPeasyICS) have been removed - use equivalent packages from [packagist.org](https://packagist.org) instead.
## Other upgrade changes
See the changelog for full details.
* File structure simplified, classes live in the `src/` folder
* Most statically called functions now use the `static` keyword instead of `self`, so it's possible to override static internal functions in subclasses, for example `validateAddress()`
* Complete RFC standardisation on CRLF (`\r\n`) line breaks by default:
* `PHPMailer::$LE` still exists, but all uses of it are changed to `static::$LE` for easier overriding. It may be changed to `\n` automatically when sending via `mail()` on UNIX-like OSs
* `PHPMailer::CRLF` line ending constant removed
* The length of the line break is no longer used in line length calculations
* Similar changes to line break handling in SMTP and POP3 classes
* All elements previously marked as deprecated have been removed:
* `PHPMailer->Version`
* `PHPMailer->ReturnPath`
* `PHPMailer->PluginDir`
* `PHPMailer->encodeQPphp()`
* `SMTP->CRLF`
* `SMTP->Version`
* `SMTP->SMTP_PORT`
* `POP3->CRLF`
* `POP3->Version`
* NTLM authentication has been removed - it never worked anyway!
* `PHPMailer->Workstation`
* `PHPMailer->Realm`
* `SMTP::authenticate` method signature changed
* `parseAddresses()` is now static
* `validateAddress()` is now called statically from `parseAddresses()`
* `idnSupported()` is now static and is called statically from `punyencodeAddress()`
* Fixes for sendmail parameter problems in WordPress, thanks to @SirLouen
* Reduce memory consumption when sending large attachments by @RobinvanderVliet
## Version 7.0.1 (November 25th, 2025)
* Use From domain when generating CIDs in msgHTML.
* Update to PHPCompatibility 10, resolve numerous PHPCS issues in PHP 8.5.
* Revise GitHub actions for PHP 8.5 and experimental 8.6 tests.
* Switch gmail example from the deprecated IMAP extension to use `directorytree/imapengine` for IMAP uploads.
* Set `htmlspecialchars()` flags explicitly and consistently.
* Convert XOAUTH2 token exceptions into PHPMailer Exceptions. The original exception is available as an inner exception.
* Deprecate VERSION constants in POP3 and SMTP classes.
* Remove dependency on `roave/security-advisories`; it's now built into composer 2.9.
* Update Dutch, Esperanto, and Norwegian translations.
## Version 7.0.0 (October 15th, 2025)
This is exactly the same as 6.11.1 but bumps the major version number to indicate the presence of a BC break in child classes. Specifically, `lang()`, `setLanguage()`, and `$language` are now static, and should be called statically.
## Version 6.12.0 (October 15th, 2025)
This is exactly the same as 6.10.0, reverting all the changes in 6.11.0 and 6.11.1, which inadvertently introduced a BC break affecting child classes. 6.11.1 has been re-released as 7.0.0.
## Version 6.11.1 (September 30th, 2025)
* Avoid function signature problems with the deprecation of `$useimap` in `parseAddresses`.
## Version 6.11.0 (September 29th, 2025)
* Add support for [RFC4954](https://www.rfc-editor.org/rfc/rfc4954#section-4) two-part authentication for large XOAUTH2 tokens.
* Also support empty tokens.
* Avoid bogus static analyser deprecation warnings in `setFrom`.
* Make language loading entirely static, thanks to @SirLouen.
* Emit warnings when `parseAddresses()` is used without the IMAP extension.
* Handle `mb_decode_mimeheader` changes from PHP 8.3+.
* Deprecate the charset param to parseAddresses.
* Fix PHP 8.5 linting issue.
* Don't use `-t` switch when calling qmail.
* Checking for interrupted system calls now works in languages other than English.
* Add support for extracting gmail transaction IDs after sending.
* For consistency, the protected `ReplyTo` property has been changed to match the format used for other address arrays.
* Fix line length issues when using S/MIME signing.
* Pin action runners to exact versions to avoid unexpected upstream changes.
## Version 6.10.0 (April 24th, 2025)
* Add support for [RFC 6530 SMTPUTF8](https://www.rfc-editor.org/rfc/rfc6530), permitting use of UTF-8 Unicode characters everywhere, thanks to @arnt and ICANN. See `SMTPUTF8.md` for details.
* More reliable checking for multibyte support.
## Version 6.9.3 (November 22nd, 2024)
* Add support for the release version of PHP 8.4
* Add experimental support for PHP 8.5
* Use rfc-editor.org for all RFC docs links
## Version 6.9.2 (October 9th, 2024)
* Escape dots in SMTP transaction ID patterns
* Add link to [aboutmy.email](https://aboutmy.email) testing service in docs
* Add a more comprehensive example for using XOAUTH2 with gmail and Azure, thanks to @decomplexity
* Update Turkish, Spanish, Japanese, Russian, French translations
* Add Urdu & Kurdish (Sorani) translations
* Fix broken links in docs and comments, avoid http links, correct link to LGPL 2.1 license file
* Cleaner PSR-3 SMTP debug logging
* Bump GitHub action versions
* Fix error handler scope issue in POP3 and SMTP classes
* Numerous test improvements by @jrfnl
## Version 6.9.1 (November 25th, 2023)
* Finalise SendOauth2 example
## Version 6.9.0 (November 23rd, 2023)
* Add support for official release of PHP 8.3, add experimental support for PHP 8.4
* Add `clearCustomHeader` and `replaceCustomHeader` methods
* Add support for the XCLIENT SMTP extension with `setSMTPXclientAttribute` and `getSMTPXclientAttributes` methods
* Don't attempt opportunistic TLS when connecting to localhost
* Add package link and example showing how to use @decomplexity's SendOauth2 wrapper
* Update example to show a better way of using an SMTP subclass
* Avoid some more deprecation warnings
* Update Danish and Polish translations
* Add Bengali and Assamese translations
## Version 6.8.1 (August 29th, 2023)
* Don't reflect malformed DSNs in error messages to avert any risk of XSS
* Improve Simplified Chinese, Sinhalese, and Norwegian translations
* Don't use `setAccessible` in PHP >= 8.1 in tests
* Avoid a deprecation notice in PHP 8.3
* Fix link in readme
## Version 6.8.0 (March 6th, 2023)
* Add DSN parsing class, thanks to @voronkovich
* Fix some name edge cases, expand tests
* Add pattern for ZonMTA message IDs
* Improve Hindi translation
## Version 6.7.1 (December 8th, 2022)
* Add official support for PHP 8.2
* Add PHP 8.3 to test suite with "experimental" status
* Add ext-openssl to composer suggest list
* Bump development dependencies
## Version 6.7 (December 5th, 2022)
* Break out boundary definitions into a method (note that boundary format has also changed slightly)
* Remove MIME preamble to match popular client behaviour, may help with DKIM too
* Fix handling of trailing whitespace in simple DKIM canonicalisation
* Fix some possible POP3 auth issues, including a TCP hang (thanks to @czirkoszoltan)
* Add Azure XOAUTH2 example and docs (thanks to @greew)
* Preserve errors during disconnect
* Avoid some PHP 8.1 type issues
* Update CI to run on Ubuntu 22.04
## Version 6.6.5 (October 7th, 2022)
* Don't try to issue RSET if there has been a connection error
* Reject attempts to add folders as attachments
* Don't suppress earlier error messages on close()
* Handle Host === null better
* Update Danish and Polish translations
* Change recommendation for Microsoft OAuth package to thenetworg/oauth2-azure
* Bump some GitHub action versions
## Version 6.6.4 (August 22nd, 2022)
* Update Greek translation
* Add text/csv MIME type
* Fix DKIM when sending to anonymous group via mail()
* Improve docs around auth following gmail & MS deprecations
* Update GitHub action deps
* Add OpenSSF Scorecard security health metrics
## Version 6.6.3 (June 20th, 2022)
* Add an HTML form to the OAuth setup script
* Minor CS improvements
* Add Mongolian translation
* Remove bogus "ch" translation
## Version 6.6.2 (June 14th, 2022)
* Fix docs deployment GitHub action
* Updates to parallel-lint and console highlighter, thanks to @jrfnl
## Version 6.6.1 (June 14th, 2022) (unreleased)
* Don't clear errors on RSET, so they can still be obtained when using keepalive
* Bump some GitHub action versions
* Fix some tests
* 🇺🇦 Slava Ukraini!
## Version 6.6 (February 28th, 2022)
* Introduce interface for OAuth providers, making it easier to use OAuth libraries other than the League one, thanks to @pdscopes.
* Add more contextual information to TLS connection failure error messages, and throw exceptions on TLS connection errors, thanks to @miken32
## Version 6.5.4 (February 17th, 2022)
* If we can't use escaping functions, refuse to do unsafe things
* Avoid PHP 8.1 trim issue
* Add tests for XMailer
* Fix bug in use of CharSet property
* Fix bug in file upload example
* Update dev dependencies
## Version 6.5.3 (November 25th, 2021)
* Wrong commit tagged for the 6.5.2 release!
* Version file updated
## Version 6.5.2 (November 25th, 2021)
* Enable official support for PHP 8.1
* Enable experimental support for PHP 8.2
* Fix for PHP 5.6
* Fix for incorrect options for punyencoding IDNs
## Version 6.5.1 (August 18th, 2021)
* Provisional support for PHP 8.1
* Major overhaul of test suite
* Add codecov.io coverage reporting
* Prefer implicit TLS on port 465 as default encryption scheme in examples, as per RFC8314
* Fix potential noisy output from IMAP address parser
* Stricter checking of custom MessageID validity
* Replace invalid default From address
* Support fallback for languages, so a request for `pt_xx` will fall back to `pt` rather than the default `en`.
* Support multi-line RFC2047 addresses in parseAddresses
* Improved Japanese translation
Many thanks to @jrfnl for all her work.
## Version 6.5.0 (June 16th, 2021)
* **SECURITY** Fixes CVE-2021-34551, a complex RCE affecting Windows hosts. See [SECURITY.md](SECURITY.md) for details.
* The fix for this issue changes the way that language files are loaded. While they remain in the same PHP-like format, they are processed as plain text, and any code in them will not be run, including operations such as concatenation using the `.` operator.
* *Deprecation* The current translation file format using PHP arrays is now deprecated; the next major version will introduce a new format.
* **SECURITY** Fixes CVE-2021-3603 that may permit untrusted code to be run from an address validator. See [SECURITY.md](SECURITY.md) for details.
* The fix for this issue includes a minor BC break: callables injected into `validateAddress`, or indirectly through the `$validator` class property, may no longer be simple strings. If you want to inject your own validator, provide a closure instead of a function name.
* Haraka message ID strings are now recognised
## Version 6.4.1 (April 29th, 2021)
* **SECURITY** Fixes CVE-2020-36326, a regression of CVE-2018-19296 object injection introduced in 6.1.8, see SECURITY.md for details
* Reject more file paths that look like URLs, matching RFC3986 spec, blocking URLS using schemes such as `ssh2`
* Ensure method signature consistency in `doCallback` calls
* Ukrainian language update
* Add composer scripts for checking coding standards and running tests
## Version 6.4.0 (March 31st, 2021)
* Revert change that made the `mail()` and sendmail transports set the envelope sender if one isn't explicitly provided, as it causes problems described in <https://github.com/PHPMailer/PHPMailer/issues/2298>
* Check for mbstring extension before decoding addresss in `parseAddress`
* Add Serbian Latin translation (`sr_latn`)
* Enrol PHPMailer in Tidelift
## Version 6.3.0 (February 19th, 2021)
* Handle early connection errors such as 421 during connection and EHLO states
* Switch to GitHub Actions for CI
* Generate debug output for `mail()`, sendmail, and qmail transports. Enable using the same mechanism as for SMTP: set `SMTPDebug` > 0
* Make the `mail()` and sendmail transports set the envelope sender the same way as SMTP does, i.e. use whatever `From` is set to, only falling back to the `sendmail_from` php.ini setting if `From` is unset. This avoids errors from the `mail()` function if `Sender` is not set explicitly and php.ini is not configured. This is a minor functionality change, so bumps the minor version number.
* Extend `parseAddresses` to decode encoded names, improve tests
## Version 6.2.0
* PHP 8.0 compatibility, many thanks to @jrf_nl!
* Switch from PHP CS Fixer to PHP CodeSniffer for coding standards
* Create class constants for the debug levels in the POP3 class
* Improve French, Slovenian, and Ukrainian translations
* Improve file upload examples so file extensions are retained
* Resolve PHP 8 line break issues due to a very old PHP bug being fixed
* Avoid warnings when using old openssl functions
* Improve Travis-CI build configuration
## Version 6.1.8 (October 9th, 2020)
* Mark `ext-hash` as required in composer.json. This has long been required, but now it will cause an error at install time rather than runtime, making it easier to diagnose
* Make file upload examples safer
* Update links to SMTP testing servers
* Avoid errors when set_time_limit is disabled (you need better hosting!)
* Allow overriding auth settings for local tests; makes it easy to run tests using HELO
* Recover gracefully from errors during keepalive sessions
* Add AVIF MIME type mapping
* Prevent duplicate `To` headers in BCC-only messages when using `mail()`
* Avoid file function problems when attaching files from Windows UNC paths
* Improve German, Bahasa Indonesian, Filipino translations
* Add Javascript-based example
* Increased test coverage
## Version 6.1.7 (July 14th, 2020)
* Split SMTP connection into two separate methods
* Undo BC break in PHP versions 5.2.3 - 7.0.0 introduced in 6.1.2 when injecting callables for address validation and HTML to text conversion
* Save response to SMTP welcome banner as other responses are saved
* Retry stream_select if interrupted by a signal
## Version 6.1.6 (May 27th, 2020)
* **SECURITY** Fix insufficient output escaping bug in file attachment names. CVE-2020-13625. Reported by Elar Lang of Clarified Security.
* Correct Armenian ISO language code from `am` to `hy`, add mapping for fallback
* Use correct timeout property in debug output
## Version 6.1.5 (March 14th, 2020)
* Reject invalid custom headers that are empty or contain breaks
* Various fixes for DKIM issues, especially when using `mail()` transport
* Drop the `l=` length tag from DKIM signatures; it's a mild security risk
* Ensure CRLF is used explicitly when needed, rather than `static::$LE`
* Add a method for trimming header content consistently
* Some minor tweaks to resolve static analyser complaints
* Check that attachment files are readable both when adding *and* when sending
* Work around Outlook bug in mishandling MIME preamble
* Danish translation improvements
## Version 6.1.4 (December 10th, 2019)
* Clean up hostname handling
* Avoid IDN error on older PHP versions, prep for PHP 8.0
* Don't force RFC2047 folding unnecessarily
* Enable tests on full release of PHP 7.4
## Version 6.1.3 (November 21st, 2019)
* Fix an issue preventing injected debug handlers from working
* Fix an issue relating to connection timeout
* Add `SMTP::MAX_REPLY_LENGTH` constant
* Remove some dev dependencies; phpdoc no longer included
* Fix an issue where non-compliant servers returning bare codes caused an SMTP hang
## Version 6.1.2 (November 13th, 2019)
* Substantial revision of DKIM header generation
* Use shorter hashes for auto-generated CID values
* Fix format of content-id headers, and only use them for inline attachments
* Remove all use of XHTML
* Lots of coding standards cleanup
* API docs are now auto-updated via GitHub actions
* Fix header separation bug created in 6.1.1
* Fix misidentification of background attributes in SVG images in msgHTML
## Version 6.1.1 (September 27th 2019)
* Fix misordered version tag
## Version 6.1.0 (September 27th 2019)
* Multiple bug fixes for folding of long header lines, thanks to @caugner
* Add support for [RFC2387 child element content-type hint](https://www.rfc-editor.org/rfc/rfc2387#section-3.1) in `multipart/related` structures.
* Support for Ical event methods other than `REQUEST`, thanks to @puhr-mde
* Change header folding and param separation to use spaces instead of tabs
* Use ; to separate multiple MIME header params
* Add support for RFC3461 DSN messages
* IMAP example code fixed
* Use PHP temp streams instead of temp files
* Allow for longer SMTP error codes
* Updated Brazilian Portuguese translation
* Throw exceptions on invalid encoding values
* Add Afrikaans translation, thanks to @Donno191
* Updated Farsi/Persian translation
* Add PHP 7.4 to test config
* Remove some ambiguity about setting XMailer property
* Improve error checking in mailing list example
* Drop PHP 5.5 from CI config as it's no longer supported by Travis-CI
* Fix S/MIME signing
* Add constants for encryption type
* More consistent use of constants for encryption, charset, encoding
* Add PHPMailer logo images
## Version 6.0.7 (February 1st 2019)
* Include RedHat GPL Cooperation Commitment - see the `COMMITMENT` file for details.
* Don't exclude composer.json from git exports as it breaks composer updates in projects that use PHPMailer
* Updated Malay translation
* Fix language tests
## Version 6.0.6 (November 14th 2018)
* **SECURITY** Fix potential object injection vulnerability. Reported by Sehun Oh of cyberone.kr.
* Added Tagalog translation, thanks to @StoneArtz
* Added Malagache translation, thanks to @Hackinet
* Updated Serbian translation, fixed incorrect language code, thanks to @mmilanovic4
* Updated Arabic translations (@MicroDroid)
* Updated Hungarian translations
* Updated Dutch translations
* Updated Slovenian translation (@filips123)
* Updated Slovak translation (@pcmanik)
* Updated Italian translation (@sabas)
* Updated Norwegian translation (@aleskr)
* Updated Indonesian translation (@mylastof)
* Add constants for common values, such as `text/html` and `quoted-printable`, and use them
* Added support for copied headers in DKIM, helping with debugging, and an option to add extra headers to the DKIM signature. See DKIM_sign example for how to use them. Thanks to @gwi-mmuths.
* Add Campaign Monitor transaction ID pattern matcher
* Remove deprecated constant and ini values causing warnings in PHP 7.3, added PHP 7.3 build to Travis config.
* Expanded test coverage
## Version 5.2.27 (November 14th 2018)
* **SECURITY** Fix potential object injection vulnerability. Reported by Sehun Oh of cyberone.kr.
* Note that the 5.2 branch is now deprecated and will not receive security updates after 31st December 2018.
## Version 6.0.5 (March 27th 2018)
* Re-roll of 6.0.4 to fix missed version file entry. No code changes.
## Version 6.0.4 (March 27th 2018)
* Add some modern MIME types
* Add Hindi translation (thanks to @dextel2)
* Improve composer docs
* Fix generation of path to language files
## Version 6.0.3 (January 5th 2018)
* Correct DKIM canonicalization of line breaks for header & body - thanks to @themichaelhall
* Make dependence on ext-filter explicit in composer.json
## Version 6.0.2 (November 29th 2017)
* Don't make max line length depend on line break format
* Improve Travis-CI config - thanks to Filippo Tessarotto
* Match SendGrid transaction IDs
* `idnSupported()` now static, as previously documented
* Improve error messages for invalid addresses
* Improve Indonesian translation (thanks to @januridp)
* Improve Esperanto translation (thanks to @dknacht)
* Clean up git export ignore settings for production and zip bundles
* Update license doc
* Updated upgrading docs
* Clarify `addStringEmbeddedImage` docs
* Hide auth credentials in all but lowest-level debug output, prevents leakage in bug reports
* Code style cleanup
## Version 6.0.1 (September 14th 2017)
* Use shorter Message-ID headers (with more entropy) to avoid iCloud blackhole bug
* Switch to Symfony code style (though it's not well defined)
* CI builds now apply syntax & code style checks, so make your PRs tidy!
* CI code coverage only applied on latest version of PHP to speed up builds (thanks to @Slamdunk for these CI changes)
* Remove `composer.lock` - it's important that libraries break early; keeping it is for apps
* Rename test scripts to PSR-4 spec
* Make content-id values settable on attachments, not just embedded items
* Add SMTP transaction IDs to callbacks & allow for future expansion
* Expand test coverage
## Version 6.0 (August 28th 2017)
This is a major update that breaks backwards compatibility.
* **Requires PHP 5.5 or later**
* **Uses the `PHPMailer\PHPMailer` namespace**
* File structure simplified and PSR-4 compatible, classes live in the `src/` folder
* The custom autoloader has been removed: [**use composer**](https://getcomposer.org)!
* Classes & Exceptions renamed to make use of the namespace
* Most statically called functions now use the `static` keyword instead of `self`, so it's possible to override static internal functions in subclasses, for example `validateAddress()`
* Complete RFC standardisation on CRLF (`\r\n`) line breaks for SMTP by default:
* `PHPMailer:$LE` defaults to CRLF
* All uses of `PHPMailer::$LE` property converted to use `static::$LE` constant for consistency and ease of overriding
* Similar changes to line break handling in SMTP and POP3 classes.
* Line break format for `mail()` transport is set automatically.
* Warnings emitted for buggy `mail()` in PHP versions 7.0.0 - 7.0.16 and 7.1.0 - 7.1.2; either upgrade or switch to SMTP.
* Extensive reworking of XOAUTH2, adding support for Google, Yahoo and Microsoft providers, thanks to @sherryl4george
* Major cleanup of docs and examples
* All elements previously marked as deprecated have been removed:
* `PHPMailer->Version` (replaced with `VERSION` constant)
* `PHPMailer->ReturnPath`
* `PHPMailer->PluginDir`
* `PHPMailer->encodeQPphp()`
* `SMTP->CRLF` (replaced with `LE` constant)
* `SMTP->Version` (replaced with `VERSION` constant)
* `SMTP->SMTP_PORT` (replaced with `DEFAULT_PORT` constant)
* `POP3->CRLF` (replaced with `LE` constant)
* `POP3->Version` (replaced with `VERSION` constant)
* `POP3->POP3_PORT` (replaced with `DEFAULT_PORT` constant)
* `POP3->POP3_TIMEOUT` (replaced with `DEFAULT_TIMEOUT` constant)
* NTLM authentication has been removed - it never worked anyway!
* `PHPMailer->Workstation`
* `PHPMailer->Realm`
* `SingleTo` functionality is deprecated; this belongs at a higher level - PHPMailer is not a mailing list system.
* `SMTP::authenticate` method signature changed
* `parseAddresses()` is now static
* `validateAddress()` is now called statically from `parseAddresses()`
* `idnSupported()` is now static and is called statically from `punyencodeAddress()`
* `PHPMailer->SingleToArray` is now protected
* `fixEOL()` method removed - it duplicates `PHPMailer::normalizeBreaks()`, so use that instead
* Don't try to use an auth mechanism if it's not supported by the server
* Reorder automatic AUTH mechanism selector to try most secure method first
* `Extras` classes have been removed - use alternative packages from [packagist.org](https://packagist.org) instead
* Better handling of automatic transfer encoding switch in the presence of long lines
* Simplification of address validation - now uses PHP's `FILTER_VALIDATE_EMAIL` pattern by default, retains advanced options
* `Debugoutput` can accept a PSR-3 logger instance
* To reduce code footprint, the examples folder is no longer included in composer deployments or GitHub zip files
* Trap low-level errors in SMTP, reports via debug output
* More reliable folding of message headers
* Inject your own SMTP implementation via `setSMTPInstance()` instead of having to subclass and override `getSMTPInstance()`.
* Make obtaining SMTP transaction ID more reliable
* Better handling of unreliable PHP timeouts
* Made `SMTPDebug = 4` slightly less noisy
## Version 5.2.25 (August 28th 2017)
* Make obtaining SMTP transaction ID more reliable
* Add Bosnian translation
* This is the last official release in the legacy PHPMailer 5.2 series; there may be future security patches (which will be found in the [5.2-stable branch](https://github.com/PHPMailer/PHPMailer/tree/5.2-stable)), but no further non-security PRs or issues will be accepted. Migrate to PHPMailer 6.0.
## Version 5.2.24 (July 26th 2017)
* **SECURITY** Fix XSS vulnerability in one of the code examples, [CVE-2017-11503](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-11503). The `code_generator.phps` example did not filter user input prior to output. This file is distributed with a `.phps` extension, so it it not normally executable unless it is explicitly renamed, so it is safe by default. There was also an undisclosed potential XSS vulnerability in the default exception handler (unused by default). Patches for both issues kindly provided by Patrick Monnerat of the Fedora Project.
* **SECURITY** Fix XSS vulnerability in one of the code examples, [CVE-2017-11503](https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2017-11503). The `code_generator.phps` example did not filter user input prior to output. This file is distributed with a `.phps` extension, so it is not normally executable unless it is explicitly renamed, so it is safe by default. There was also an undisclosed potential XSS vulnerability in the default exception handler (unused by default). Patches for both issues kindly provided by Patrick Monnerat of the Fedora Project.
* Handle bare codes (an RFC contravention) in SMTP server responses
* Make message timestamps more dynamic - calculate the date separately for each message
* More thorough checks for reading attachments.
* Throw an exception when trying to send a message with an empty body caused by an internal error.
* Replaced all use of MD5 and SHA1 hash functions with SHA256.
* Now checks for invalid host strings when sending via SMTP.
If you are having problems connecting or sending emails through your SMTP server, the SMTP class can provide more information about the processing/errors taking place.
Use the debug functionality of the class to see what's going on in your connections. To do that, set the debug level in your script. For example:
Setting the `SMTPDebug` property results in different amounts of output:
* `0`: Disable debugging (you can also leave this out completely, 0 is the default).
* `1`: Output messages sent by the client.
* `2`: as 1, plus responses received from the server (this is probably the most useful setting for debugging).
* `3`: as 2, plus more information about the initial connection.
* `4`: as 3, plus even lower-level information, very verbose.
You don't need to use levels above 2 unless you're having trouble connecting at all - it will just make output more verbose and more difficult to read.
Note that you will get no output until you call `send()`, because no SMTP conversation takes place until you do that.
##Debug output format
The form that the debug output taks is determined by the `Debugoutput` property. This has several options:
* `echo` Output plain-text as-is, appropriate for CLI
* `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output
* `error_log` Output to error log as configured in php.ini
By default PHPMailer will use `echo` if run from a `cli` or `cli-server` SAPI, `html` otherwise. Alternatively, you can implement your own system by providing a callable expecting two parameters: a message string and the debug level:
Generated documentation for PHPMailer is [available online](https://phpmailer.github.io/PHPMailer/), and is regenerated automatically whenever changes are made.
Pre-built PHPMailer API documentation is not provided in this repo, but you can generate it by running `phpdoc` in the top-level folder of this project, and documentation will be generated in this `docs` folder. You will need to have [phpDocumentor](https://www.phpdoc.org) installed. The configuration for phpdoc is in the [phpdoc.dist.xml file](https://github.com/PHPMailer/PHPMailer/blob/master/phpdoc.dist.xml).
Further help and information is available in [the PHPMailer README](https://github.com/PHPMailer/PHPMailer/blob/master/README.md), [the examples folder](https://github.com/PHPMailer/PHPMailer/tree/master/examples), and in [the GitHub wiki](https://github.com/PHPMailer/PHPMailer/wiki).
Fixes and additions to documentation are welcome - please submit pull requests or improve wiki pages.
This is built for PHP Mailer 1.72 and was not tested with any previous version. It was developed under PHP 4.3.11 (E_ALL). It works under PHP 5 and 5.1 with E_ALL, but not in Strict mode due to var deprecation (but then neither does PHP Mailer either!). It follows the RFC 1939 standard explicitly and is fully commented.
With that noted, here is how to implement it:
I didn't want to modify the PHP Mailer classes at all, so you will have to include/require this class along with the base one. It can sit quite happily in the phpmailer directory.
When you need it, create your POP3 object
Right before I invoke PHP Mailer I activate the POP3 authorisation. POP3 before SMTP is a process whereby you login to your web hosts POP3 mail server BEFORE sending out any emails via SMTP. The POP3 logon 'verifies' your ability to send email by SMTP, which typically otherwise blocks you. On my web host (Pair Networks) a single POP3 logon is enough to 'verify' you for 90 minutes. Here is some sample PHP code that activates the POP3 logon and then sends an email via PHP Mailer:
The PHP Mailer parts of this code should be obvious to anyone who has used PHP Mailer before. One thing to note - you almost certainly will not need to use SMTP Authentication *and* POP3 before SMTP together. The Authorisation method is a proxy method to all of the others within that class. There are connect, Logon and disconnect methods available, but I wrapped them in the single Authorisation one to make things easier.
1. pop3.example.com - The POP3 Mail Server Name (hostname or IP address)
2. 110 - The POP3 Port on which to connect (default is usually 110, but check with your host)
3. 30 - A connection time-out value (in seconds)
4. mailer - The POP3 Username required to logon
5. password - The POP3 Password required to logon
6. 1 - The class debug level (0 = off, 1+ = debug output is echoed to the browser)
Final Comments + the Download
1) This class does not support APOP connections. This is only because I did not have an APOP server to test with, but if you'd like to see that added just contact me.
2) Opening and closing lots of POP3 connections can be quite a resource/network drain. If you need to send a whole batch of emails then just perform the authentication once at the start, and then loop through your mail sending script. Providing this process doesn't take longer than the verification period lasts on your POP3 server, you should be fine. With my host that period is 90 minutes, i.e. plenty of time.
3) If you have heavy requirements for this script (i.e. send a LOT of email on a frequent basis) then I would advise seeking out an alternative sending method (direct SMTP ideally). If this isn't possible then you could modify this class so the 'last authorised' date is recorded somewhere (MySQL, Flat file, etc) meaning you only open a new connection if the old one has expired, saving you precious overhead.
4) There are lots of other POP3 classes for PHP available. However most of them implement the full POP3 command set, where-as this one is purely for authentication, and much lighter as a result. However using any of the other POP3 classes to just logon to your server would have the same net result. At the end of the day, use whatever method you feel most comfortable with.
Download
My thanks to Chris Ryan for the inspiration (even if indirectly, via his SMTP class)
This folder contains a collection of examples of using [PHPMailer](https://github.com/PHPMailer/PHPMailer).
## About testing email sending
When working on email sending code you'll find yourself worrying about what might happen if all these test emails got sent to your mailing list. The solution is to use a fake mail server, one that acts just like the real thing, but just doesn't actually send anything out. Some offer web interfaces, feedback, logging, the ability to return specific error codes, all things that are useful for testing error handling, authentication etc. Here's a selection of mail testing tools you might like to try:
* [FakeEmail](https://github.com/tomwardill/FakeEmail), a Python-based fake mail server with a web interface.
* [smtp-sink](https://www.postfix.org/smtp-sink.1.html), part of the Postfix mail server, so you may have this installed already. This is used in the GitHub actions configuration to run PHPMailer's unit tests.
* [smtp4dev](https://github.com/rnwood/smtp4dev), a dummy SMTP server for Windows and Linux.
* [fakesendmail.sh](https://github.com/PHPMailer/PHPMailer/blob/master/test/fakesendmail.sh), part of PHPMailer's test setup, this is a shell script that emulates sendmail for testing 'mail' or 'sendmail' methods in PHPMailer.
* [HELO](https://usehelo.com), a very nice (commercial) mail server desktop app from BeyondCode, and [how to set it up for local testing](https://usehelo.com/blog/how-to-use-helo-with-phps-mail-function).
* [msglint](https://www.splitbrain.org/_static/msglint/), not a mail server, the IETF's MIME structure analyser checks the formatting of your messages.
* [MailHog](https://github.com/les-enovateurs/mailhog-examples), a Go-based email testing tool for developers with a web interface. You can use it with Docker and GitHub Actions to test your mails. The repository also contains a small part of PHPMailer's setup.
* [aboutmy.email](https://aboutmy.email), a service for evaluating your email config – SPF, DKIM, DMARC, and compliance with list-unsubscribe, TLS, and many other settings.
Most of these examples use the `example.com` and `example.net` domains. These domains are reserved by IANA for illustrative purposes, as documented in [RFC 2606](https://www.rfc-editor.org/rfc/rfc2606). Don't use made-up domains like 'mydomain.com' or 'somedomain.com' in examples as someone, somewhere, probably owns them!
## Security note
Before running these examples in a web server, you'll need to rename them with '.php' extensions. They are supplied as '.phps' files which will usually be displayed with syntax highlighting by PHP instead of running them. This prevents potential security issues with running potential spam-gateway code if you happen to deploy these code examples on a live site - _please don't do that!_
Similarly, don't leave your passwords in these files as they will be visible to the world!
## [mail.phps](mail.phps)
This is a basic example which creates an email message from an external HTML file, creates a plain text body, sets various addresses, adds an attachment and sends the message. It uses PHP's built-in mail() function which is the simplest to use, but relies on the presence of a local mail server, something which is not usually available on Windows. If you find yourself in that situation, either install a local mail server, or use a remote one and send using SMTP instead.
This is probably the most common reason for using PHPMailer - building a contact form. This example has a basic, unstyled form and also illustrates how to filter input data before using it, how to validate addresses, how to avoid being abused as a spam gateway, and how to address messages correctly so that you don't fail SPF checks.
## [exceptions.phps](exceptions.phps)
Like the mail example, but shows how to use PHPMailer's optional exceptions for error handling.
## [extending.phps](extending.phps)
This shows how to create a subclass of PHPMailer to customise its behaviour and simplify coding in your app.
## [smtp.phps](smtp.phps)
A simple example sending using SMTP with authentication.
## [smtp_no_auth.phps](smtp_no_auth.phps)
A simple example sending using SMTP without authentication.
## [send_file_upload.phps](send_file_upload.phps)
Lots of people want to do this... This is a simple form which accepts a file upload and emails it.
A slightly more complex form that allows uploading multiple files at once and sends all of them as attachments to an email.
## [sendmail.phps](sendmail.phps)
A simple example using sendmail. Sendmail is a program (usually found on Linux/BSD, OS X and other UNIX-alikes) that can be used to submit messages to a local mail server without a lengthy SMTP conversation. It's probably the fastest sending mechanism, but lacks some error reporting features. There are sendmail emulators for most popular mail servers including postfix, qmail, exim etc.
## [gmail.phps](gmail.phps)
Submitting email via Google's Gmail service is a popular use of PHPMailer. It's much the same as normal SMTP sending, just with some specific settings, namely using TLS encryption, authentication is enabled, and it connects to the SMTP submission port 587 on the smtp.gmail.com host. This example does all that.
## [gmail_xoauth.phps](gmail_xoauth.phps)
Gmail now likes you to use XOAUTH2 for SMTP authentication. This is extremely laborious to set up, but once it's done you can use it repeatedly and will no longer need Gmail's ineptly-named "Allow less secure apps" setting enabled. [Read the guide in the wiki](https://github.com/PHPMailer/PHPMailer/wiki/Using-Gmail-with-XOAUTH2) for how to set it up.
## [pop_before_smtp.phps](pop_before_smtp.phps)
Back in the stone age, before effective SMTP authentication mechanisms were available, it was common for ISPs to use POP-before-SMTP authentication. As it implies, you authenticate using the POP3 protocol (an older protocol now mostly replaced by the far superior IMAP), and then the SMTP server will allow send access from your IP address for a short while, usually 5-15 minutes. PHPMailer includes a basic POP3 protocol client with just enough functionality to carry out this sequence - it's just like a normal SMTP conversation (without authentication), but connects via POP3 first.
## [mailing_list.phps](mailing_list.phps)
This is a somewhat naïve, but reasonably efficient example of sending similar emails to a list of different addresses. It sets up a PHPMailer instance using SMTP, then connects to a MySQL database to retrieve a list of recipients. The code loops over this list, sending email to each person using their info and marks them as sent in the database. It makes use of SMTP keepalive which saves reconnecting and re-authenticating between each message.
## [ssl_options.phps](ssl_options.phps)
PHP 5.6 introduced SSL certificate verification by default, and this applies to mail servers exactly as it does to web servers. Unfortunately, SSL misconfiguration in mail servers is quite common, so this caused a common problem: those that were previously using servers with bad configs suddenly found they stopped working when they upgraded PHP. PHPMailer provides a mechanism to disable SSL certificate verification as a workaround and this example shows how to do it. Bear in mind that this is **not** a good approach - the right way is to fix your mail server config!
An example of how to sign messages using [S/MIME](https://en.wikipedia.org/wiki/S/MIME), ensuring that your data can't be tampered with in transit, and proves to recipients that it was you that sent it.
* * *
## [smtp_check.phps](smtp_check.phps)
This is an example showing how to use the SMTP class by itself (without PHPMailer) to check an SMTP connection.
## [smtp_low_memory.phps](smtp_low_memory.phps)
This demonstrates how to extend the SMTP class and make PHPMailer use it. In this case it's an effort to make the SMTP class use less memory when sending large attachments.
<p>Image data URL (base64)<imgsrc="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="alt="#"></p>
<p>Image data URL (URL-encoded)<imgsrc="data:image/gif,GIF89a%01%00%01%00%00%00%00%21%F9%04%01%0A%00%01%00%2C%00%00%00%00%01%00%01%00%00%02%02L%01%00%3B"alt="#"></p>
<p>This folder contains a collection of examples of using <ahref="https://github.com/PHPMailer/PHPMailer">PHPMailer</a>.</p>
<h2>About testing email sending</h2>
<p>When working on email sending code you'll find yourself worrying about what might happen if all these test emails got sent to your mailing list. The solution is to use a fake mail server, one that acts just like the real thing, but just doesn't actually send anything out. Some offer web interfaces, feedback, logging, the ability to return specific error codes, all things that are useful for testing error handling, authentication etc. Here's a selection of mail testing tools you might like to try:</p>
<ul>
<li><ahref="https://github.com/Nilhcem/FakeSMTP">FakeSMTP</a>, a Java desktop app with the ability to show an SMTP log and save messages to a folder. </li>
<li><ahref="https://github.com/isotoma/FakeEmail">FakeEmail</a>, a Python-based fake mail server with a web interface.</li>
<li><ahref="http://www.postfix.org/smtp-sink.1.html">smtp-sink</a>, part of the Postfix mail server, so you probably already have this installed. This is used in the Travis-CI configuration to run PHPMailer's unit tests.</li>
<li><ahref="http://smtp4dev.codeplex.com">smtp4dev</a>, a dummy SMTP server for Windows.</li>
<li><ahref="https://github.com/PHPMailer/PHPMailer/blob/master/test/fakesendmail.sh">fakesendmail.sh</a>, part of PHPMailer's test setup, this is a shell script that emulates sendmail for testing 'mail' or 'sendmail' methods in PHPMailer.</li>
<li><ahref="http://tools.ietf.org/tools/msglint/">msglint</a>, not a mail server, the IETF's MIME structure analyser checks the formatting of your messages.</li>
<p>Before running these examples you'll need to rename them with '.php' extensions. They are supplied as '.phps' files which will usually be displayed with syntax highlighting by PHP instead of running them. This prevents potential security issues with running potential spam-gateway code if you happen to deploy these code examples on a live site - <em>please don't do that!</em> Similarly, don't leave your passwords in these files as they will be visible to the world!</p>
<p>This script is a simple code generator - fill in the form and hit submit, and it will use when you entered to email you a message, and will also generate PHP code using your settings that you can copy and paste to use in your own apps. If you need to get going quickly, this is probably the best place to start.</p>
<h2><ahref="mail.phps">mail.phps</a></h2>
<p>This script is a basic example which creates an email message from an external HTML file, creates a plain text body, sets various addresses, adds an attachment and sends the message. It uses PHP's built-in mail() function which is the simplest to use, but relies on the presence of a local mail server, something which is not usually available on Windows. If you find yourself in that situation, either install a local mail server, or use a remote one and send using SMTP instead.</p>
<p>A simple example sending using SMTP without authentication.</p>
<h2><ahref="sendmail.phps">sendmail.phps</a></h2>
<p>A simple example using sendmail. Sendmail is a program (usually found on Linux/BSD, OS X and other UNIX-alikes) that can be used to submit messages to a local mail server without a lengthy SMTP conversation. It's probably the fastest sending mechanism, but lacks some error reporting features. There are sendmail emulators for most popular mail servers including postfix, qmail, exim etc.</p>
<h2><ahref="gmail.phps">gmail.phps</a></h2>
<p>Submitting email via Google's Gmail service is a popular use of PHPMailer. It's much the same as normal SMTP sending, just with some specific settings, namely using TLS encryption, authentication is enabled, and it connects to the SMTP submission port 587 on the smtp.gmail.com host. This example does all that.</p>
<p>Before effective SMTP authentication mechanisms were available, it was common for ISPs to use POP-before-SMTP authentication. As it implies, you authenticate using the POP3 protocol (an older protocol now mostly replaced by the far superior IMAP), and then the SMTP server will allow send access from your IP address for a short while, usually 5-15 minutes. PHPMailer includes a POP3 protocol client, so it can carry out this sequence - it's just like a normal SMTP conversation (without authentication), but connects via POP first.</p>
<p>This is a somewhat naïve example of sending similar emails to a list of different addresses. It sets up a PHPMailer instance using SMTP, then connects to a MySQL database to retrieve a list of recipients. The code loops over this list, sending email to each person using their info and marks them as sent in the database. It makes use of SMTP keepalive which saves reconnecting and re-authenticating between each message.</p>
<p>This is an example showing how to use the SMTP class by itself (without PHPMailer) to check an SMTP connection.</p>
<hr>
<p>Most of these examples use the 'example.com' domain. This domain is reserved by IANA for illustrative purposes, as documented in <ahref="http://tools.ietf.org/html/rfc2606">RFC 2606</a>. Don't use made-up domains like 'mydomain.com' or 'somedomain.com' in examples as someone, somewhere, probably owns them!</p>
'/path/to/cert.crt', //The location of your certificate file
'/path/to/cert.key', //The location of your private key file
'yourSecretPrivateKeyPassword', //The password you protected your private key with (not the Import Password! may be empty but parameter must not be omitted!)
//The password you protected your private key with (not the Import Password!
//May be empty but the parameter must not be omitted!
'yourSecretPrivateKeyPassword',
'/path/to/certchain.pem' //The location of your chain file
);
//Send the message, check for errors
if (!$mail->send()) {
echo "Mailer Error: " . $mail->ErrorInfo;
echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
echo "Message sent!";
echo 'Message sent!';
}
/**
/*
* REMARKS:
* If your email client does not support S/MIME it will most likely just show an attachment smime.p7s which is the signature contained in the email.
* Other clients, such as Thunderbird support S/MIME natively and will validate the signature automatically and report the result in some way.
* If your email client does not support S/MIME it will most likely just show an attachment smime.p7s,
* which is the signature contained in the email.
* Other clients, such as Thunderbird support S/MIME natively and will validate the signature
* automatically and report the result in some way.
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
.syntaxhighlightera,.syntaxhighlighterdiv,.syntaxhighlightercode,.syntaxhighlightertable,.syntaxhighlightertabletd,.syntaxhighlightertabletr,.syntaxhighlightertabletbody,.syntaxhighlightertablethead,.syntaxhighlightertablecaption,.syntaxhighlightertextarea{-moz-border-radius:0000!important;-webkit-border-radius:0000!important;background:none!important;border:0!important;bottom:auto!important;float:none!important;height:auto!important;left:auto!important;line-height:1.1em!important;margin:0!important;outline:0!important;overflow:visible!important;padding:0!important;position:static!important;right:auto!important;text-align:left!important;top:auto!important;vertical-align:baseline!important;width:auto!important;box-sizing:content-box!important;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace!important;font-weight:normal!important;font-style:normal!important;font-size:1em!important;min-height:inherit!important;min-height:auto!important;}
These classes provide optional additional functions to PHPMailer.
These are not loaded by the PHPMailer autoloader, so in some cases you may need to `require` them yourself before using them.
## EasyPeasyICS
This class was originally written by Manuel Reinhard and provides a simple means of generating ICS/vCal files that are used in sending calendar events. PHPMailer does not use it directly, but you can use it to generate content appropriate for placing in the `Ical` property of PHPMailer. The PHPMailer project is now its official home as Manuel has given permission for that and is no longer maintaining it himself.
## htmlfilter
This class by Konstantin Riabitsev and Jim Jagielski implements HTML filtering to remove potentially malicious tags, such as `<script>` or `onclick=` attributes that can result in XSS attacks. This is a simple filter and is not as comprehensive as [HTMLawed](http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/) or [HTMLPurifier](http://htmlpurifier.org), but it's easier to use and considerably better than nothing! PHPMailer does not use it directly, but you may want to apply it to user-supplied HTML before using it as a message body.
## NTLM_SASL_client
This class by Manuel Lemos (bundled with permission) adds the ability to authenticate with Microsoft Windows mail servers that use NTLM-based authentication. It is used by PHPMailer if you send via SMTP and set the `AuthType` property to `NTLM`; you will also need to use the `Realm` and `Workstation` properties. The original source is [here](http://www.phpclasses.org/browse/file/7495.html).
Some files were not shown because too many files have changed in this diff
Show More