>>>Pytania?
Zadawaj je, oceniaj prelekcję, komentuj
lub polub poprzez sli.do:
WarszawskieDniInformatyki.pl/slido
SOLID code in practice
creating good object-oriented
PHP applications
/prgTW /prgTW /prgTW
Senior developer, 9+ years of experience
Tomasz Wójcik
WRITING SOLID CODE IN PRACTICE
SRP - Single Responsibility Principle
OCP - Open/Closed Principle
LSP - Liskov Substitution Principle
ISP - Interface Segregation Principle
DIP - Dependency Inversion Principle
What SOLID stands for
WRITING SOLID CODE IN PRACTICE
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
• class/module should have only single responsibility
• the above is defined as single reason for change
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}

Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}













$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}





















file_put_contents($cacheFile, $randomEmoji);
cache control
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}



























$response = file_get_contents('https://api.github.com/emojis');

fetching data
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}





























if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

} error handling
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}







































$emojis = json_decode($response, true);
data transformation
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}









































$randomEmoji = array_rand($emojis);






return $randomEmoji;
random emoji logic
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
class FileCache
class FileGetContentsFetcher
throw new Exception
class JsonSerializer
class RandomGithubEmoji
• cache control
• fetching data
• error handling
• data transformation
• random emoji logic
Single Responsibility Principle
custom one
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{















$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);







return $randomEmoji;

}

}













$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}





















file_put_contents($cacheFile, $randomEmoji);
cache control
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{















$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);







return $randomEmoji;

}

}

Single Responsibility Principle
cache control












$cache = new FileCache(__DIR__ . '/random_emoji');



$randomEmoji = $cache->fetch();

if (null !== $randomEmoji)

{

return $randomEmoji;

}



















$cache->put($randomEmoji);

WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}





if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}



























$response = file_get_contents('https://api.github.com/emojis');

fetching data
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}





if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}

Single Responsibility Principle
fetching data


























$response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');
WRITING SOLID CODE IN PRACTICE
































return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

error handling
Single Responsibility Principle
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{



}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}

WRITING SOLID CODE IN PRACTICE
Single Responsibility Principle
error handling
































throw new UnableToFetchEmojiException;
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{



}



$emojis = json_decode($response, true);

$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}

WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}





$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}







































$emojis = json_decode($response, true);
data transformation
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cacheFile = __DIR__ . '/random_emoji';



if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)

{

return file_get_contents($cacheFile);

}



$response = file_get_contents('https://api.github.com/emojis');

if (!$response)

{

return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';

}





$randomEmoji = array_rand($emojis);



file_put_contents($cacheFile, $randomEmoji);



return $randomEmoji;

}

}

Single Responsibility Principle
data transformation






































$emojis = (new JsonSerializer)->deserialize($response, 'json');
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cache = new FileCache(__DIR__ . '/random_emoji');

$randomEmoji = $cache->fetch();

if (null !== $randomEmoji)

{

return $randomEmoji;

}



$response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');

if (!$response)

{

throw new UnableToFetchEmojiException;

}



$emojis = (new JsonSerializer)->deserialize($response, 'json');

$randomEmoji = array_rand($emojis);



$cache->put($randomEmoji);



return $randomEmoji;

}

}
Single Responsibility Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{

public function fetch(): string

{

$cache = new FileCache(__DIR__ . '/random_emoji');

$randomEmoji = $cache->fetch();

if (null !== $randomEmoji)

{

return $randomEmoji;

}



$response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');

if (!$response)

{

throw new UnableToFetchEmojiException;

}



$emojis = (new JsonSerializer)->deserialize($response, 'json');

$randomEmoji = array_rand($emojis);



$cache->put($randomEmoji);



return $randomEmoji;

}

}












new FileCache(__DIR__ . '/random_emoji');













new FileGetContentsFetcher











