Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Add EmailAddress value object #93

Closed
ro0NL opened this issue Apr 24, 2016 · 3 comments
Closed

Proposal: Add EmailAddress value object #93

ro0NL opened this issue Apr 24, 2016 · 3 comments

Comments

@ro0NL
Copy link
Contributor

ro0NL commented Apr 24, 2016

Hi,

What do you think of a EmailAddress value object, to clearify;

try {
    $email = EmailAddress::fromString('Alias+metadata@domain.COM');
    //$email = EmailAddress::fromString('Alias+metadata@domain.COM', new EmailValidator());
    //$email = new EmailAddress('Alias+metadata', 'domain.COM');
} catch (InvalidEmailAddressException $e) {
    // $e->getEmailValidator();
    echo $e->getEmailAddress() . ': ' . $e->getMessage();
    exit;
}
$email->getHost(); // "domain.COM"
$email->getLocal(); /** and/or */ $email->getAlias(); // "Alias+metadata"
$email->getMetadata(); // array('plus_sign' => 'metadata'); // not sure how this is called :)
$email->normalize()->getHost(); // "domain.com"
$email->normalize()->getAlias(); // "alias+metadata"
$email->equals($otherEmail); // true|false
(string) $email; // "Alias+metadata@domain.COM"
(string) $email->normalize(); // "alias+metadata@domain.com"

I think this would be a useful addition to the library...

@egulias
Copy link
Owner

egulias commented Apr 28, 2016

Hi @ro0NL
Nice proposal, thanks for taking the time.
How would you manage warnings? Something like $email->getWarnings()?
What do you think of
$email = new EmailAddress('Alias+metadata', 'domain.COM', new EmailValidator([new RFCValidation()]));? (I'm thinking of v2 of the lib here).

I don't fully agree with $email->normalize()->getHost();
What about

$host = $email->getHost(); // Host
$host->normalize(); //domain.com
(string)$host // domain.COM

and the same for Local ? So we homogenize normalize() behaviour.

@ro0NL
Copy link
Contributor Author

ro0NL commented Apr 29, 2016

@egulias

new EmailValidator([new RFCValidation()])

I like it :) This is also being discussed at symfony for their EmailValidator, calling it profiles (symfony/symfony#18428), maybe you can collaborate to speed things up, i.e. your v2 lib could then simplify the symfony email validator a lot.


$email = new EmailAddress('Alias+metadata', 'domain.COM', new EmailValidator([new RFCValidation()]));

I'm not sure the constructor needs a validator / should perform any validation, as it's a value object. I would keep it really simple;

public function __construct($local, $host = null) {
    if ($host ===  null) {
        if (false === ($atPos = mb_strrpos($local, '@'))) {
            throw new \DomainException('Malformed email: ' . $local . ' (missing @)');
        }
        $host = mb_substr($local, ($atPos + 1));
        $local = mb_substr($local, 0, $atPos);
    }
    $this->local = $local;
    $this->host = $host;
}

A factory (method), i.e. fromString could perform built-in validation, ensuring a correct email is passed to the constructor. Constructing the VO yourself means you have knowledge of the data which should be valid (e.g. loaded ORM data).


About warnings, i'm not sure a VO object should depepd on any validation knowledge, as it's part of the validation process and the VO just represents data. I.e. you should get warnings from the validator when validating (it would be weird to __construct the VO with warnings yourself...), but I understand this requires some thought. Maybe fromString could work like this;

public static function fromString($email, array &$warnings = null, EmailValidator $validator = null) {
    // not sure about the EmailValidator internals, but you get the point
    $validator = $validator ?: new EmailValidator();
    $valid = $validator->isValid($email);
    $warnings = $validator->getWarnings();
    unset($warnings); //reference
    if (!$valid) {
        throw new InvalidEmailException($email, $validator);
    }

    return new static($email); // perhaps decompose $email here instead of in the constructor

Last; the normalization thingy. What i meant was a fluent interface;

public function normalize() {
    $local = mb_strtolower($this->local); // whatever normalization is needed
    $host = mb_strtolower($this->host); // whatever normalization is needed

    return new self($local, $host);
}

I think individual email part VO's (LocalPart, HostPart, EmailPartInterface) would be over-enginering the address VO. Perhaps the normalization itself could be moved to a dedicated class (EmailNormalizer).

@ro0NL
Copy link
Contributor Author

ro0NL commented Apr 29, 2016

About naming..i alwast thought it was;

foo.bar.com:80 = host
foo.bar.com = hostname
foo = domain
bar = domain
com = domain

and therefor the correct keyword should be hostname, however im reading some specs now which is pretty confusing :) but i think domain will do just fine and is probably correct anyway :/ as long as its consistent.

@ro0NL ro0NL closed this as completed Mar 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants