Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/Illuminate/Support/Str.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Illuminate\Support;

use Closure;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
use Illuminate\Support\Traits\Macroable;
use JsonException;
use League\CommonMark\Environment\Environment;
Expand Down Expand Up @@ -2011,6 +2013,19 @@ public static function freezeUlids(?Closure $callback = null)
return $ulid;
}

/**
* Determine if a given value is a valid Email.
*
* @param mixed $value
* @return bool
*/
public static function isEmail(string $value): bool
{
$validator = new EmailValidator();

return $validator->isValid($value, new RFCValidation());
}
Comment on lines +2022 to +2027
Copy link
Contributor

@shaedrich shaedrich Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optionally, as the email validation rule allows, we could allow the developer to specify which validation style they want to use.

Suggested change
public static function isEmail(string $value): bool
{
$validator = new EmailValidator();
return $validator->isValid($value, new RFCValidation());
}
public static function isEmail(string $value, ?string $rule = null): bool
{
$validator = new EmailValidator();
return $validator->isValid($value, match ($rule) {
'strict' => new NoRFCWarningsValidation(),
'dns' => new DNSCheckValidation(),
'spoof' => new SpoofCheckValidation(),
'filter' => new FilterEmailValidation(),
default => new RFCValidation(),
});
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a string, $rule could probably be made an enum 🤔

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shaedrich , i think it must be enum 100%

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum could even do the mapping itself:

enum EmailValidation
{
    case NoRfcWarnings;
    case DnsCheck;
    case SpoofCheck;
    case Filter;
    case Rfc;

    public function validation()
    {
        return match ($this) {
            self::NoRfc => new NoRFCWarningsValidation(),
            self::Dns => new DNSCheckValidation(),
            self::SpoofCheck => new SpoofCheckValidation(),
            self::Filter => new FilterEmailValidation(),
            default => new RFCValidation(),
        }
    }
}
Suggested change
public static function isEmail(string $value): bool
{
$validator = new EmailValidator();
return $validator->isValid($value, new RFCValidation());
}
public static function isEmail(string $value, ?EmailValidation $rule = null): bool
{
$validator = new EmailValidator();
return $validator->isValid($value, $rule?->validation() ?? new RFCValidation());
}


/**
* Remove all strings from the casing caches.
*
Expand Down
42 changes: 42 additions & 0 deletions tests/Support/SupportStrTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,48 @@ public function testChopEnd()
$this->assertSame($expected, Str::chopEnd($subject, $needle));
}
}

public function testValidEmails()
{
// Standard valid email formats according to RFC 5322
$this->assertTrue(Str::isEmail('example@laravel.com'));
$this->assertTrue(Str::isEmail('user.name+alias@domain.co.uk'));
$this->assertTrue(Str::isEmail('123456@domain.com'));
$this->assertTrue(Str::isEmail('user@sub.domain.com'));
$this->assertTrue(Str::isEmail('a@b.co'));
$this->assertTrue(Str::isEmail('user@domain.org'));
$this->assertTrue(Str::isEmail('user_name@domain.com'));
$this->assertTrue(Str::isEmail('user-name@domain.io'));
$this->assertTrue(Str::isEmail('user@localhost'));
$this->assertTrue(Str::isEmail('user@123.123.123.123'));
$this->assertTrue(Str::isEmail('"user@name"@example.com'));
$this->assertTrue(Str::isEmail('"john.doe"@example.com'));
$this->assertTrue(Str::isEmail('very.common@example.com'));
$this->assertTrue(Str::isEmail('disposable.style.email.with+symbol@example.com'));
$this->assertTrue(Str::isEmail('user@domain.toolongtld'));
$this->assertTrue(Str::isEmail('user@domain.c'));
$this->assertTrue(Str::isEmail('user@[192.168.1.1]'));

$this->assertFalse(Str::isEmail('plainaddress'));
$this->assertFalse(Str::isEmail('@missinglocal.com'));
$this->assertFalse(Str::isEmail('username@.com'));
$this->assertFalse(Str::isEmail('username@com.'));
$this->assertFalse(Str::isEmail('username@domain..com'));
$this->assertFalse(Str::isEmail('user@domain,com'));
$this->assertFalse(Str::isEmail('user@domain@another.com'));
$this->assertFalse(Str::isEmail('username@-domain.com'));
$this->assertFalse(Str::isEmail('username@domain-.com'));
$this->assertFalse(Str::isEmail('user@domain.com '));
$this->assertFalse(Str::isEmail(' user@domain.com'));
$this->assertFalse(Str::isEmail('username@domain.com#'));
$this->assertFalse(Str::isEmail('username@domain..com'));
$this->assertFalse(Str::isEmail('user@sub..domain.com'));
$this->assertFalse(Str::isEmail('user@.com'));
$this->assertFalse(Str::isEmail('"user@name"@example..com'));
$this->assertFalse(Str::isEmail('user.@domain.com'));
$this->assertFalse(Str::isEmail('user..name@domain.com'));
$this->assertFalse(Str::isEmail('user@sub_domain.com'));
}
}

class StringableObjectStub
Expand Down
Loading