new JsonSerializer
We still have concrete dependencies
WRITING SOLID CODE IN PRACTICE
Dependency Injection
not a part of SOLID but closely related
WRITING SOLID CODE IN PRACTICE
• providing dependencies from outside of the dependent class
• class mustn’t use new or static methods
Dependency Injection
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{
/** @var FileCache */

private $cache;



/** @var FileGetContentsFetcher */

private $fetcher;



/** @var JsonSerializer */

private $serializer;



public function __construct(FileCache $cache, FileGetContentsFetcher $fetcher, JsonSerializer $serializer)
{

$this->cache = $cache;

$this->fetcher = $fetcher;

$this->serializer = $serializer;

}


// …
}
Dependency Injection
WRITING SOLID CODE IN PRACTICE
Dependency Inversion Principle
WRITING SOLID CODE IN PRACTICE
• used to decouple software modules
• high-level modules shouldn’t depend on low-level modules, they both should
depend on abstractions
• variables must be created via injection or creational patterns (eg. factories)
• class members should be interfaces or abstracts
• class shouldn’t derive from concretions (composition over inheritance)
• methods shouldn’t derive from already implemented methods
Dependency Inversion Principle
WRITING SOLID CODE IN PRACTICE
Dependency Inversion Principle
https://en.wikipedia.org/wiki/Dependency_inversion_principle
WRITING SOLID CODE IN PRACTICE
Dependency Inversion Principle
WRITING SOLID CODE IN PRACTICE
Dependency Inversion Principle
https://en.wikipedia.org/wiki/Dependency_inversion_principle
WRITING SOLID CODE IN PRACTICE
Dependency Inversion Principle
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{
/** @var CacheInterface */

private $cache;



/** @var FetcherInterface */

private $fetcher;



/** @var SerializerInterface */

private $serializer;



public function __construct(Cache Interface $cache , FetcherInterface $fetcher, SerializerInterface $serializer)
{

$this->cache = $cache;

$this->fetcher = $fetcher;

$this->serializer = $serializer;

}


// ...
}
Dependency Injection
WRITING SOLID CODE IN PRACTICE
<?php



class RandomGithubEmoji

{
/** @var Cache Interface */

private $cache ;



/** @var FetcherInterface */

private $fetcher;



/** @var SerializerInterface */

private $serializer;



public function __construct(Cache Interface $cache , FetcherInterface $fetcher, SerializerInterface $serializer)
{

$this->cache = $cache 

$this->fetcher = $fetcher;

$this->serializer = $serializer;

}


// ...
}
Dependency Injection
























CacheFactoryInterface $cacheFactory


$this->cache = $cacheFactory->createCache(__DIR__ . '/random_emoji');
WRITING SOLID CODE IN PRACTICE
PSR-11 - Container interface
orchestrating how frameworks and libraries obtain objects and parameters
• https://packagist.org/packages/symfony/dependency-injection
• https://packagist.org/packages/pimple/pimple
• https://packagist.org/packages/league/container
• https://packagist.org/packages/php-di/php-di
WRITING SOLID CODE IN PRACTICE
Open/Closed Principle
WRITING SOLID CODE IN PRACTICE
• class should be open for extension
• class behaviour can be changed in derived classes
• protected instead of private methods
• class should be closed for modification
• there should be no reason to change class code
• interfaces
• think of dependencies coming from 3rd party vendors
Open/Closed Principle
WRITING SOLID CODE IN PRACTICE
Open/Closed Principle
<?php



interface RandomEmojiInterface

{

public function fetch(): string

}
WRITING SOLID CODE IN PRACTICE
Open/Closed Principle
<?php



interface RandomEmojiInterface

{

public function fetch(): string

}
WRITING SOLID CODE IN PRACTICE
<?php











class Emoji

{

/** @var string */

private $url;



public function __construct(string $url)

{

$this->url = $url;

}



public function getUrl(): string

{

return $this->url;

}

}



interface RandomEmojiInterface

{

public function fetch(): Emoji

}
Open/Closed Principle
WRITING SOLID CODE IN PRACTICE
<?php











class Emoji

