SlideShare a Scribd company logo
❌ Let's talk about code ✔
Alexander Makarov, Yii framework
Who's Alexander? 🤠
Writing code is easy! 👌
Especially PHP code! 🤘
Right? 😟
😭
The plan 📃
1. Composition and inheritance.
2. State.
3. Dependencies and injection.
4. Methods.
5. Exceptions.
6. Object types.
7. Services and PHP long running servers.
8. Tests.
9. Principles.
10. Layers and abstraction.
❌ What breaks our code? ☠
● Inheritance.
● Coupling.
● State.
● Assumptions.
● Testing.
● Architecture.
● ...
😍 Composition and inheritance 🤨
😍 Composition vs inheritance 🤕
abstract class Notifier
{
private string $template;
private array $parameters;
public function __construct(string $template, array $parameters = [])
{
// ...
}
protected function renderTemplate(): string
{
// ...
}
abstract public function send(): void;
}
final class EmailNotifier extends Notifier
{
private string $to;
private string $subject;
public function __construct(string $to, string $subject, string $template, array
$parameters = [])
{
// ...
}
public function send(): void
{
mail($this->to, $this->subject, $this->renderTemplate());
}
}
abstract Notifier - compose message, send message
○ final EmailNotifier.
○ final SmsNotifier.
○ final DebugNotifier.
Problems?
Notifier is coupled with concrete implementations.
We need more!
○ Sms notifier and Email notifier should log.
○ Sms notifier and Email notifier should not allow to send message twice.
○ We should send to multiple channels with fallback.
○ We should not send emails on April 1st.
○ 😱
Let's try without inheritance? 😎
final class Notifier
{
private Sender $sender;
public function __construct(Sender $sender) {
// ...
}
public function send(Message $message, Address $to): void
{
$this->sender->send($message, $to);
}
}
final class Notifier
{
private Sender $sender;
public function __construct(Sender $sender) {
// ...
}
public function send(Message $message, Address $to): void
{
$this->sender->send($message, $to);
}
}
interface Message
{
public function getSubject(): string;
public function getText(): string;
}
final class Address
{
public function getEmail(): string { … }
public function getPhone(): string { … }
}
interface Message
{
public function getSubject(): string;
public function getText(): string;
}
final class Address
{
public function getEmail(): string { … }
public function getPhone(): string { … }
}
interface Sender
{
public function send(Message $message, Address $to): void;
}
class EmailSender implements Sender
class SmsSender implements Sender
class Logger implements Sender
class DuplicatePreventer implements Sender
$sender = new DuplicatePreventer(new Logger(new SmsSender()));
$notifier = new Notifier($sender);
$notifier->send($message, $to);
So… inheritance is bad?
● Yes. Prefer to avoid it.
● Use it where appropriate: exceptions and other
hierarchies.
State 🤯
final class DataProvider
{
public function __construct(Query $query, Connection $connection) { ... }
public function all()
{
return $this->connection->findAll($this->query);
}
public function one()
{
$limitedQuery = $this->query->limit(1);
return $this->connection->findOne($limitedQuery);
}
}
$query = (new Query())
->select()
->from('post');
$dataProvider = new DataProvider($query);
var_dump($dataProvider->one());
var_dump($dataProvider->all());
interface QueryInterface
{
public function select(string $columns): self;
public function from(string $table): self;
public function limit(int $limit): self;
}
🤯
Alexander Makarov "Let’s talk about code"
public function limit(int $limit): self
{
$this->limit = $limit;
return $this;
}
public function one()
{
$limitedQuery = $this->query->limit(1);
return
$this->connection->findOne($limitedQuery);
}
How to fix it?
public function limit(int $limit): self
{
$new = clone $this;
$new->limit = $limit;
return $new;
}
public function limit(int $limit): self
{
$new = clone $this;
$new->limit = $limit;
return $new;
}
State
● Avoid it if possible.
● Encapsulate it.
● Absolutely avoid for fluent interfaces.
● Fluent interface = immutability.
Dependencies and injection 🤓
final class Notifier
{
private Sender $sender;
public function __construct(Sender $sender) {
// ...
}
public function send(Message $message, Address $to): void
{
$this->sender->send($message, $to);
}
}
final class Notifier
{
private Sender $sender;
public function __construct(Sender $sender) {
// ...
}
public function send(Message $message, Address $to): void
{
$this->sender->send($message, $to);
}
}
Is it possible to do it wrong?
final class Notifier
{
private ContainerInterface $container;
public function __construct(ContainerInterface $container) {
// ...
}
public function send(Message $message, Address $to): void
{
$sender = $this->container->get(Sender::class);
$sender->send($message, $to);
}
}
public function __construct(ContainerInterface $container) {
// ...
}
public function send(Message $message, Address $to): void
{
$sender = $this->container->get(Sender::class);
Dependency Injection
● DI is simple.
● You can do DI without container.
● Prefer composition over inheritance.
● Request what you need, not what you want to
ask to get what you need.
● Avoid passing container around.
Is it possible to do moar wrong?
Alexander Makarov "Let’s talk about code"
final class Notifier
{
public function send(Message $message, Address $to): void
{
$sender = App::$container->get(Sender::class);
$sender->send($message, $to);
}
}
final class Notifier
{
public function send(Message $message, Address $to): void
{
$sender = App::$container->get(Sender::class);
$sender->send($message, $to);
}
}
Methods 😎
Method structure
1. Precondition checks.
2. Failure scenarios.
3. Main path.
4. Postcondition checks.
5. return.
Precondition checks
● Check if arguments are valid.
● Throw InvalidArgumentException.
● Don't try to "fix" things.
Fixing things
● Auth via Facebook. Same nickname present.
Attach Facebook ID to the account.
● Only letters are allowed in a nickname.
"samdark123" entered. Fix it by removing all
the numbers.
Alexander Makarov "Let’s talk about code"
Failure scenarios
● RuntimeException.
● Avoid trying to "fix" things.
Main path
● Just what method does.
Postcondition checks
● Check if what was done is done right.
● Throw RuntimeException if not.
How do I know method is OK?
Cyclomatic complexity
Number of independent execution paths in a
control graph.
Alexander Makarov "Let’s talk about code"
function doIt(int $number) {
if ($number === 42) {
$result = calculateSpecialResult();
} else {
$result = calculateResult(42);
}
if ($result === null) {
throw new RuntimeException('Unable to get result');
}
return $result;
}
V(G) = E - N + 2 = 7 - 6 + 2 = 3
V(G) = P + 1 = 2 + 1 = 3
● V(G) - cyclomatic complexity.
● E - edges.
● N - nodes.
● P - control structure points:
○ if → 1.
○ compound if → number of
sub-conditions.
○ iteration → 1.
○ switch → 1 per branch.
Keep cyclomatic
complexity low.
Return/throw early
function login(array $data)
{
if (isset($data['username'], $data['password'])) {
$user = $this->findUser($data['username']);
if ($user !== null) {
if ($user->isPasswordValid($data['password'])) {
$this->loginUser();
$this->refresh();
} else {
throw new InvalidArgumentException('Password is not valid.');
}
} else {
throw new InvalidArgumentException('User not found.');
}
} else {
throw new InvalidArgumentException('Both username and password are required.');
}
}
function login(array $data)
{
if (!isset($data['username'], $data['password'])) {
throw new InvalidArgumentException('Both username and password are required.');
}
$user = $this->findUser($data['username']);
if ($user === null) {
throw new InvalidArgumentException('User not found.');
}
if (!$user->isPasswordValid($data['password'])) {
throw new InvalidArgumentException('Password is not valid.');
}
$this->loginUser();
$this->refresh();
}
function login(array $data)
{
$this->assertUsernameAndPasswordPresent($data);
$user = $this->findUser($data['username']);
$this->assertUserFound($user);
$this->assertPasswordValid($user, $data['password']);
$this->loginUser();
$this->refresh();
}
Boolean flags
If a method has a boolean flag:
1. Single responsibility principle is violated.
2. You need multiple methods instead.
Visibility scopes
Private should be your default.
🔞 Exceptions 🚳
Exceptions
● Two types:
○ Ones we can handle.
■ Custom type, domain exceptions.
○ Ones we should not handle.
■ Prefer built-in types:
● RunitmeException.
● InvalidArgumentException.
Exception messages
● Should be explanative.
● Use proper English sentences.
Friendly exceptions
● https://github.com/yiisoft/friendly-exception
● getName().
● getSolution().
Object types 🦆 🦜 🦉
Services and non-services
● Services are doing something.
● Non-services hold data and return/mutate it.
Domain objects
● Extract domain objects to validate less.
○ Money, Point, Email.
● Composite values are object as well.
● Should not have dependencies injected. Only values.
● Use static named constructors: fromString, fromInt etc.
● Use private constructor for validation.
● Do not bloat these.
● Immutable.
DTO
● Can go crazy here but keep it simple.
● Public properties are fine :)
● Collect errors instead of throwing
exceptions.
● Use mass-assign.
Services 🛠
Services / Components
● Created once.
● Usually immutable.
● Use proper DI.
● Usually stored in DI container and/or accessible
via service locator.
Rules for a service
● Preferably stateless.
● No method injection or property injection.
● No optional dependencies.
● Explicit dependencies (proper DI).
● Action-relevant data should be passed to action
method.
● Light constructors. Postpone initialization.
● Do not make assumptions. Throw exceptions right
away.
PHP long running servers 🚀
How it works
// initialization
while ($request = $psr7->acceptRequest()) {
$response = $application->handle($request);
$psr7->respond($response);
gc_collect_cycles();
}
Rules
● Stateless services / State reset.
● Taking care about memory.
● Killing what's not used.
🪓🔨Tests ⛏🏹
How to write tests
● Arrange, act, assert.
● No dependencies.
● One test - one AAA.
● Prefer black box tests.
○ Test public API.
○ Hands off non-public API!
○ Don't check internal state!
○ Don't test methods, tests behavior.
Principles 📙
Should it be immutable?
● Service = immutable (if possible).
● Entity = mutable.
● Other objects = immutable.
● Fluent interface = immutable (don't do it with
mutable objects).
How to mutate object?
● DTO:
○ Directly.
● Value objects:
○ Immutable chaining.
● Entities:
○ Record changes as events.
● Other objects:
○ Immutable chaining.
Getting/setting info
Command-query separation
● Doing both is confusing.
● In some cases it is necessary.
Query method
class ShoppingCart
{
public function getTotal(): int {
}
}
Rules for queries
● Return a single type.
● Do not return internal state.
● Be specific. Avoid too flexible methods.
● Add abstraction for queries crossing system
boundaries.
● Do not use command methods in query
methods.
Command method
class ShoppingCart
{
public function addItem(Item $item): void {
if ($this->getTotal() + $item->cost > self::MAX_TOTAL) {
throw new MaxTotalReached('Leave something for others!');
}
$this->items[] = $item;
}
}
Rules for commands
● Name in imperative form.
● Limit the scope, postpone with events and
queues.
● Throw exceptions.
● Add abstraction for commands crossing system
boundaries.
● Use queries to get info, commands to process it.
Models
Read/write models
● Similar to query / command.
● Search in Elastic / Insert to MySQL.
● Read models should be use-case specific.
● Read models should be close to data source.
🥞 Layers and abstraction 🍔
Alexander Makarov "Let’s talk about code"
Alexander Makarov "Let’s talk about code"
Alexander Makarov "Let’s talk about code"
Don't over-abstract
● Some objects are meant to be concrete:
○ Controllers.
○ Application services.
○ Entities.
○ Value objects.
FIN 🎬
1. Composition and inheritance.
2. State.
3. Dependencies and injection.
4. Methods.
5. Exceptions.
6. Object types.
7. Services and PHP long running servers.
8. Tests.
9. Principles.
10. Layers and abstraction.
There's always more 👀
Keep learning! 💪
Think! 💡
❓ Questions time!
● https://www.yiiframework.com/
● https://twitter.com/sam_dark
● https://rmcreative.ru/
● PHP Russia Videos
● Further learning:
○ Matthias Noback https://matthiasnoback.nl/
○ Cyclomatic complexity by Anna Filina
https://www.youtube.com/watch?v=UdjEb6t9-e4
○ Anthony Ferrara videos: https://www.youtube.com/user/ircmaxell/videos

