Effective PHP. Outlook ● Creating objects ● Methods common to all objects ● Classes and interfaces ● Methods ● General programming ● Exceptions & logging
Effective PHPVasily Kartashov notes.kartashov.com Session 1: Creating Objects
Consider static factory method instead of constructors. Two things are bad about default PHP constructor ● PHP doesn’t allow overloaded function signatures, as in using same method name with different arguments ● “new” operator has lower precedence than “->”
Typical solution #1: Merged signature public function __construct(string $type, string $content) { if ($type == 'text') { $this->type = $type; $this->text = $content; } elseif ($type == 'ssml') { ... } else { throw new IllegalArgumentException(...); } }
Typical solution #1: Obscure arrays of parameters public function __construct(array $options) { if ($options['type'] == 'text') { $this->type = $options['type']; $this->text = $options['text']; } elseif ($options['type'] == 'ssml') { ... } else { throw new IllegalArgumentException(...); } }
Static factory methods: class SpeechOuptut { Private $text, $type; private function __construct() {} public static function plainText(string $text) { $speech = new self; $speech->type = 'text'; $speech->text = $text; return $speech; } public static function ssml(string $ssml) { ... } }
Pros & Cons ➔ Static factories are harder to chain. Subclasses may need to redefine all the factory methods ➔ Static factories are clearer ➔ Static factories avoid ambiguous and irrelevant parameters
Enforce singleton with a static property: public static function database(): Database { static $database; if (!isset($database)) { $database = new Database(); } return $database; }
Pros & Cons ➔ Singletons are efficient ➔ Some algorithms require singletons, like types in GraphQL schema ➔ Singletons can be super dangerous when they contain state, use with care
Enforce non-instantiability with a private constructor: class Helper { private __constructor() {} public static function randomName(): string { ... } }
Pros & Cons ➔ It’s communicates perfectly clear the intention of the class and how it should be used ➔ Good solution if you want to use free standing methods with autoloading
Builder pattern: class ControllerProgram { private __constructor() {} public static function builder(): ControllerProgramBuilder { $program = new ControllerProgram; $constructor = function (array $zones) use ($program) { $program->zones = $zones; return $program; } return new ControllerProgramBuilder($constructor); } }
Builder pattern. Builder: class ControllerProgramBuilder { private $constructor; private $zones; public function __construct(callable $constructor) { $this->constructor = $constructor; } public function withZones(array $zones): ControllerProgramBuilder { foreach ($zones as $zone) assert ($zone instanceof Zone::class); $this->zones = $zones; return $this; } public function build(): ControllerProgram { return ($this->constructor)($this->zones); } }
Pros & Cons ➔ Quite complex ➔ The builder and the class itself contain pretty much the same information ➔ Much easier to test validity of arguments ➔ Prevents user from creating misformed objects, and provides nice chaining way of creating objects

Effective PHP. Part 1

  • 1.
    Effective PHP. Outlook ●Creating objects ● Methods common to all objects ● Classes and interfaces ● Methods ● General programming ● Exceptions & logging
  • 2.
  • 3.
    Consider static factorymethod instead of constructors. Two things are bad about default PHP constructor ● PHP doesn’t allow overloaded function signatures, as in using same method name with different arguments ● “new” operator has lower precedence than “->”
  • 4.
    Typical solution #1: Mergedsignature public function __construct(string $type, string $content) { if ($type == 'text') { $this->type = $type; $this->text = $content; } elseif ($type == 'ssml') { ... } else { throw new IllegalArgumentException(...); } }
  • 5.
    Typical solution #1: Obscurearrays of parameters public function __construct(array $options) { if ($options['type'] == 'text') { $this->type = $options['type']; $this->text = $options['text']; } elseif ($options['type'] == 'ssml') { ... } else { throw new IllegalArgumentException(...); } }
  • 6.
    Static factory methods: classSpeechOuptut { Private $text, $type; private function __construct() {} public static function plainText(string $text) { $speech = new self; $speech->type = 'text'; $speech->text = $text; return $speech; } public static function ssml(string $ssml) { ... } }
  • 7.
    Pros & Cons ➔Static factories are harder to chain. Subclasses may need to redefine all the factory methods ➔ Static factories are clearer ➔ Static factories avoid ambiguous and irrelevant parameters
  • 8.
    Enforce singleton witha static property: public static function database(): Database { static $database; if (!isset($database)) { $database = new Database(); } return $database; }
  • 9.
    Pros & Cons ➔Singletons are efficient ➔ Some algorithms require singletons, like types in GraphQL schema ➔ Singletons can be super dangerous when they contain state, use with care
  • 10.
    Enforce non-instantiability witha private constructor: class Helper { private __constructor() {} public static function randomName(): string { ... } }
  • 11.
    Pros & Cons ➔It’s communicates perfectly clear the intention of the class and how it should be used ➔ Good solution if you want to use free standing methods with autoloading
  • 12.
    Builder pattern: class ControllerProgram { private__constructor() {} public static function builder(): ControllerProgramBuilder { $program = new ControllerProgram; $constructor = function (array $zones) use ($program) { $program->zones = $zones; return $program; } return new ControllerProgramBuilder($constructor); } }
  • 13.
    Builder pattern. Builder: classControllerProgramBuilder { private $constructor; private $zones; public function __construct(callable $constructor) { $this->constructor = $constructor; } public function withZones(array $zones): ControllerProgramBuilder { foreach ($zones as $zone) assert ($zone instanceof Zone::class); $this->zones = $zones; return $this; } public function build(): ControllerProgram { return ($this->constructor)($this->zones); } }
  • 14.
    Pros & Cons ➔Quite complex ➔ The builder and the class itself contain pretty much the same information ➔ Much easier to test validity of arguments ➔ Prevents user from creating misformed objects, and provides nice chaining way of creating objects