{

/** @var string */

private $url;



public function __construct(string $url)

{

$this->url = $url;

}



public function getUrl(): string

{

return $this->url;

}

}



interface RandomEmojiInterface

{

public function fetch(): Emoji

}
Open/Closed Principle
WRITING SOLID CODE IN PRACTICE
















/** @var string */

private $url;



public function __construct(string $url)

{

$this->url = $url;

}



public function getUrl(): string

{

return $this->url;

}











<?php

interface EmojiInterface

{

public function getUrl(): string;

}



class Emoji implements EmojiInterface

{





















}



interface RandomEmojiInterface

{

public function fetch(): EmojiInterface;

}
Open/Closed Principle
WRITING SOLID CODE IN PRACTICE
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
• many specific interfaces are better than one generic
• client shouldn’t be aware and forced to depend on methods it doesn’t use
• helps keeping parts of the system decoupled
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
<?php



interface CacheInterface

{

public function get($key);



public function put($something);



}



class FileCache implements CacheInterface

{

// get, put

}
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
<?php



interface CacheInterface

{

public function get($key);



public function put($something);



public function getMany(array $keys);



public function putMany(array $items);

}



class FileCache implements CacheInterface

{

// get, put

// getMany, putMany

}
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
<?php



interface CacheInterface

{

public function get($key);



public function put($something);



public function getMany(array $keys);



public function putMany(array $items);



public function invalidate($key);



public function invalidateMany(array $keys);



public function invalidataAll();

}



class FileCache implements CacheInterface

{

// get, put

// getMany, putMany
// invalidate, invalidateMany, invalidateAll

}
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
<?php



interface CacheInterface

{

public function get($key);



public function put($something);



public function getMany(array $keys);



public function putMany(array $items);



public function invalidate($key);



public function invalidateMany(array $keys);



public function invalidataAll();



public function gc();



public function countAll();

}



class FileCache implements CacheInterface

{

// :(

}
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
Interface Segregation Principle
WRITING SOLID CODE IN PRACTICE
Liskov Substitution Principle
(Barbara Liskov)
WRITING SOLID CODE IN PRACTICE
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
Liskov Substitution Principle
WRITING SOLID CODE IN PRACTICE
• objects of type T can be replaced by any subtype S of type T without altering
desired properties of the program/module (eg. correctness)
• signature requirements
• covariance of return types, contravariance of method arguments
• no new exceptions being non-derived from exceptions thrown in super type
• behavioral conditions
• preconditions cannot be strengthen in subtype
• postconditions cannot be weakened in subtype
Liskov Substitution Principle
WRITING SOLID CODE IN PRACTICE
<?php



interface T

{

}



interface S extends T

{

}





// ---------------------------





interface A

{

public function do(T $t);

}



interface B extends A

{

public function do(T $t);

}

Liskov Substitution Principle
<?php



interface T

{

}



interface S extends T

{

}
invariance of method argument
WRITING SOLID CODE IN PRACTICE
<?php



interface T

{

}



interface S extends T

{

}




// ---------------------------





interface A

{

public function do(T $t);

}



interface B extends A

{

public function do(S $s);

}

Liskov Substitution Principle
covariance of method argument is not allowed
• violating A::do signature
• violating “preconditions cannot be strengthen in subtype”
WRITING SOLID CODE IN PRACTICE
<?php



interface T

{

}



interface S extends T

{

}

// ---------------------------


class A

{

public function do(): S
{
}

}



class B extends A

{

public function do(): T
{
}

}

Liskov Substitution Principle
contravariance of return type is not allowed
• violating A::do signature
• violating “postconditions cannot be weakened in subtype”
WRITING SOLID CODE IN PRACTICE
Summary
WRITING SOLID CODE IN PRACTICE
Before
WRITING SOLID CODE IN PRACTICE
After
WRITING SOLID CODE IN PRACTICE
Benefits of writing SOLID code
WRITING SOLID CODE IN PRACTICE
Benefits
• code is well organised and more declarative
• code is easy to read and understand (short learning curve)
• code is loosely coupled and thus reusable
• code is easy to adapt
• code is easy to extend
• code is easy to refactor
• code is easy to unit test
• code is easy to replace/mock in functional tests
Thanks for attention
Questions?