More Related Content

PDF
Object::Franger: Wear a Raincoat in your Code
PDF
Object Calisthenics Adapted for PHP
PDF
Object Features
PDF
From android/java to swift (3)
PDF
Javascript essentials
PPT
Php Chapter 1 Training
ODP
Javascript & jQuery: A pragmatic introduction
PPTX
Php & my sql
Object::Franger: Wear a Raincoat in your Code
Object Calisthenics Adapted for PHP
Object Features
From android/java to swift (3)
Javascript essentials
Php Chapter 1 Training
Javascript & jQuery: A pragmatic introduction
Php & my sql

What's hot (20)

PDF
Beyond Design Principles and Patterns
PDF
You code sucks, let's fix it
PDF
Beyond design principles and patterns (muCon 2019 edition)
PDF
DPC 2019, Amsterdam: Beyond design patterns and principles - writing good OO ...
PDF
Pim Elshoff "Technically DDD"
KEY
Data::Verifier and Message::Stack
PDF
Jeremiah Caballero - Introduction of Clean Code
PDF
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
PPTX
Class 8 - Database Programming
PPT
شرح مقرر البرمجة 2 لغة جافا - الوحدة الرابعة
PDF
Dependency Injection
PPTX
Crafting beautiful software
PPT
J query lecture 1
PPTX
Let's write secure Drupal code! - DrupalCamp London 2019
PPT
Php variables
PPTX
Hacking Your Way To Better Security - Dutch PHP Conference 2016
PDF
Values
PDF
Your code sucks, let's fix it - DPC UnCon
PDF
Metaprogramming in Ruby
KEY
Making Templatetags Suck Less
Beyond Design Principles and Patterns
You code sucks, let's fix it
Beyond design principles and patterns (muCon 2019 edition)
DPC 2019, Amsterdam: Beyond design patterns and principles - writing good OO ...
Pim Elshoff "Technically DDD"
Data::Verifier and Message::Stack
Jeremiah Caballero - Introduction of Clean Code
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
Class 8 - Database Programming
شرح مقرر البرمجة 2 لغة جافا - الوحدة الرابعة
Dependency Injection
Crafting beautiful software
J query lecture 1
Let's write secure Drupal code! - DrupalCamp London 2019
Php variables
Hacking Your Way To Better Security - Dutch PHP Conference 2016
Values
Your code sucks, let's fix it - DPC UnCon
Metaprogramming in Ruby
Making Templatetags Suck Less
Ad

