Part 4 of a bespoke in-company training. Complete set of slides under:
Part 1: https://www.slideshare.net/VasilyKartashov/effective-php-part-1
Part 2: https://www.slideshare.net/VasilyKartashov/effective-php-part-2
Part 3: https://www.slideshare.net/VasilyKartashov/effective-php-part-3
Part 4: https://www.slideshare.net/VasilyKartashov/effective-php-part-4
Part 5: https://www.slideshare.net/VasilyKartashov/effective-php-part-5
Part 6: https://www.slideshare.net/VasilyKartashov/effective-php-part-6
More about the author: http://notes.kartashov.com/about/
2. Design for inheritance or prohibit it
Real world example:
class FilesystemLogger implements LoggerInterface
{
...
private function validatePath(string $path): bool {
return preg_match(‘|^[a-z0-9]$|’, $path);
}
...
}
You cannot update validator, but you can extend the class. Either make the class final of make
validatePath protected.
3. Pros & Cons
➔ Not enforced by any standard or static
analyser.
➔ Communicates clearly the intent and
design of the class.
4. Prefer interfaces to abstract classes
Abstract classes allow you to create “half-finished” implementations, i.e. if you try and
extend an abstract class you’ll be forced to implement missing functionalities (abstract
methods) and thus comply with authors design intention. An example:
abstract class AbstractApplication {
abstract public function process(Request $request): Response;
}
You cannot create an Application without providing specific algorithm for how requests
are transformed into responses. Everything around it including autoloading, cacheing,
etc… has been defined in the AbstractApplication and we’re just missing this one bit.
5. Abstract classes
It’s a nice way to separate application-specific features from common features shared
between all applications. AbstractApplication contains common code, application
specific features end in the subclass of AbstractApplication.
In this case AbstractApplication and specific Application extends AbstractApplication
share the same interface, as expose the same set of public functions to outsiders.
6. Interfaces
Let’s say we want to write a command line runner that would accept request and
output the response to CLI.
class CliRunner {
private $implementation;
public function __construct(? $implementation) {
$this->implementation = $implementation;
}
public function run() {
$response = $this->implementation->process($request);
echo $response;
}
}
Should we use Application as $implementation type?
7. Pros & Cons
➔ Extracting interfaces mean increasing
number of classes. Some people see it
as “verbosity” instead of seeing it as a
process of discovering underlying
abstractions.
➔ Easier to test, to reason, harder to
abuse.
8. Prefer hierarchies over tagged classes
What is a tagged class?
class CompatiblityPolicy {
private $version;
public function canUpdateSoftware(): bool {
if ($this->version) {
return $this->version->software() > 125;
} else {
return false;
}
}
}
9. Pros & Cons
➔ Logic of tagged classes can become
quite complex quickly, imagine 2 tags
sharing 90% of the logic?
➔ As before, more classes.
10. Use function objects for strategies
Let’s assume we want a transactional context around our DB
queries. The typical pseudo-code would look like following
try {
start();
// your code
commit();
} catch (Exception e) {
rollback();
throw e;
}
11. Strategy “object”
How do we reuse “wrapping” code without copy and paste? By
passing a callback into the generic method.
try {
start();
$value = $callback();
commit();
return $value;
} catch (Exception e) {
rollback();
throw e;
}
12. Pros & Cons
➔ Callbacks are not typed in PHP (yet, but
there are RFCs).
➔ Easy to abuse.