SOLID code in practice - creating good object oriented PHP application @ WDI 2018

  • 2.
    >>>Pytania? Zadawaj je, oceniajprelekcję, komentuj lub polub poprzez sli.do: WarszawskieDniInformatyki.pl/slido
  • 3.
    SOLID code inpractice creating good object-oriented PHP applications /prgTW /prgTW /prgTW Senior developer, 9+ years of experience Tomasz Wójcik
  • 4.
    WRITING SOLID CODEIN PRACTICE SRP - Single Responsibility Principle OCP - Open/Closed Principle LSP - Liskov Substitution Principle ISP - Interface Segregation Principle DIP - Dependency Inversion Principle What SOLID stands for
  • 5.
    WRITING SOLID CODEIN PRACTICE Single Responsibility Principle
  • 6.
    WRITING SOLID CODEIN PRACTICE • class/module should have only single responsibility • the above is defined as single reason for change Single Responsibility Principle
  • 7.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 Single Responsibility Principle
  • 8.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 
 
 
 
 
 
 
 
 
 file_put_contents($cacheFile, $randomEmoji); cache control Single Responsibility Principle
  • 9.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 $response = file_get_contents('https://api.github.com/emojis');
 fetching data Single Responsibility Principle
  • 10.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 } error handling Single Responsibility Principle
  • 11.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 $emojis = json_decode($response, true); data transformation Single Responsibility Principle
  • 12.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 $randomEmoji = array_rand($emojis); 
 
 
 return $randomEmoji; random emoji logic Single Responsibility Principle
  • 13.
    WRITING SOLID CODEIN PRACTICE class FileCache class FileGetContentsFetcher throw new Exception class JsonSerializer class RandomGithubEmoji • cache control • fetching data • error handling • data transformation • random emoji logic Single Responsibility Principle custom one
  • 14.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 
 
 
 
 
 
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 
 
 
 
 
 
 
 
 
 file_put_contents($cacheFile, $randomEmoji); cache control Single Responsibility Principle
  • 15.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 
 
 
 
 
 
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 
 
 return $randomEmoji;
 }
 }
 Single Responsibility Principle cache control 
 
 
 
 
 
 $cache = new FileCache(__DIR__ . '/random_emoji');
 
 $randomEmoji = $cache->fetch();
 if (null !== $randomEmoji)
 {
 return $randomEmoji;
 }
 
 
 
 
 
 
 
 
 
 $cache->put($randomEmoji);

  • 16.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 $response = file_get_contents('https://api.github.com/emojis');
 fetching data Single Responsibility Principle
  • 17.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 Single Responsibility Principle fetching data 
 
 
 
 
 
 
 
 
 
 
 
 
 $response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');
  • 18.
    WRITING SOLID CODEIN PRACTICE 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 error handling Single Responsibility Principle <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }

  • 19.
    WRITING SOLID CODEIN PRACTICE Single Responsibility Principle error handling 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 throw new UnableToFetchEmojiException; <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 
 }
 
 $emojis = json_decode($response, true);
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }

  • 20.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 $emojis = json_decode($response, true); data transformation Single Responsibility Principle
  • 21.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cacheFile = __DIR__ . '/random_emoji';
 
 if (is_file($cacheFile) && time() - filemtime($cacheFile) <= 3)
 {
 return file_get_contents($cacheFile);
 }
 
 $response = file_get_contents('https://api.github.com/emojis');
 if (!$response)
 {
 return 'https://assets-cdn.github.com/images/icons/emoji/trollface.png?v6';
 }
 
 
 $randomEmoji = array_rand($emojis);
 
 file_put_contents($cacheFile, $randomEmoji);
 
 return $randomEmoji;
 }
 }
 Single Responsibility Principle data transformation 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 $emojis = (new JsonSerializer)->deserialize($response, 'json');
  • 22.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cache = new FileCache(__DIR__ . '/random_emoji');
 $randomEmoji = $cache->fetch();
 if (null !== $randomEmoji)
 {
 return $randomEmoji;
 }
 
 $response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');
 if (!$response)
 {
 throw new UnableToFetchEmojiException;
 }
 
 $emojis = (new JsonSerializer)->deserialize($response, 'json');
 $randomEmoji = array_rand($emojis);
 
 $cache->put($randomEmoji);
 
 return $randomEmoji;
 }
 } Single Responsibility Principle
  • 23.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 {
 public function fetch(): string
 {
 $cache = new FileCache(__DIR__ . '/random_emoji');
 $randomEmoji = $cache->fetch();
 if (null !== $randomEmoji)
 {
 return $randomEmoji;
 }
 
 $response = (new FileGetContentsFetcher)->fetch('https://api.github.com/emojis');
 if (!$response)
 {
 throw new UnableToFetchEmojiException;
 }
 
 $emojis = (new JsonSerializer)->deserialize($response, 'json');
 $randomEmoji = array_rand($emojis);
 
 $cache->put($randomEmoji);
 
 return $randomEmoji;
 }
 } 
 
 
 
 
 
 new FileCache(__DIR__ . '/random_emoji');
 
 
 
 
 
 
 new FileGetContentsFetcher
 
 
 
 
 
 new JsonSerializer We still have concrete dependencies
  • 24.
    WRITING SOLID CODEIN PRACTICE Dependency Injection not a part of SOLID but closely related
  • 25.
    WRITING SOLID CODEIN PRACTICE • providing dependencies from outside of the dependent class • class mustn’t use new or static methods Dependency Injection
  • 26.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 { /** @var FileCache */
 private $cache;
 
 /** @var FileGetContentsFetcher */
 private $fetcher;
 
 /** @var JsonSerializer */
 private $serializer;
 
 public function __construct(FileCache $cache, FileGetContentsFetcher $fetcher, JsonSerializer $serializer) {
 $this->cache = $cache;
 $this->fetcher = $fetcher;
 $this->serializer = $serializer;
 } 
 // … } Dependency Injection
  • 27.
    WRITING SOLID CODEIN PRACTICE Dependency Inversion Principle
  • 28.
    WRITING SOLID CODEIN PRACTICE • used to decouple software modules • high-level modules shouldn’t depend on low-level modules, they both should depend on abstractions • variables must be created via injection or creational patterns (eg. factories) • class members should be interfaces or abstracts • class shouldn’t derive from concretions (composition over inheritance) • methods shouldn’t derive from already implemented methods Dependency Inversion Principle
  • 29.
    WRITING SOLID CODEIN PRACTICE Dependency Inversion Principle https://en.wikipedia.org/wiki/Dependency_inversion_principle
  • 30.
    WRITING SOLID CODEIN PRACTICE Dependency Inversion Principle
  • 31.
    WRITING SOLID CODEIN PRACTICE Dependency Inversion Principle https://en.wikipedia.org/wiki/Dependency_inversion_principle
  • 32.
    WRITING SOLID CODEIN PRACTICE Dependency Inversion Principle
  • 33.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 { /** @var CacheInterface */
 private $cache;
 
 /** @var FetcherInterface */
 private $fetcher;
 
 /** @var SerializerInterface */
 private $serializer;
 
 public function __construct(Cache Interface $cache , FetcherInterface $fetcher, SerializerInterface $serializer) {
 $this->cache = $cache;
 $this->fetcher = $fetcher;
 $this->serializer = $serializer;
 } 
 // ... } Dependency Injection
  • 34.
    WRITING SOLID CODEIN PRACTICE <?php
 
 class RandomGithubEmoji
 { /** @var Cache Interface */
 private $cache ;
 
 /** @var FetcherInterface */
 private $fetcher;
 
 /** @var SerializerInterface */
 private $serializer;
 
 public function __construct(Cache Interface $cache , FetcherInterface $fetcher, SerializerInterface $serializer) {
 $this->cache = $cache 
 $this->fetcher = $fetcher;
 $this->serializer = $serializer;
 } 
 // ... } Dependency Injection 
 
 
 
 
 
 
 
 
 
 
 
 CacheFactoryInterface $cacheFactory 
 $this->cache = $cacheFactory->createCache(__DIR__ . '/random_emoji');
  • 35.
    WRITING SOLID CODEIN PRACTICE PSR-11 - Container interface orchestrating how frameworks and libraries obtain objects and parameters • https://packagist.org/packages/symfony/dependency-injection • https://packagist.org/packages/pimple/pimple • https://packagist.org/packages/league/container • https://packagist.org/packages/php-di/php-di
  • 36.
    WRITING SOLID CODEIN PRACTICE Open/Closed Principle
  • 37.
    WRITING SOLID CODEIN PRACTICE • class should be open for extension • class behaviour can be changed in derived classes • protected instead of private methods • class should be closed for modification • there should be no reason to change class code • interfaces • think of dependencies coming from 3rd party vendors Open/Closed Principle
  • 38.
    WRITING SOLID CODEIN PRACTICE Open/Closed Principle <?php
 
 interface RandomEmojiInterface
 {
 public function fetch(): string
 }
  • 39.
    WRITING SOLID CODEIN PRACTICE Open/Closed Principle <?php
 
 interface RandomEmojiInterface
 {
 public function fetch(): string
 }
  • 40.
    WRITING SOLID CODEIN PRACTICE <?php
 
 
 
 
 
 class Emoji
 {
 /** @var string */
 private $url;
 
 public function __construct(string $url)
 {
 $this->url = $url;
 }
 
 public function getUrl(): string
 {
 return $this->url;
 }
 }
 
 interface RandomEmojiInterface
 {
 public function fetch(): Emoji
 } Open/Closed Principle
  • 41.
    WRITING SOLID CODEIN PRACTICE <?php
 
 
 
 
 
 class Emoji
 {
 /** @var string */
 private $url;
 
 public function __construct(string $url)
 {
 $this->url = $url;
 }
 
 public function getUrl(): string
 {
 return $this->url;
 }
 }
 
 interface RandomEmojiInterface
 {
 public function fetch(): Emoji
 } Open/Closed Principle
  • 42.
    WRITING SOLID CODEIN PRACTICE 
 
 
 
 
 
 
 
 /** @var string */
 private $url;
 
 public function __construct(string $url)
 {
 $this->url = $url;
 }
 
 public function getUrl(): string
 {
 return $this->url;
 }
 
 
 
 
 
 <?php
 interface EmojiInterface
 {
 public function getUrl(): string;
 }
 
 class Emoji implements EmojiInterface
 {
 
 
 
 
 
 
 
 
 
 
 }
 
 interface RandomEmojiInterface
 {
 public function fetch(): EmojiInterface;
 } Open/Closed Principle
  • 43.
    WRITING SOLID CODEIN PRACTICE Interface Segregation Principle
  • 44.
    WRITING SOLID CODEIN PRACTICE • many specific interfaces are better than one generic • client shouldn’t be aware and forced to depend on methods it doesn’t use • helps keeping parts of the system decoupled Interface Segregation Principle
  • 45.
    WRITING SOLID CODEIN PRACTICE <?php
 
 interface CacheInterface
 {
 public function get($key);
 
 public function put($something);
 
 }
 
 class FileCache implements CacheInterface
 {
 // get, put
 } Interface Segregation Principle
  • 46.
    WRITING SOLID CODEIN PRACTICE <?php
 
 interface CacheInterface
 {
 public function get($key);
 
 public function put($something);
 
 public function getMany(array $keys);
 
 public function putMany(array $items);
 }
 
 class FileCache implements CacheInterface
 {
 // get, put
 // getMany, putMany
 } Interface Segregation Principle
  • 47.
    WRITING SOLID CODEIN PRACTICE <?php
 
 interface CacheInterface
 {
 public function get($key);
 
 public function put($something);
 
 public function getMany(array $keys);
 
 public function putMany(array $items);
 
 public function invalidate($key);
 
 public function invalidateMany(array $keys);
 
 public function invalidataAll();
 }
 
 class FileCache implements CacheInterface
 {
 // get, put
 // getMany, putMany // invalidate, invalidateMany, invalidateAll
 } Interface Segregation Principle
  • 48.
    WRITING SOLID CODEIN PRACTICE <?php
 
 interface CacheInterface
 {
 public function get($key);
 
 public function put($something);
 
 public function getMany(array $keys);
 
 public function putMany(array $items);
 
 public function invalidate($key);
 
 public function invalidateMany(array $keys);
 
 public function invalidataAll();
 
 public function gc();
 
 public function countAll();
 }
 
 class FileCache implements CacheInterface
 {
 // :(
 } Interface Segregation Principle
  • 49.
    WRITING SOLID CODEIN PRACTICE Interface Segregation Principle
  • 50.
    WRITING SOLID CODEIN PRACTICE Interface Segregation Principle
  • 51.
    WRITING SOLID CODEIN PRACTICE Interface Segregation Principle
  • 52.
    WRITING SOLID CODEIN PRACTICE Liskov Substitution Principle (Barbara Liskov)
  • 53.
    WRITING SOLID CODEIN PRACTICE https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) Liskov Substitution Principle
  • 54.
    WRITING SOLID CODEIN PRACTICE • objects of type T can be replaced by any subtype S of type T without altering desired properties of the program/module (eg. correctness) • signature requirements • covariance of return types, contravariance of method arguments • no new exceptions being non-derived from exceptions thrown in super type • behavioral conditions • preconditions cannot be strengthen in subtype • postconditions cannot be weakened in subtype Liskov Substitution Principle
  • 55.
    WRITING SOLID CODEIN PRACTICE <?php
 
 interface T
 {
 }
 
 interface S extends T
 {
 }
 
 
 // ---------------------------
 
 
 interface A
 {
 public function do(T $t);
 }
 
 interface B extends A
 {
 public function do(T $t);
 }
 Liskov Substitution Principle <?php
 
 interface T
 {
 }
 
 interface S extends T
 {
 } invariance of method argument
  • 56.
    WRITING SOLID CODEIN PRACTICE <?php
 
 interface T
 {
 }
 
 interface S extends T
 {
 } 
 
 // ---------------------------
 
 
 interface A
 {
 public function do(T $t);
 }
 
 interface B extends A
 {
 public function do(S $s);
 }
 Liskov Substitution Principle covariance of method argument is not allowed • violating A::do signature • violating “preconditions cannot be strengthen in subtype”
  • 57.
    WRITING SOLID CODEIN PRACTICE <?php
 
 interface T
 {
 }
 
 interface S extends T
 {
 }
 // --------------------------- 
 class A
 {
 public function do(): S { }
 }
 
 class B extends A
 {
 public function do(): T { }
 }
 Liskov Substitution Principle contravariance of return type is not allowed • violating A::do signature • violating “postconditions cannot be weakened in subtype”
  • 58.
    WRITING SOLID CODEIN PRACTICE Summary
  • 59.
    WRITING SOLID CODEIN PRACTICE Before
  • 60.
    WRITING SOLID CODEIN PRACTICE After
  • 61.
    WRITING SOLID CODEIN PRACTICE Benefits of writing SOLID code
  • 62.
    WRITING SOLID CODEIN PRACTICE Benefits • code is well organised and more declarative • code is easy to read and understand (short learning curve) • code is loosely coupled and thus reusable • code is easy to adapt • code is easy to extend • code is easy to refactor • code is easy to unit test • code is easy to replace/mock in functional tests
  • 63.