Similar to Alexander Makarov "Let’s talk about code" (20)

PDF
Lithium: The Framework for People Who Hate Frameworks
PPT
Mocking Dependencies in PHPUnit
PPT
Mocking Dependencies in PHPUnit
PDF
Effective PHP. Part 1
PDF
Effective PHP. Part 3
PDF
Clean code & design patterns
PDF
KEY
Building Better Applications with Data::Manager
PDF
Effective PHP. Part 5
PDF
PHP traits, treat or threat?
PDF
PHP Technical Questions
PDF
Design how your objects talk through mocking
ODP
Clean code and refactoring
PDF
Building Testable PHP Applications
PDF
Decoupling Objects With Standard Interfaces
PPTX
Clean Code: Chapter 3 Function
PDF
Smelling your code
PDF
PHPSpec BDD Framework
KEY
PHPSpec BDD for PHP
PDF
Hacker News vs. Slashdot—Reputation Systems in Crowdsourced Technology News
Lithium: The Framework for People Who Hate Frameworks
Mocking Dependencies in PHPUnit
Mocking Dependencies in PHPUnit
Effective PHP. Part 1
Effective PHP. Part 3
Clean code & design patterns
Building Better Applications with Data::Manager
Effective PHP. Part 5
PHP traits, treat or threat?
PHP Technical Questions
Design how your objects talk through mocking
Clean code and refactoring
Building Testable PHP Applications
Decoupling Objects With Standard Interfaces
Clean Code: Chapter 3 Function
Smelling your code
PHPSpec BDD Framework
PHPSpec BDD for PHP
Hacker News vs. Slashdot—Reputation Systems in Crowdsourced Technology News
Ad

