Named Constructors
In PHP
About Me
Conor Smith
Senior Developer, Journal Media
@conorsmith




We are hiring
careers.thejournal.ie
A Constructor
class DisplayColour
{
public function __construct(int $red, int $green, int $blue)
{
if ($red < 0 || $red > 255) {
throw new \DomainException("Red value must be between 0 and 255");
}
// More guard statements...
$this->red = $red;
$this->green = $green;
$this->blue = $blue;
}
}
A "Flexible" Constructor
class DisplayColour
{
public function __construct(int $redOrHue, int $greenOrSat, int $blueOrLight, bool $isRgb = true)
{
if ($isRgb) {
if ($redOrHue < 0 || $redOrHue > 255) {
throw new \DomainException("Red value must be between 0 and 255");
}
} else {
if ($redOrHue < 0 || $redOrHue > 360) {
throw new \DomainException("Hue value must be between 0 and 360");
}
}
// More guard statements...
if ($isRgb) {
$this->red = $redOrHue;
$this->green = $greenOrSat;
$this->blue = $blueOrLight;
} else {
// Convert HSL values to RGB values...
}
}
}
A "Flexibler" Constructor
class DisplayColour
{
public function __construct(
$redOrHueOrHexValue,
int $greenOrSat = null,
int $blueOrLightness = null,
bool $isNotHsl = true
) {
// Here be dragons...
}
}
A Named Constructor
class DisplayColour
{
public static function fromHex(string $value)
{
if (strlen($value) !== 6) {
throw new \DomainException("Hex value must be 6 characters long.");
}
// More guard statements...
return new self(
$red = hextodec(substr($value, 0, 2)),
$green = hextodec(substr($value, 2, 2)),
$blue = hextodec(substr($value, 4, 2))
);
}
}
Another Named Constructor
class DisplayColour
{
public static function fromHsl(int $hue, int $saturation, int $lightness)
{
if ($hue < 0 || $hue > 360) {
throw new \DomainException("Hue value must be between 0 and 360.");
}
// More guard statements...
// Convert HSL values to RGB values...
return new self($red, $green, $blue);
}
}
The Actual Constructor
class DisplayColour
{
public static function fromRgb(int $red, int $green, int $blue)
{
return new self($red, $green, $blue);
}
private function __construct(int $red, int $green, int $blue)
{
if ($red < 0 || $red > 255) {
throw new \DomainException("Red value must be between 0 and 255.");
}
// More guard statements...
$this->red = $red;
$this->green = $green;
$this->blue = $blue;
}
}
Same Interface,
Different Implementation
class DisplayColour
{
public static function fromRgb(int $red, int $green, int $blue) { ... }
public static function fromHsl(int $hue, int $saturation, int $lightness) { ... }
public static function fromHex(string $value) { ... }
private function __construct(int $hue, int $saturation, int $lightness)
{
// Guard statements...
$this->hue = $hue;
$this->saturation = $saturation;
$this->lightness = $lightness;
}
}
Why?
Readability
class DisplayColour
{
public static function fromRgb(int $red, int $green, int $blue) { ... }
public static function fromHsl(int $hue, int $saturation, int $lightness) { ... }
public static function fromHex(string $value) { ... }
}
class DisplayColour
{
public function __construct(
$redOrHueOrHexValue,
int $greenOrSaturation = null,
int $blueOrLightness = null,
bool $isNotHsl = true
) {
...
}
}
vs
Extensibility
// Import some colour values as strings...
$bgHex = "ffe375";
$fgHex = "333";
$backgroundColour = DisplayColour::fromHex($bgHex);
// This won't work...
$foregroundColour = DisplayColour::fromHex($fgHex);
// Add a new named constructor!
$foregroundColour = DisplayColour::fromShortHex($fgHex);
Expressiveness
$colour = new DisplayColour(232, 80, 66);
$colour = DisplayColour::fromRgb(232, 80, 66);
RGB
HSL
$red = DisplayColour::fromRgb(232, 80, 66);
Real World
Examples
League\Url
use League\Url\Url;
$exampleUrl = Url::createFromUrl("http://example.com");
$currentUrl = Url::createFromServer($_SERVER);
Symfony Http Request
use Symfony\Component\HttpFoundation\Request;
$serverRequest = Request::createFromGlobals();
$testRequest = Request::create("/some-endpoint", "GET");
Carbon
use Carbon\Carbon;
$date = Carbon::createFromDate(2015, 12, 31, "Europe\Dublin");
$lunchtimeToday = Carbon::createFromTime(13, 0, 0, "Europe\Dublin");
$dbDate = Carbon::createFromFormat("Y-m-d H:i:s", "2015-12-31 13:00:00");
$date = Carbon::createFromTimestamp(1451566800);
$now = Carbon::now();
$date = Carbon::parse("Last day of December 2015");
League\Csv
use League\Csv\Reader;
$csv = Reader::createFromPath("/tmp/data.csv");
$file = new SplFileObject("/tmp/data.csv");
$csv = Reader::createFromFileObject($file);
$data = "league,csv\nsymfony,request\nnesbot,carbon";
$csv = Reader::createFromString($data, "\n");
Naming Conventions
$oneHundred = Emoji::fromCode(":100:");
echo $oneHundred->toCode(); // :100:
$date = Carbon::createFromTimestamp(1451566800);
$plusOne = Emoji::thumbsUp()
return Emoji::null();
Start Tomorrow
The next time you make a change to a Value Object in your codebase, give it a named constructor.
Questions?
Named Constructors in PHP
By Conor Smith
Named Constructors in PHP
PHP Dublin, April 21st 2016 http://blog.conorsmith.ie/named-constructors-in-php/
- 1,899