I'm trying to improve my limited knowledge in PHP (7) and I wrote these two simple classes as a proof of concept.
They both have just one (private) property ->email and they run validation before setting a value for it (unless it's null).
They are both supposed to run at business-tier
GtokenA uses magic methods and is more strict than GtokenB. It does not allow to set any property that is not declared.
<?php
class GtokenA
{
/**
* @var string $email
*/
protected $email;
public function __construct($email = null)
{
$this->__set('email', $email);
}
public function __get($property)
{
if(property_exists($this,$property)){
return $this->$property;
}
}
public function __set($property, $value)
{
if (!property_exists($this, $property)) {
throw new InvalidArgumentException("The class " . get_class($this) . " does not have the property " . $property);
}
switch ($property) {
case 'email':
// a null value is acceptable
// otherwise I need the email to be a valid recipient
$validator = new \EmailValidator\Validator();
try {
if(!is_null($value) && !$validator->isSendable($value)) {
throw new UnexpectedValueException("Wrong value for ->$property");
}
} catch (Exception $e) {
throw new UnexpectedValueException("Wrong value for ->$property : " . $e->getMessage());
}
break;
}
$this->$property = $value;
return $this;
}
}
GtokenB uses setters and getters:
<?php
class GtokenB
{
/**
* @var string $email
*/
private $email;
public function __construct($email = null)
{
$this->setEmail($email);
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email = null)
{
// a null email is acceptable
// otherwise I need the email to be a valid recipient
$validator = new \EmailValidator\Validator();
try {
if(!is_null($email) && !$validator->isSendable($email)) {
throw new UnexpectedValueException("Wrong email for ->email");
}
} catch (Exception $e) {
throw new UnexpectedValueException("Wrong email for ->email : " . $e->getMessage());
}
$this->email = $email;
return $this;
}
}
I also wrote some (hopefully meaningful) tests for my first time ever.
GtokenA tests:
<?php
require_once 'app/src/models/gtokenB.php';
/**
* GtokenA test case.
*/
class GtokenBTest extends PHPUnit_Framework_TestCase
{
/**
* @covers __construct()
*/
public function test__construct_withArguments()
{
$email = "[email protected]";
$gtoken = new GtokenB($email);
$this->assertEquals($email, $gtoken->getEmail());
}
/**
* @covers __construct()
**/
public function test_construct_noArguments()
{
$gtoken = new GtokenB();
$gtoken->setEmail(null);
$this->assertEquals(null, $gtoken->getEmail());
}
public function validEmailStringProvider(){
return array(
'valid_1' => array("[email protected]", "[email protected]"),
'valid_2' => array("[email protected]", "[email protected]"),
'valid_3' => array(null,null),
'valid_4' => array("[email protected]", "[email protected]"),
);
}
/**
* @covers Gtoken::__set()
* @dataProvider validEmailStringProvider
*/
public function test_setter_Email($email, $expected)
{
$gtoken = new GtokenB();
$gtoken->setEmail($email);
$this->assertEquals($expected, $gtoken->getEmail());
}
public function invalidEmailString()
{
return array(
"invalid_1" => array("email-examplecom"),
"invalid_2" => array("email-example.com"),
"invalid_3" => array("email\\\"@example.com"),
"invalid_4" => array("email@example!.com"),
"invalid_6" => array("email@example$.com"),
"invalid_7" => array("[email protected]"),
"invalid_8" => array("email\@example.com"),
"invalid_9" => array("[email protected]."),
"invalid_10" => array("[email protected]"),
'integer' => array(123, null),
'array_0' => array(array(), null),
'array_1' => array(array('[email protected]'), null),
);
}
/**
* @covers Gtoken::__set()
* @expectedException UnexpectedValueException
* @dataProvider invalidEmailString
**/
public function test_setter_Email_throwsUnexpectedValueException($email)
{
$gtoken = new GtokenB();
$gtoken->setEmail($email);
}
}
GtokenB tests:
<?php
require_once 'app/src/models/gtokenA.php';
/**
* GtokenA test case.
*/
class GtokenATest extends PHPUnit_Framework_TestCase
{
/**
* @covers __construct()
*/
public function test__construct_withArguments()
{
$email = "[email protected]";
$gtoken = new GtokenA($email);
$this->assertEquals($email, $gtoken->email);
}
/**
* @covers __construct()
**/
public function test_construct_noArguments()
{
$gtoken = new GtokenA();
$gtoken->email = null;
$this->assertEquals(null, $gtoken->email);
}
/**
* @covers __set()
* @expectedException InvalidArgumentException
**/
public function test_set_notExistentProperty()
{
$gtoken = new GtokenA();
$gtoken->kk = null;
}
public function validEmailStringProvider(){
return array(
'valid_1' => array("[email protected]", "[email protected]"),
'valid_2' => array("[email protected]", "[email protected]"),
'valid_3' => array(null,null),
'valid_4' => array("[email protected]", "[email protected]"),
);
}
/**
* @covers Gtoken::__set()
* @dataProvider validEmailStringProvider
*/
public function test_setter_Email($email, $expected)
{
$gtoken = new GtokenA();
$gtoken->email = $email;
$this->assertEquals($expected, $gtoken->email);
}
public function invalidEmailString()
{
return array(
"invalid_1" => array("email-examplecom"),
"invalid_2" => array("email-example.com"),
"invalid_3" => array("email\\\"@example.com"),
"invalid_4" => array("email@example!.com"),
"invalid_6" => array("email@example$.com"),
"invalid_7" => array("[email protected]"),
"invalid_8" => array("email\@example.com"),
"invalid_9" => array("[email protected]."),
"invalid_10" => array("[email protected]"),
'integer' => array(123, null),
'array_0' => array(array(), null),
'array_1' => array(array('[email protected]'), null),
);
}
/**
* @covers Gtoken::__set()
* @expectedException UnexpectedValueException
* @dataProvider invalidEmailString
**/
public function test_setter_Email_throwsUnexpectedValueException($email)
{
$gtoken = new GtokenA();
$gtoken->email = $email;
}
}
I prefer GtokenA by far. It feels more elegant in the usage ( $obj->email = "value"
instead of $obj->setEmail("value")
) but, most important, it's strict and I could write and run this test:
/**
* @covers __set()
* @expectedException InvalidArgumentException
**/
public function test_set_notExistentProperty()
{
$gtoken = new GtokenA();
$gtoken->kk = null;
}
But maybe this is just a personal preference that brings no value to the code.
Questions:
- How do you rate my code?
- Would you suggest any improvement?
- Do I use Exceptions in a meaningful way?