More from Fwdays (20)

PDF
"Mastering UI Complexity: State Machines and Reactive Patterns at Grammarly",...
PDF
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
PPTX
"Computer Use Agents: From SFT to Classic RL", Maksym Shamrai
PPTX
"Як ми переписали Сільпо на Angular", Євген Русаков
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
PDF
"Validation and Observability of AI Agents", Oleksandr Denisyuk
PPTX
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
PDF
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
PPTX
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
PPTX
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
PDF
"AI is already here. What will happen to your team (and your role) tomorrow?"...
PPTX
"Is it worth investing in AI in 2025?", Alexander Sharko
PDF
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
PDF
"Scaling in space and time with Temporal", Andriy Lupa.pdf
PDF
"Database isolation: how we deal with hundreds of direct connections to the d...
PDF
"Scaling in space and time with Temporal", Andriy Lupa .pdf
PPTX
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
PPTX
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
PPTX
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
PPTX
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...
"Mastering UI Complexity: State Machines and Reactive Patterns at Grammarly",...
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
"Computer Use Agents: From SFT to Classic RL", Maksym Shamrai
"Як ми переписали Сільпо на Angular", Євген Русаков
"AI Transformation: Directions and Challenges", Pavlo Shaternik
"Validation and Observability of AI Agents", Oleksandr Denisyuk
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
"Co-Authoring with a Machine: What I Learned from Writing a Book on Generativ...
"Human-AI Collaboration Models for Better Decisions, Faster Workflows, and Cr...
"AI is already here. What will happen to your team (and your role) tomorrow?"...
"Is it worth investing in AI in 2025?", Alexander Sharko
''Taming Explosive Growth: Building Resilience in a Hyper-Scaled Financial Pl...
"Scaling in space and time with Temporal", Andriy Lupa.pdf
"Database isolation: how we deal with hundreds of direct connections to the d...
"Scaling in space and time with Temporal", Andriy Lupa .pdf
"Provisioning via DOT-Chain: from catering to drone marketplaces", Volodymyr ...
" Observability with Elasticsearch: Best Practices for High-Load Platform", A...
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
"Istio Ambient Mesh in production: our way from Sidecar to Sidecar-less",Hlib...

