Compare commits

...

24 Commits

Author SHA1 Message Date
Stefan Grootscholten d42c8731f0 Fix issue that local part parser uses remaining code from lexer (#409)
Without this fix the added test will output

1) Egulias\EmailValidator\Tests\EmailValidator\EmailParserTest::testMultipleEmailAddresses
Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'another-local-part'
+'some-random-but-large-domain-part.example.comanother-local-part'
2025-03-06 23:45:56 +01:00
Eduardo Gulias Davis 515346048d Different strategy 2024-12-31 16:29:41 +01:00
Eduardo Gulias Davis 4fc3f5c419 Different strategy 2024-12-31 16:26:39 +01:00
Eduardo Gulias Davis 7dc3b8de4c Coverage path (#401)
* coverage path

* Use different action
2024-12-31 16:15:01 +01:00
Cari D. Burstein 3bd9c8c802 Update NoDNSRecord.php (#395)
Fixed typo in No DNS Record reason
2024-12-31 15:41:19 +01:00
Eduardo Gulias Davis 8be1dae097 Polish (#400)
* Polish

* polish

* improve workflow

* Fix action usage

* polish

* polish
2024-12-31 15:39:18 +01:00
Eduardo Gulias Davis dcf8f79c41 polish 2024-12-27 23:41:39 +01:00
Eduardo Gulias Davis e30356b45e Fix action usage 2024-12-27 23:22:30 +01:00
Eduardo Gulias Davis 229560066e improve workflow 2024-12-27 23:11:08 +01:00
Eduardo Gulias Davis 1ed7ade52c Update to recommended two workflows for secure secret when uploadin (#399)
* Update to recommended two workflows for secure secret when uploadin

* Making depenant workflow work
2024-12-27 22:52:06 +01:00
Eduardo Gulias Davis 793eb01a3f Fix for secrets on PRs 2024-12-27 20:22:03 +01:00
Eduardo Gulias Davis 32b52d9e08 Test for #317 (#397) 2024-12-27 20:21:18 +01:00
Eduardo Gulias Davis b115554301 Fix Readme style 2024-12-27 01:36:43 +01:00
Eduardo Gulias Davis c67e8e655f Fix Readme style 2024-12-27 01:27:39 +01:00
Eduardo Gulias Davis 2647beedf3 Removed stale changelog 2024-12-27 01:17:25 +01:00
Eduardo Gulias Davis e25ccf7e74 Add depandabot 2024-12-27 00:36:05 +01:00
Eduardo Gulias Davis d379dc240b Update readme 2024-12-27 00:31:08 +01:00
Eduardo Gulias Davis 621d46003b Fix github action (#396)
* Fix github action

* PHP8.4 is too new

* Revert log storage
2024-12-27 00:28:29 +01:00
chris 87db43e4ff docs: fix build status in README.md (#392) 2024-10-16 23:10:38 +02:00
chris c118cc7666 ci: fix deprecation for github actions (#391) 2024-10-08 19:20:25 +02:00
Arnt Gulbrandsen b4a1a0e5a6 Regularise a test. (#389)
The validEmailsProvider contained n-1 valid email addresses and 1 domain;
this replaces the domain with two valid email addresses in that domain, so
that all entries match the name.
2024-09-15 22:03:00 +02:00
Danny van der Sluijs 92c1ab0cc1 Update GitHub Actions to V4 (#383)
* Update tests.yml using actions/checkout@v4

* Update static-analysis.yml usingactions/checkout@v4
2024-05-01 15:20:12 +02:00
Daniel 8775776563 chore: replace scalar with more precise type specification (#369)
Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
2023-10-22 23:56:40 +02:00
Andrew Ivchenkov 87b8b8506e Used the most strict psalm level and fixed all psalm errors (#377) 2023-10-15 23:39:26 +02:00
23 changed files with 156 additions and 121 deletions
-1
View File
@@ -3,5 +3,4 @@
/.* export-ignore
/phpunit.xml.dist export-ignore
/psalm.xml export-ignore
/psalm.baseline.xml export-ignore
/README.md export-ignore
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "composer"
directory: "/"
schedule:
interval: "weekly"
+1 -2
View File
@@ -3,7 +3,6 @@ name: static analysis
on:
push:
branches:
- master
- '*.x'
pull_request:
@@ -18,7 +17,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
+7 -15
View File
@@ -1,13 +1,10 @@
name: build
name: unit-tests
on:
push:
branches:
- master
- '*.x'
pull_request:
schedule:
- cron: '0 0 * * *'
jobs:
tests:
@@ -27,7 +24,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -38,7 +35,7 @@ jobs:
coverage: xdebug
- name: Install dependencies
uses: ramsey/composer-install@v2
uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.deps }}
@@ -47,15 +44,10 @@ jobs:
- name: Execute tests
run: vendor/bin/phpunit --coverage-clover build/logs/clover.xml --exclude-group flaky
- name: Coverage
if: ${{ matrix.php == '8.1' && matrix.deps == 'lowest' }}
shell: bash
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
run: bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r build/logs/clover.xml
- name: Store artifacts
uses: actions/upload-artifact@v3
if: ${{ matrix.php == '8.1' && matrix.deps == 'lowest' }}
uses: actions/upload-artifact@v4
with:
name: logs
path: build/logs
name: clover.xml
path: build/logs
+33
View File
@@ -0,0 +1,33 @@
name: upload-coverage-to-codacy
on:
workflow_run:
workflows: ["unit-tests"]
types:
- completed
jobs:
upload:
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
strategy:
fail-fast: true
name: upload-coverage
steps:
- name: Setup logs directory
run: mkdir -p build/coverage
- name: Download clover.xml artifact
uses: dawidd6/action-download-artifact@v7
with:
workflow: tests.yml
name: clover.xml
path: build/coverage
- name: Upload Coverage to Codacy
shell: bash
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
run: bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r build/coverage/clover.xml
-32
View File
@@ -1,32 +0,0 @@
# EmailValidator Changelog
## New Features
* Access to local part and domain part from EmailParser
* Validations outside of the scope of the RFC will be considered "extra" validations, thus opening the door for adding new; will live in their own folder "extra" (as requested in #248, #195, #183).
## Breaking changes
* PHP version upgraded to match Symfony's (as of 12/2020).
* DNSCheckValidation now fails for missing MX records. While the RFC argues that the existence of only A records to be valid, starting in v3 they will be considered invalid.
* Emails domain part are now intenteded to be RFC 1035 compliant, rendering previous valid emails (e.g example@examp&) invalid.
## PHP versions upgrade policy
PHP version upgrade requirement will happen via MINOR (3.x) version upgrades of the library, following the adoption level by major frameworks.
## Changes
* #235
* #215
* #130
* #258
* #188
* #181
* #217
* #214
* #249
* #236
* #257
* #210
## Thanks
To contributors, be it with PRs, reporting issues or supporting otherwise.
+54 -32
View File
@@ -1,8 +1,9 @@
# EmailValidator
[![Build Status](https://github.com/egulias/EmailValidator/workflows/tests/badge.svg?branch=4.x)](https://github.com/egulias/EmailValidator/actions)
[![Build Status](https://github.com/egulias/EmailValidator/actions/workflows/tests.yml/badge.svg)](https://github.com/egulias/EmailValidator/actions/workflows/tests.yml)
[![Quality Badge](https://app.codacy.com/project/badge/Grade/55d44898c7e44ebdb4e457523563ad63)](https://app.codacy.com/gh/egulias/EmailValidator/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Test Coverage](https://app.codacy.com/project/badge/Coverage/55d44898c7e44ebdb4e457523563ad63)](https://app.codacy.com/gh/egulias/EmailValidator/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
![Latest Release](https://img.shields.io/github/v/release/egulias/EmailValidator)
A library for validating emails against several RFC.
@@ -10,32 +11,36 @@ A library for validating emails against several RFC.
This library aims to support RFCs:
* [5321](https://tools.ietf.org/html/rfc5321),
* [5322](https://tools.ietf.org/html/rfc5322),
* [6530](https://tools.ietf.org/html/rfc6530),
* [6531](https://tools.ietf.org/html/rfc6531),
* [5321](https://tools.ietf.org/html/rfc5321),
* [5322](https://tools.ietf.org/html/rfc5322),
* [6530](https://tools.ietf.org/html/rfc6530),
* [6531](https://tools.ietf.org/html/rfc6531),
* [6532](https://tools.ietf.org/html/rfc6532),
* [1035](https://tools.ietf.org/html/rfc1035)
## Supported versions
**Current major version with full support is v3**
| Version | Released | EOL | Only critical bug fixes | Full |
|:-------:|:----------:|:---:|:-----------------------:|:----:|
| v4.x | 2023/01/07 | - | X | X |
| v3.x | 2020/12/29 | - | X | |
|**v4.x** |**2023/01/07** | - | **X** |**X** |
| v3.x | 2020/12/29 | YES | | |
| v2.1.x | 2016/05/16 | YES | | |
| v1.2 | 2013/19/05 | YES | | |
## Requirements
* PHP 8.1
* [Composer](https://getcomposer.org) is required for installation
* [Spoofchecking](/src/Validation/Extra/SpoofCheckValidation.php) and [DNSCheckValidation](/src/Validation/DNSCheckValidation.php) validation requires that your PHP system has the [PHP Internationalization Libraries](https://php.net/manual/en/book.intl.php) (also known as PHP Intl)
* PHP 8.1
* [Composer](https://getcomposer.org) is required for installation
* [Spoofchecking](/src/Validation/Extra/SpoofCheckValidation.php) and
[DNSCheckValidation](/src/Validation/DNSCheckValidation.php) validation
requires that your PHP system has the
[PHP Internationalization Libraries](https://php.net/manual/en/book.intl.php)
(also known as PHP Intl)
**Note**: `PHP version upgrades will happen to accomodate to the pace of major frameworks. Minor versions bumps will go via minor versions of this library (i.e: PHP7.3 -> v3.x+1). Major versions will go with major versions of the library`
**Note**: `PHP version upgrades will happen to accomodate to the pace of major
frameworks. Minor versions bumps will go via minor versions of this library
(i.e: PHP7.3 -> v3.x+1). Major versions will go with major versions
of the library`
## Installation
@@ -47,9 +52,12 @@ composer require egulias/email-validator
## Getting Started
`EmailValidator` requires you to decide which (or combination of them) validation/s strategy/ies you'd like to follow for each [validation](#available-validations).
`EmailValidator` requires you to decide which (or combination of them)
validation/s strategy/ies you'd like to follow for each
[validation](#available-validations).
A basic example with the RFC validation
```php
<?php
@@ -60,17 +68,22 @@ $validator = new EmailValidator();
$validator->isValid("example@example.com", new RFCValidation()); //true
```
### Available validations
1. [RFCValidation](/src/Validation/RFCValidation.php): Standard RFC-like email validation.
2. [NoRFCWarningsValidation](/src/Validation/NoRFCWarningsValidation.php): RFC-like validation that will fail when warnings* are found.
3. [DNSCheckValidation](/src/Validation/DNSCheckValidation.php): Will check if there are DNS records that signal that the server accepts emails. This does not entail that the email exists.
4. [MultipleValidationWithAnd](/src/Validation/MultipleValidationWithAnd.php): It is a validation that operates over other validations performing a logical and (&&) over the result of each validation.
5. [MessageIDValidation](/src/Validation/MessageIDValidation.php): Follows [RFC2822 for message-id](https://tools.ietf.org/html/rfc2822#section-3.6.4) to validate that field, that has some differences in the domain part.
6. [Your own validation](#how-to-extend): You can extend the library behaviour by implementing your own validations.
2. [NoRFCWarningsValidation](/src/Validation/NoRFCWarningsValidation.php):
RFC-like validation that will fail when warnings* are found.
3. [DNSCheckValidation](/src/Validation/DNSCheckValidation.php):
Will check if there are DNS records that signal that the server accepts emails. This does not entail that the email exists.
4. [MultipleValidationWithAnd](/src/Validation/MultipleValidationWithAnd.php):
It is a validation that operates over other validations performing a logical and (&&) over the result of each validation.
5. [MessageIDValidation](/src/Validation/MessageIDValidation.php):
Follows [RFC2822 for message-id](https://tools.ietf.org/html/rfc2822#section-3.6.4) to validate that field, that has some differences in the domain part.
6. [Your own validation](#how-to-extend): You can extend the library behaviour
by implementing your own validations.
*warnings: Warnings are deviations from the RFC that in a broader interpretation are accepted.
*warnings: Warnings are deviations from the RFC that in a broader interpretation
are accepted.
```php
<?php
@@ -90,29 +103,38 @@ $validator->isValid("example@ietf.org", $multipleValidations); //true
```
#### Additional validations
Validations not present in the RFCs
1. [SpoofCheckValidation](/src/Validation/Extra/SpoofCheckValidation.php): Will check for multi-utf-8 chars that can signal an erroneous email name.
Validations not present in the RFCs
1. [SpoofCheckValidation](/src/Validation/Extra/SpoofCheckValidation.php):
Will check for multi-utf-8 chars that can signal an erroneous email name.
### How to extend
It's easy! You just need to implement [EmailValidation](/src/Validation/EmailValidation.php) and you can use your own validation.
It's easy! You just need to implement
[EmailValidation](/src/Validation/EmailValidation.php) and you can use your own
validation.
## Contributing
Please follow the [Contribution guide](CONTRIBUTING.md). Is short and simple and will help a lot.
Please follow the [Contribution guide](CONTRIBUTING.md).
Is short and simple and will help a lot.
## Other Contributors
(You can find current contributors [here](https://github.com/egulias/EmailValidator/graphs/contributors))
(You can find current contributors
[here](https://github.com/egulias/EmailValidator/graphs/contributors))
As this is a port from another library and work, here are other people related to the previous one:
As this is a port from another library and work, here are other people related
to the previous one:
* Ricard Clau [@ricardclau](https://github.com/ricardclau): Performance against PHP built-in filter_var (v2 and earlier)
* Josepf Bielawski [@stloyd](https://github.com/stloyd): For its first re-work of Dominic's lib
* Dominic Sayers [@dominicsayers](https://github.com/dominicsayers): The original isemail function
* Ricard Clau [@ricardclau](https://github.com/ricardclau):
Performance against PHP built-in filter_var (v2 and earlier)
* Josepf Bielawski [@stloyd](https://github.com/stloyd):
For its first re-work of Dominic's lib
* Dominic Sayers [@dominicsayers](https://github.com/dominicsayers):
The original `isemail` function
## License
Released under the MIT License attached with this code.
Released under the MIT License attached with this code.
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.4.0@62db5d4f6a7ae0a20f7cc5a4952d730272fc0863">
<file src="src/Parser/DomainPart.php">
<RedundantConditionGivenDocblockType occurrences="1">
<code>null !== $this-&gt;lexer-&gt;token['type']</code>
</RedundantConditionGivenDocblockType>
</file>
</files>
+5 -10
View File
@@ -3,7 +3,11 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config ./vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm.baseline.xml"
errorLevel="1"
findUnusedCode="false"
findUnusedPsalmSuppress="true"
disableSuppressAll="true"
findUnusedVariablesAndParams="true"
>
<projectFiles>
<directory name="src" />
@@ -11,13 +15,4 @@
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<issueHandlers>
<DeprecatedMethod>
<errorLevel type="suppress">
<!-- This issue needs to be resolved before upgrading to Lexer 3 -->
<referencedMethod name="Doctrine\Common\Lexer\Token::offsetGet"/>
</errorLevel>
</DeprecatedMethod>
</issueHandlers>
</psalm>
+1 -2
View File
@@ -61,7 +61,7 @@ class EmailLexer extends AbstractLexer
/**
* US-ASCII visible characters not valid for atext (@link http://tools.ietf.org/html/rfc5322#section-3.2.3)
*
* @var array
* @var array<string, int>
*/
protected $charValue = [
'{' => self::S_OPENCURLYBRACES,
@@ -177,7 +177,6 @@ class EmailLexer extends AbstractLexer
* @throws \UnexpectedValueException
* @return boolean
*
* @psalm-suppress InvalidScalarArgument
*/
public function find($type): bool
{
+2 -1
View File
@@ -8,11 +8,12 @@ use Egulias\EmailValidator\Result\ValidEmail;
use Egulias\EmailValidator\Warning\CFWSNearAt;
use Egulias\EmailValidator\Result\InvalidEmail;
use Egulias\EmailValidator\Result\Reason\ExpectingATEXT;
use Egulias\EmailValidator\Warning\Warning;
class LocalComment implements CommentStrategy
{
/**
* @var array
* @var array<int, Warning>
*/
private $warnings = [];
+1
View File
@@ -307,6 +307,7 @@ class DomainPart extends PartParser
{
if (preg_match('/[^\x00-\x7F]/', $label)) {
idn_to_ascii($label, IDNA_DEFAULT, INTL_IDNA_VARIANT_UTS46, $idnaInfo);
/** @psalm-var array{errors: int, ...} $idnaInfo */
return (bool) ($idnaInfo['errors'] & IDNA_ERROR_LABEL_TOO_LONG);
}
return strlen($label) > self::LABEL_MAX_LENGTH;
+1
View File
@@ -34,6 +34,7 @@ class LocalPart extends PartParser
public function parse(): Result
{
$this->lexer->clearRecorded();
$this->lexer->startRecording();
while (!$this->lexer->current->isA(EmailLexer::S_AT) && !$this->lexer->current->isA(EmailLexer::S_EMPTY)) {
+1 -1
View File
@@ -11,6 +11,6 @@ class NoDNSRecord implements Reason
public function description() : string
{
return 'No MX or A DSN record was found for this email';
return 'No MX or A DNS record was found for this email';
}
}
+8 -4
View File
@@ -6,22 +6,26 @@ interface Result
{
/**
* Is validation result valid?
*
*/
public function isValid() : bool;
public function isValid(): bool;
/**
* Is validation result invalid?
* Usually the inverse of isValid()
*
*/
public function isInvalid() : bool;
public function isInvalid(): bool;
/**
* Short description of the result, human readable.
*
*/
public function description() : string;
public function description(): string;
/**
* Code for user land to act upon.
*
*/
public function code() : int;
public function code(): int;
}
+1 -2
View File
@@ -7,13 +7,12 @@ class DNSGetRecordWrapper
/**
* @param string $host
* @param int $type
*
*
* @return DNSRecords
*/
public function getRecords(string $host, int $type): DNSRecords
{
// A workaround to fix https://bugs.php.net/bug.php?id=73149
/** @psalm-suppress InvalidArgument */
set_error_handler(
static function (int $errorLevel, string $errorMessage): never {
throw new \RuntimeException("Unable to get DNS record for the host: $errorMessage");
+2 -2
View File
@@ -5,7 +5,7 @@ namespace Egulias\EmailValidator\Validation;
class DNSRecords
{
/**
* @param array $records
* @param list<array<array-key, mixed>> $records
* @param bool $error
*/
public function __construct(private readonly array $records, private readonly bool $error = false)
@@ -13,7 +13,7 @@ class DNSRecords
}
/**
* @return array
* @return list<array<array-key, mixed>>
*/
public function getRecords(): array
{
@@ -22,9 +22,6 @@ class SpoofCheckValidation implements EmailValidation
}
}
/**
* @psalm-suppress InvalidArgument
*/
public function isValid(string $email, EmailLexer $emailLexer) : bool
{
$checker = new Spoofchecker();
+12 -2
View File
@@ -2,16 +2,26 @@
namespace Egulias\EmailValidator\Warning;
use UnitEnum;
class QuotedPart extends Warning
{
public const CODE = 36;
/**
* @param scalar|null $prevToken
* @param scalar|null $postToken
* @param UnitEnum|string|int|null $prevToken
* @param UnitEnum|string|int|null $postToken
*/
public function __construct($prevToken, $postToken)
{
if ($prevToken instanceof UnitEnum) {
$prevToken = $prevToken->name;
}
if ($postToken instanceof UnitEnum) {
$postToken = $postToken->name;
}
$this->message = "Deprecated Quoted String found between $prevToken and $postToken";
}
}
+2 -2
View File
@@ -7,8 +7,8 @@ class QuotedString extends Warning
public const CODE = 11;
/**
* @param scalar $prevToken
* @param scalar $postToken
* @param string|int $prevToken
* @param string|int $postToken
*/
public function __construct($prevToken, $postToken)
{
+14
View File
@@ -28,4 +28,18 @@ class EmailParserTest extends TestCase
$this->assertEquals($local, $parser->getLocalPart());
$this->assertEquals($domain, $parser->getDomainPart());
}
public function testMultipleEmailAddresses()
{
$parser = new EmailParser(new EmailLexer());
$parser->parse('some-local-part@some-random-but-large-domain-part.example.com');
$this->assertSame('some-local-part', $parser->getLocalPart());
$this->assertSame('some-random-but-large-domain-part.example.com', $parser->getDomainPart());
$parser->parse('another-local-part@another.example.com');
$this->assertSame('another-local-part', $parser->getLocalPart());
$this->assertSame('another.example.com', $parser->getDomainPart());
}
}
@@ -32,8 +32,9 @@ class DNSCheckValidationTest extends TestCase
['"Fred\ Bloggs"@ietf.org'],
['"Joe.\\Blow"@ietf.org'],
// unicide
['ñandu.cl'],
// unicode
['info@ñandu.cl'],
['ñandu@ñandu.cl'],
];
}
@@ -150,6 +150,8 @@ class RFCValidationTest extends TestCase
['\r\n \r\n test@iana.org'],
['test;123@foobar.com'],
['examp║le@symfony.com'],
['example@invalid-.domain.com'],
['example@-invalid.com'],
['0'],
[0],
];