Benjamin Cremer
@benjamincremer
Once a month. Speakers wanted!
meetup.com/phpugms
@phpugms
// booking controller
throw new \InvalidArgumentException(
sprintf('Booking by uuid "%s" not found', $request->getUuid()),
4321
);
// a few lines later...
throw new \InvalidArgumentException(
sprintf('Booking by uuid "%s" not found', $request->getUuid()),
4321
);
// a few lines later...
throw new \InvalidArgumentException(
sprintf('Booking by uuid "%s" not found', $request->getUuid()),
4321
);
class BookingNotFoundException extends \InvalidArgumentException
{
public const MESSAGE_TEMPLATE = 'Booking by uuid "%s" not found'
private $code = 4321;
}
throw new \BookingNotFoundException(
sprintf(
BookingNotFoundException::MESSAGE_TEMPLATE,
$request->getUuid()
)
);
final class BookingNotFoundException extends \InvalidArgumentException
{
private $code = 4321;
public static function createFromUuid($uuid)
{
return new self(sprintf(
'Booking by uuid "%s" not found', $uuid
));
}
}
throw \BookingNotFoundException::createFromUuid(
$request->getUuid()
);
abstract class AbstractClass
{
// Public API
public function doSomething()
{
$this->step1();
$this->step2();
}
// Template methods
abstract protected function step1();
abstract protected function step2();
}
class ConcreteClass extends AbstractClass
{
protected function step1() { […] }
protected function step2() { […] }
}
$baz = new ConcreteClass();
$baz->doSomething();
abstract class AbstractClass
{
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function doSomething()
{
$this->step1();
$this->step2();
$this->logger->log('Did Something');
}
}
class ConcreteClass extends AbstractClass
{
private $db;
public function __construct(Connection $db)
{
$this->db = $db;
}
protected function step1() { $this->db->exec() }
protected function step2() { […] }
}
class ConcreteClass extends AbstractClass
{
public function __construct(Connection $db)
{
$this->db = $db;
}
protected function step1() { […] }
protected function step2() { […] }
}
class ConcreteClass extends AbstractClass
{
public function __construct(
Connection $db
Logger $logger,
) {
$this->db = $db;
parent::__construct($logger);
}
protected function step1() { […] }
protected function step2() { […] }
}
This is a runtime error, not a compile time error
Side effects of changing something in a base class
https://codeburst.io/inheritance-is-evil-stop-using-it-6c4f1caf5117
final class MyFacade
{
public function __construct(ConcreteClass $myImplementation, Logger $logger)
{
$this->myImplementation = $myImplementation;
$this->logger = $logger;
}
public function doSomething()
{
$this->myImplementation->step1();
$this->myImplementation->step2();
$this->logger->log(..);
}
}
class ConcreteClass
{
public function __construct(Connection $db)
{
$this->db = $db;
}
public function step1() { […] }
public function step2() { […] }
}
$baz = new MyFacade(
new ConcreteClass(new DB())
new Logger();
);
$baz->doSomething();
Class BookingCache extends RedisCacheAdapter
{
public function addBooking(Bookinging $booking): void
{
$this->put($booking->getUuid(), $booking);
}
public function getBookingByUuid(string $uuid): Bookinging
{
return $this->get($uuid);
}
}
Class BookingCache
{
private $cache;
public function __construct(CacheAdapterInterface $cache) {}
public function addBooking(Bookinging $booking): void
{
$this->cache->put($booking->getUuid(), $booking);
}
public function getBookingByUuid(string $uuid): Bookinging
{
return $this->cache->get($uuid);
}
}
// return true to win
function foo($x) {
return $x === $x();
}
foo( ? );
function foo($x) {
return $x === $x();
}
class MyClass
{
function __invoke()
{
return $this;
}
}
foo(
new MyClass()
);
function foo($x) {
return $x === $x();
}
foo(
new class { function __invoke() { return $this; }}
);
function foo($x) {
return $x === $x();
}
foo(
$x = function () use ($x) { return $x; }
);
Notice: Undefined variable: x in ...
function foo($x) {
return $x === $x();
}
foo(
$x = function () use (&$x) { return $x; }
);
function foo($x) {
return $x === $x();
}
function a() {return 'a';}
foo('a');
class SomeClass
{
private $foo;
}
class SomeClass
{
private $foo;
public function poke(self $other)
{
$other->foo = $this->foo; // access to private property
}
}
final class BookingNotFoundException
{
private $uuid;
private function __construct()
{
// empty
}
public static function createFromUuid($uuid): self
{
$object = new self();
$object->uuid = $uuid;
return $object;
}
}
class Kitchen
{
private $cake = 'yummy';
}
$kitchen = new Kitchen();
$thief = function($kitchen) {
return $kitchen->cake;
};
$cake = $thief($kitchen);
PHP Fatal error: Cannot access private property Kitchen::$yummy in […]
$thief = function($kitchen) {
return $kitchen->cake;
};
$thief->bindTo(null, $kitchen);
$cake = $thief($kitchen);
class Color
{
private $red;
private $green;
private $blue;
public function __construct(int $red, int $green, int $blue)
{
foreach ([$red, $green, $blue] as $color) {
if ($color < 0 || $color > 255) {
throw new InvalidArgumentException(
'Color values must be between 0 and 255'
);
}
}
$this->red = $red;
$this->green = $green;
$this->blue = $blue;
}
}
final class Color
{
private $red;
private $green;
private $blue;
private function __construct() {}
public static function createFromHexstring(string $hex): self
{
$parts = str_split($hex, 2);
return self::createFromRGB(
hexdec($parts[0]),
hexdec($parts[1]),
hexdec($parts[2]),
);
}
public static function createFromRGB(int $red, int $green, int $blue): self
{
// validation […]
$color = new self();
$color->red = $red;
$color->green = $green;
$color->blue = $blue;
return $color;
}
}
$red = Color::createFromRGB(255, 0, 0);
$blue = Color::createFromHexstring('0000FF')
final class Color
{
private $red;
private $green;
private $blue;
[…]
public function isEqualTo(Color $color): bool
{
return $this->red === $color->red
&& $this->green === $color->green
&& $this->blue === $color->blue;
}
}
$red = Color::createFromRGB(255, 0, 0);
$blue = Color::createFromHexstring('0000FF');
$anotherRed = Color::createFromHexstring('FF0000');
$red->isEqualTo($blue); // false
$red->isEqualTo($anotherRed); // true
final class Color
{
private $red;
private $green;
private $blue;
[…]
public function mix(Color $color): self
{
$mixedColor = new self();
$mixedColor->red = min([$this->red + $color->red, 255]);
$mixedColor->green = min([$this->green + $color->green, 255]);
$mixedColor->blue = min([$this->blue + $color->blue, 255]);
return $mixedColor; // return new instance
}
}
$red = Color::createFromRGB(255, 0, 0);
$green = Color::createFromRGB(0, 255, 0);
$yellow = $red->mix($green);
Duplication is far cheaper than the wrong abstraction — Sandi Metz, RailsConf 2014Sandi Metz, "The Wrong Abstraction"
You aren't gonna need it
Always implement things when you actually need them, never when you just foresee that you need them. — Ron JeffriesRon Jeffries (April 4, 1998). "You're NOT gonna need it!"
$ php fizzbuzz.php 1 2 Fizz // divisible by three 4 Buzz // divisible by five Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz // divisible by three AND five 16
foreach(range(1,100) as $i) {
if ($i % 3 == 0 && $i % 5 ==0) {
echo "FizzBuzz\n";
} else if($i % 3 == 0){
echo "Fizz\n";
} else if($i % 5 == 0){
echo "Buzz\n";
} else {
echo $i."\n";
}
}
foreach(range(1,100) as $i) {
$val = ($i % 3 == 0 ? "Fizz" : "").($i % 5 == 0 ? "Buzz" : "");
echo (empty($val) ? $i : $val)."\n";
}
$fizzers = [1, 0, 0, 0];
$buzzers = [2, 0, 0, 0, 0];
$words = [null, 'Fizz', 'Buzz', 'FizzBuzz'];
foreach (range(1, 100) as $i) {
$words[0] = $i;
echo $words[$fizzers[$i % 3] + $buzzers[$i % 5]]."\n";
}
Code is harder to read, than to write!
Please leave feedback