Recently uploaded (20)

PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
Approach and Philosophy of On baking technology
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPT
Teaching material agriculture food technology
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Empathic Computing: Creating Shared Understanding
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Big Data Technologies - Introduction.pptx
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Encapsulation theory and applications.pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
DOCX
The AUB Centre for AI in Media Proposal.docx
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
Unlocking AI with Model Context Protocol (MCP)
Review of recent advances in non-invasive hemoglobin estimation
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Building Integrated photovoltaic BIPV_UPV.pdf
Approach and Philosophy of On baking technology
Diabetes mellitus diagnosis method based random forest with bat algorithm
Teaching material agriculture food technology
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
“AI and Expert System Decision Support & Business Intelligence Systems”
Digital-Transformation-Roadmap-for-Companies.pptx
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Empathic Computing: Creating Shared Understanding
CIFDAQ's Market Insight: SEC Turns Pro Crypto
MYSQL Presentation for SQL database connectivity
Big Data Technologies - Introduction.pptx
NewMind AI Weekly Chronicles - August'25 Week I
Encapsulation theory and applications.pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
The AUB Centre for AI in Media Proposal.docx

Alexander Makarov "Let’s talk about code"

  • 1. ❌ Let's talk about code ✔ Alexander Makarov, Yii framework
  • 3. Writing code is easy! 👌
  • 8. 1. Composition and inheritance. 2. State. 3. Dependencies and injection. 4. Methods. 5. Exceptions. 6. Object types. 7. Services and PHP long running servers. 8. Tests. 9. Principles. 10. Layers and abstraction.
  • 9. ❌ What breaks our code? ☠
  • 10. ● Inheritance. ● Coupling. ● State. ● Assumptions. ● Testing. ● Architecture. ● ...
  • 11. 😍 Composition and inheritance 🤨
  • 12. 😍 Composition vs inheritance 🤕
  • 13. abstract class Notifier { private string $template; private array $parameters; public function __construct(string $template, array $parameters = []) { // ... } protected function renderTemplate(): string { // ... } abstract public function send(): void; }
  • 14. final class EmailNotifier extends Notifier { private string $to; private string $subject; public function __construct(string $to, string $subject, string $template, array $parameters = []) { // ... } public function send(): void { mail($this->to, $this->subject, $this->renderTemplate()); } }
  • 15. abstract Notifier - compose message, send message ○ final EmailNotifier. ○ final SmsNotifier. ○ final DebugNotifier. Problems? Notifier is coupled with concrete implementations. We need more! ○ Sms notifier and Email notifier should log. ○ Sms notifier and Email notifier should not allow to send message twice. ○ We should send to multiple channels with fallback. ○ We should not send emails on April 1st. ○ 😱
  • 16. Let's try without inheritance? 😎
  • 17. final class Notifier { private Sender $sender; public function __construct(Sender $sender) { // ... } public function send(Message $message, Address $to): void { $this->sender->send($message, $to); } }
  • 18. final class Notifier { private Sender $sender; public function __construct(Sender $sender) { // ... } public function send(Message $message, Address $to): void { $this->sender->send($message, $to); } }
  • 19. interface Message { public function getSubject(): string; public function getText(): string; } final class Address { public function getEmail(): string { … } public function getPhone(): string { … } }
  • 20. interface Message { public function getSubject(): string; public function getText(): string; } final class Address { public function getEmail(): string { … } public function getPhone(): string { … } }
  • 21. interface Sender { public function send(Message $message, Address $to): void; }
  • 22. class EmailSender implements Sender class SmsSender implements Sender class Logger implements Sender class DuplicatePreventer implements Sender $sender = new DuplicatePreventer(new Logger(new SmsSender())); $notifier = new Notifier($sender); $notifier->send($message, $to);
  • 23. So… inheritance is bad? ● Yes. Prefer to avoid it. ● Use it where appropriate: exceptions and other hierarchies.
  • 25. final class DataProvider { public function __construct(Query $query, Connection $connection) { ... } public function all() { return $this->connection->findAll($this->query); } public function one() { $limitedQuery = $this->query->limit(1); return $this->connection->findOne($limitedQuery); } }
  • 26. $query = (new Query()) ->select() ->from('post'); $dataProvider = new DataProvider($query); var_dump($dataProvider->one()); var_dump($dataProvider->all());
  • 27. interface QueryInterface { public function select(string $columns): self; public function from(string $table): self; public function limit(int $limit): self; }
  • 28. 🤯
  • 30. public function limit(int $limit): self { $this->limit = $limit; return $this; }
  • 31. public function one() { $limitedQuery = $this->query->limit(1); return $this->connection->findOne($limitedQuery); }
  • 32. How to fix it?
  • 33. public function limit(int $limit): self { $new = clone $this; $new->limit = $limit; return $new; }
  • 34. public function limit(int $limit): self { $new = clone $this; $new->limit = $limit; return $new; }
  • 35. State ● Avoid it if possible. ● Encapsulate it. ● Absolutely avoid for fluent interfaces. ● Fluent interface = immutability.
  • 37. final class Notifier { private Sender $sender; public function __construct(Sender $sender) { // ... } public function send(Message $message, Address $to): void { $this->sender->send($message, $to); } }
  • 38. final class Notifier { private Sender $sender; public function __construct(Sender $sender) { // ... } public function send(Message $message, Address $to): void { $this->sender->send($message, $to); } }
  • 39. Is it possible to do it wrong?
  • 40. final class Notifier { private ContainerInterface $container; public function __construct(ContainerInterface $container) { // ... } public function send(Message $message, Address $to): void { $sender = $this->container->get(Sender::class); $sender->send($message, $to); } }
  • 41. public function __construct(ContainerInterface $container) { // ... } public function send(Message $message, Address $to): void { $sender = $this->container->get(Sender::class);
  • 42. Dependency Injection ● DI is simple. ● You can do DI without container. ● Prefer composition over inheritance. ● Request what you need, not what you want to ask to get what you need. ● Avoid passing container around.
  • 43. Is it possible to do moar wrong?
  • 45. final class Notifier { public function send(Message $message, Address $to): void { $sender = App::$container->get(Sender::class); $sender->send($message, $to); } }
  • 46. final class Notifier { public function send(Message $message, Address $to): void { $sender = App::$container->get(Sender::class); $sender->send($message, $to); } }
  • 48. Method structure 1. Precondition checks. 2. Failure scenarios. 3. Main path. 4. Postcondition checks. 5. return.
  • 49. Precondition checks ● Check if arguments are valid. ● Throw InvalidArgumentException. ● Don't try to "fix" things.
  • 50. Fixing things ● Auth via Facebook. Same nickname present. Attach Facebook ID to the account. ● Only letters are allowed in a nickname. "samdark123" entered. Fix it by removing all the numbers.
  • 52. Failure scenarios ● RuntimeException. ● Avoid trying to "fix" things.
  • 53. Main path ● Just what method does.
  • 54. Postcondition checks ● Check if what was done is done right. ● Throw RuntimeException if not.
  • 55. How do I know method is OK?
  • 56. Cyclomatic complexity Number of independent execution paths in a control graph.
  • 58. function doIt(int $number) { if ($number === 42) { $result = calculateSpecialResult(); } else { $result = calculateResult(42); } if ($result === null) { throw new RuntimeException('Unable to get result'); } return $result; }
  • 59. V(G) = E - N + 2 = 7 - 6 + 2 = 3 V(G) = P + 1 = 2 + 1 = 3 ● V(G) - cyclomatic complexity. ● E - edges. ● N - nodes. ● P - control structure points: ○ if → 1. ○ compound if → number of sub-conditions. ○ iteration → 1. ○ switch → 1 per branch. Keep cyclomatic complexity low.
  • 61. function login(array $data) { if (isset($data['username'], $data['password'])) { $user = $this->findUser($data['username']); if ($user !== null) { if ($user->isPasswordValid($data['password'])) { $this->loginUser(); $this->refresh(); } else { throw new InvalidArgumentException('Password is not valid.'); } } else { throw new InvalidArgumentException('User not found.'); } } else { throw new InvalidArgumentException('Both username and password are required.'); } }
  • 62. function login(array $data) { if (!isset($data['username'], $data['password'])) { throw new InvalidArgumentException('Both username and password are required.'); } $user = $this->findUser($data['username']); if ($user === null) { throw new InvalidArgumentException('User not found.'); } if (!$user->isPasswordValid($data['password'])) { throw new InvalidArgumentException('Password is not valid.'); } $this->loginUser(); $this->refresh(); }
  • 63. function login(array $data) { $this->assertUsernameAndPasswordPresent($data); $user = $this->findUser($data['username']); $this->assertUserFound($user); $this->assertPasswordValid($user, $data['password']); $this->loginUser(); $this->refresh(); }
  • 65. If a method has a boolean flag: 1. Single responsibility principle is violated. 2. You need multiple methods instead.
  • 68. Exceptions ● Two types: ○ Ones we can handle. ■ Custom type, domain exceptions. ○ Ones we should not handle. ■ Prefer built-in types: ● RunitmeException. ● InvalidArgumentException.
  • 69. Exception messages ● Should be explanative. ● Use proper English sentences.
  • 71. Object types 🦆 🦜 🦉
  • 72. Services and non-services ● Services are doing something. ● Non-services hold data and return/mutate it.
  • 73. Domain objects ● Extract domain objects to validate less. ○ Money, Point, Email. ● Composite values are object as well. ● Should not have dependencies injected. Only values. ● Use static named constructors: fromString, fromInt etc. ● Use private constructor for validation. ● Do not bloat these. ● Immutable.
  • 74. DTO ● Can go crazy here but keep it simple. ● Public properties are fine :) ● Collect errors instead of throwing exceptions. ● Use mass-assign.
  • 76. Services / Components ● Created once. ● Usually immutable. ● Use proper DI. ● Usually stored in DI container and/or accessible via service locator.
  • 77. Rules for a service ● Preferably stateless. ● No method injection or property injection. ● No optional dependencies. ● Explicit dependencies (proper DI). ● Action-relevant data should be passed to action method. ● Light constructors. Postpone initialization. ● Do not make assumptions. Throw exceptions right away.
  • 78. PHP long running servers 🚀
  • 79. How it works // initialization while ($request = $psr7->acceptRequest()) { $response = $application->handle($request); $psr7->respond($response); gc_collect_cycles(); }
  • 80. Rules ● Stateless services / State reset. ● Taking care about memory. ● Killing what's not used.
  • 82. How to write tests ● Arrange, act, assert. ● No dependencies. ● One test - one AAA. ● Prefer black box tests. ○ Test public API. ○ Hands off non-public API! ○ Don't check internal state! ○ Don't test methods, tests behavior.
  • 84. Should it be immutable? ● Service = immutable (if possible). ● Entity = mutable. ● Other objects = immutable. ● Fluent interface = immutable (don't do it with mutable objects).
  • 85. How to mutate object? ● DTO: ○ Directly. ● Value objects: ○ Immutable chaining. ● Entities: ○ Record changes as events. ● Other objects: ○ Immutable chaining.
  • 87. Command-query separation ● Doing both is confusing. ● In some cases it is necessary.
  • 88. Query method class ShoppingCart { public function getTotal(): int { } }
  • 89. Rules for queries ● Return a single type. ● Do not return internal state. ● Be specific. Avoid too flexible methods. ● Add abstraction for queries crossing system boundaries. ● Do not use command methods in query methods.
  • 90. Command method class ShoppingCart { public function addItem(Item $item): void { if ($this->getTotal() + $item->cost > self::MAX_TOTAL) { throw new MaxTotalReached('Leave something for others!'); } $this->items[] = $item; } }
  • 91. Rules for commands ● Name in imperative form. ● Limit the scope, postpone with events and queues. ● Throw exceptions. ● Add abstraction for commands crossing system boundaries. ● Use queries to get info, commands to process it.
  • 93. Read/write models ● Similar to query / command. ● Search in Elastic / Insert to MySQL. ● Read models should be use-case specific. ● Read models should be close to data source.
  • 94. 🥞 Layers and abstraction 🍔
  • 98. Don't over-abstract ● Some objects are meant to be concrete: ○ Controllers. ○ Application services. ○ Entities. ○ Value objects.
  • 100. 1. Composition and inheritance. 2. State. 3. Dependencies and injection. 4. Methods. 5. Exceptions. 6. Object types. 7. Services and PHP long running servers. 8. Tests. 9. Principles. 10. Layers and abstraction.
  • 104. ❓ Questions time! ● https://www.yiiframework.com/ ● https://twitter.com/sam_dark ● https://rmcreative.ru/ ● PHP Russia Videos ● Further learning: ○ Matthias Noback https://matthiasnoback.nl/ ○ Cyclomatic complexity by Anna Filina https://www.youtube.com/watch?v=UdjEb6t9-e4 ○ Anthony Ferrara videos: https://www.youtube.com/user/ircmaxell/videos