Top Banner
ESCAPING DEPENDENCY HELL Michael Haeuslmann - PHP UG Munich 2016 Source: Escape from Hell Constantine by Rommeu
67

Escaping Dependency Hell v2

Apr 12, 2017

Download

Engineering

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Escaping Dependency Hell v2

ESCAPINGDEPENDENCY HELL

Michael Haeuslmann - PHP UG Munich 2016Source: Escape from Hell Constantine by Rommeu

Page 2: Escaping Dependency Hell v2

ESCAPING GERMANWINTER?

Michael Haeuslmann - PHP UG Munich 2016Source: Escape from Hell Constantine by Rommeu

Page 3: Escaping Dependency Hell v2

PART I: DISCUSSION

Page 4: Escaping Dependency Hell v2

WHAT IS DEPENDENCY HELL?

Page 5: Escaping Dependency Hell v2

WHAT IS A DEPENDENCY?

... a dependency signifies a supplier/clientrelationship between model elements

where the modification of a supplier mayimpact the client model elements

- UML Specification v2.5

Page 6: Escaping Dependency Hell v2

WHY SHOULD WE CARE?

Page 7: Escaping Dependency Hell v2

MICHAEL HAEUSLMANN (@MICHAELHAEU)FREELANCER (PHPRAGMATIC.COM)

married, love to travel, board games, ...developing in PHP for ~8 yearsprofessional work in PHP (mostly legacy apps)open source for all the exciting stuff

Page 8: Escaping Dependency Hell v2

Buuuut ...

I don't want to be a freelancer anymore ...

... sooooo ...

Page 9: Escaping Dependency Hell v2

WE KNOW WE'RE INDEPENDENCY HELL.

HOW DO WE ESCAPE?

Page 10: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDBubble Sort?

Page 11: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDBubble Sort?

Page 12: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDCache Invalidation?

Page 13: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDCache Invalidation?

Page 14: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDCache Invalidation?

Page 15: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDCache Invalidation?

Page 16: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDCache Invalidation?

Page 17: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDWeb Applications?

Page 18: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDWeb Applications?

Page 19: Escaping Dependency Hell v2

COMPLEX VS. COMPLICATEDIt doesn't have to be complicated

Page 20: Escaping Dependency Hell v2

WHAT DO WE DO ABOUT IT?Read the code and take notes? We don't want to do everything ourselves

use a tool → Managing dependencies in the right way helps with: Complexity, maintenance and understandability

Page 21: Escaping Dependency Hell v2

PART II: ANALYSE

Page 22: Escaping Dependency Hell v2

WHAT SHOULD DEPENDENCY ANALYSIS TELLUS?

Where should we start refactoring? Why does [SomeClass] always break? What does our architecture actually look like? Is our architecture the way it should be?

Page 23: Escaping Dependency Hell v2

TOOLSPHP DEPEND BY MANUEL PICHLER

many metrics many metrics hard to maintain too vague

Page 24: Escaping Dependency Hell v2

TOOLSPHP DEPEND BY MANUEL PICHLER

Page 25: Escaping Dependency Hell v2

TOOLS

detects even the sneakiest dependencies

generates dependency visualizations

hackable (grep, sed, awk, ...)

for the nerds: written using functional style

supports PHP 5.2 to 7.1

Page 26: Escaping Dependency Hell v2

FEATURES Text For quick feedback, debugging, UNIX pipes etc. Visualisations (UML & DSM & dot) Detailed dependency & architectural analysis (Metrics)

Page 27: Escaping Dependency Hell v2

DEPHPEND - TEXT OUTPUT$> wget http://phar.dephpend.com/dephpend.phar$> composer global require dephpend/dephpend:dev­master$> dephpend help text _ _____ _ _ _____ _ | | | __ \| | | | __ \ | | __| | ___| |__) | |__| | |__) |__ _ __ __| | / _` |/ _ \ ___/| __ | ___/ _ \ '_ \ / _` | | (_| | __/ | | | | | | | __/ | | | (_| | \__,_|\___|_| |_| |_|_| \___|_| |_|\__,_| version 0.1

Usage: text [options] [­­] ()...

$> dephpend text ~/workspace/dephpend/src

Mihaeu\PhpDependencies\Util\AbstractMap ­­> Mihaeu\PhpDependencies\Util\CollectionMihaeu\PhpDependencies\Util\DI ­­> Mihaeu\PhpDependencies\Analyser\Analyser...

(*) make sure XDebug is not enabled or use php -n

Page 28: Escaping Dependency Hell v2

DEPHPEND - TEXT OUTPUT$> dephpend text ~/workspace/dephpend/src ­­no­classes | sort

Mihaeu\PhpDependencies\Analyser ­­> Mihaeu\PhpDependencies\DependenciesMihaeu\PhpDependencies\Analyser ­­> Mihaeu\PhpDependencies\OSMihaeu\PhpDependencies\Analyser ­­> Mihaeu\PhpDependencies\UtilMihaeu\PhpDependencies\Analyser ­­> PhpParserMihaeu\PhpDependencies\Analyser ­­> PhpParser\NodeMihaeu\PhpDependencies\Analyser ­­> PhpParser\Node\ExprMihaeu\PhpDependencies\Analyser ­­> PhpParser\Node\NameMihaeu\PhpDependencies\Analyser ­­> PhpParser\Node\StmtMihaeu\PhpDependencies\Analyser ­­> PhpParser\NodeVisitor...

$> dephpend text ~/workspace/dephpend/src ­­no­classes \ | grep ­e 'Analyser ­­> .*OS'

Mihaeu\PhpDependencies\Analyser ­­> Mihaeu\PhpDependencies\OS

Page 29: Escaping Dependency Hell v2

DEPHPEND - TEXT OUTPUTMake it yours!

#!/usr/bin/env sh

php build/dephpend.phar text ~/workspace/dephpend/src ­­no­classes | grep \

­e 'Analyser ­­> .*OS' \

­e 'OS ­­> .*Analyser'

if [ "$?" ­eq 0 ]; then

echo 'Architecture violation!'

exit 1

fi

Page 30: Escaping Dependency Hell v2

DEPHPEND - TEXT OUTPUT

<?php

$output = shell_exec('dephpend text ' .'~/workspace/myMVCFramework/src ­­no­classes');$constraints = [ 'Model.* ­­> .*View', 'View.* ­­> .*Model',];

if (preg_match('/('.implode(')|(', $constraints).')/x', $output)) echo 'Architecture violation'.PHP_EOL; exit(1);

Page 31: Escaping Dependency Hell v2

DEPHPEND - UML (SORT OF)dePHPend packages

$> dephpend uml ~/workspace/dephpend/src ­­no­classes ­­output=uml.png

Page 32: Escaping Dependency Hell v2
Page 33: Escaping Dependency Hell v2

DEPHPEND - UML (SORT OF)Symfony components

$> php ­d memory_limit=512M dephpend.phar uml \

~/workspace/symfony/src/Symfony/Component \

­­no­classes \

­­depth 3 \ # Symfony\Component\HttpKernel\Controller\ArgumentResolver \ # → Symfony\Component\HttpKernel

­­exclude­regex='/Test/' \ # Symfony\Component\HttpFoundation\Tests

­­output=uml.png

Page 34: Escaping Dependency Hell v2

DEPHPEND - UML (SORT OF)Symfony components

Page 35: Escaping Dependency Hell v2

DEPHPEND - UML (SORT OF)Symfony HTTP Kernel

Page 36: Escaping Dependency Hell v2

DEPENDENCY STRUCTURE MATRIX(DSM)

large graphs are unreadable same data as graph diagrams (e.g. UML class diagram) quick overview for large apps

Page 37: Escaping Dependency Hell v2

DEPHPEND DSM

Page 38: Escaping Dependency Hell v2

NDEPEND EXAMPLE

Page 39: Escaping Dependency Hell v2
Page 40: Escaping Dependency Hell v2
Page 41: Escaping Dependency Hell v2
Page 42: Escaping Dependency Hell v2
Page 43: Escaping Dependency Hell v2

PART III: FIX

Page 44: Escaping Dependency Hell v2

WHAT DO WE WANT?

We want code which is ... ... easier to understand ... easier to maintain ... easier to test

Page 45: Escaping Dependency Hell v2

OBSCURE/NASTY DEPENDENCIES

Some dependencies cannot be detected by any tool (or developer):

Fake collections (array) Overuse of scalar values (int, string, ...) Temporal dependencies, etc.

Page 46: Escaping Dependency Hell v2

BE EXPLICIT!

Implicit dependencies are hard to understand/manage!function sendNewsletter( array $customers, string $message);

function sendNewsletter( CustomerCollection $customers, Message $message);

Page 47: Escaping Dependency Hell v2

WHICH ALLOCATES MORE RAM?ARRAY

$customer = [ 'id' => 123, 'name' => 'John Doe', 'city' => 'Example City',];

CUSTOM CLASSclass Customer /** @var int */ private $id;

/** @var string */ private $name;

/** @var string */ private $city;

Winner (less RAM): custom classProof: http://www.slideshare.net/patrick.allaert/php-data-structures-and-the-impact-of-php-7-on-

them-php-days-2015

Page 48: Escaping Dependency Hell v2

DON'T MAKE ME LOOK IT UP/** * @param mixed $email * @param string|Email|array $email */function addEmail($email) if (is_array($email)) // pray everything inside the array actually is an email foreach ($email as $singleEmail) addEmail($singleEmail); else if (is_string($email)) addEmail(new Email($email)); else if ($email instanceof Email) this­>emails[] = email; else throw new InvalidArgumentException('Bad argument type');

Page 49: Escaping Dependency Hell v2

DON'T MAKE ME LOOK IT UPfunction addEmail(Email $email) $this­>emails[] = $email;

function addEmailString(string $email) $this­>addEmail(new Email($email));

function addEmailArray(array $emails) foreach ($emails as $email) /** @var Email $email */ if (is_string($email)) $this­>addEmailstring($email); else if ($email instanceof Email) $this­>addEmail($email); else throw new InvalidArgumentException('Bad argument type');

function addEmailCollection(EmailCollection $emails) $emails­>each(function (Email $email) $this­>addEmail($email); );

Page 50: Escaping Dependency Hell v2

DON'T MAKE ME LOOK IT UPfunction addEmail(Email $email) $this­>emails[] = $email;

function addEmailCollection(EmailCollection $emails) $emails­>each(function (Email $email) $this­>addEmail($email); );

function addEmailString(string $email) $this­>addEmail(new Email($email));

function addEmailArray(array $emails) foreach ($emails as $email) /** @var Email $email */ if (is_string($email)) $this­>addEmailstring($email); else if ($email instanceof Email) $this­>addEmail($email); else throw new InvalidArgumentException('Bad argument type');

Page 51: Escaping Dependency Hell v2

PRINCIPLES OF OO: SOLID

Single responsibility principle Open/closed principle Liskov substitution principle Interface segregation principle Dependency inversion principle

Page 52: Escaping Dependency Hell v2

DEPENDENCY INVERSIONBAD:

class CustomerRepository public function __construct() $this­>db = new MySQLDatabase(new DefaultConfig());

BETTER:class CustomerRepository public function __construct(MySQLDatabase $db) $this­>db = $db;

GOOD:class CustomerRepository public function __construct(Database $db) $this­>db = $db;

Page 53: Escaping Dependency Hell v2

Easier to understand and test, less likely to break

Page 54: Escaping Dependency Hell v2

DEPENDENCY INJECTIONCONTAINERS

$container = new Pimple\Container();

$container['cstmrrepo'] = function ($database) return new CustomerRepository($database);;

$cstmrRepo = $container['cstmrrepo'];

Too easy? obscure dependencies using YAMLadd another 3rd party libraryadd overhead by parsing meta format/sarcasm

Page 55: Escaping Dependency Hell v2

AVOID IMPLICIT DEPENDENCIES IN FAVOR OFEXPLICIT ONES

// why not do it yourselves?class DependencyInjectionContainer

// eager load public function getCustomerRepository() : CustomerRepository return new CustomerRepository($this­>otherDeps);

// OR: lazy load public function getCustomerRepository() : CustomerRepository if (null === $this­>customerRepository) $this­>customerRepository = new CustomerRepository($this­>otherDeps); return $this­>customerRepository;

$dependencyInjectionContainer­>getCustomerRepository();

Page 56: Escaping Dependency Hell v2

SERVICE LOCATORclass CustomerRepository public function __construct(ServiceLocator $serviceLocator) $this­>db = $serviceLocator­>getDb();

new CustomerRepository($serviceLocator);

Page 57: Escaping Dependency Hell v2

Same or similar implementation, but different usage:

CHOOSE DEPENDENCY INJECTIONCONTAINERS OVER SERVICE LOCATORS

Service Locator provides access to everythingMight as well use globals...(but not really)Target class knows more than it should= more reasons to change (=break)Always choose REAL Dependency Injection

Page 58: Escaping Dependency Hell v2

'MEMBER SYMONFY HTTPKERNEL?

Page 59: Escaping Dependency Hell v2

WHERE TO GO FROM HERE?improve visualizations command for constraint checks caching better Test & CI integration

Contributions, ideas, feedback, bug reports are welcome!

Page 60: Escaping Dependency Hell v2

QUESTIONS

???LINKS

http://www.slideshare.net/michael-haeuslmann/escaping-dependency-hell

https://dephpend.comhttps://github.com/mihaeu/dephpend

Page 61: Escaping Dependency Hell v2

HOW DOES IT WORK?

STATIC ANALYSIS

Transform the code to make parsing easierInfer direct types (require, new, type hints, ...)Infer indirect types (DICs, ...)

DYNAMIC ANALYSIS

profile the applicationtrack function tracescollect all possible input values

Page 62: Escaping Dependency Hell v2

STATIC ANALYSISEasy right?

use SomeNamespace\SomeClass;

class MyClass extends MyParent implements MyInterface

/**

* @return AnotherClass

*/

public function someFunction(SomeClass $someClass) : AnotherClass

StaticClass::staticFunction();

return new AnotherClass();

Page 63: Escaping Dependency Hell v2

STATIC ANALYSISOr is it?

class MyClass

public function someMethod($dependency)

return call_user_func('globalFunction', $dependency);

Page 64: Escaping Dependency Hell v2

or:

DYNAMIC ANALYSISXDebug to the rescue!

; php.ini

zend_extension=/path/to/xdebug.so

[xdebug]xdebug.profiler_enable = 1xdebug.auto_trace=1xdebug.collect_params=1xdebug.collect_return=3xdebug.collect_assignments=1xdebug.trace_format=1xdebug.trace_options=1

# https://github.com/mihaeu/dephpend/blob/develop/bin/dephpendphp­trace ­­dynamic=/path/to/trace­file.xt ­S localhost:8080

Page 65: Escaping Dependency Hell v2

DYNAMIC ANALYSISTRACE START [2016­10­19 16:59:03]1 0 0 0.000189 363984 main 1 /home/mike/workspace/dephpend/bin/dephpend 0 0

2 1 0 0.000209 363984 get_declared_classes 0 /home/mike/workspace/dephpend/bin/dephpend 80

...

11 211058 0 3.503452 4856528 strpos 0 /home/mike/workspace/dephpend/vendor/symfony/console/Formatter/OutputFormatter.php 177 2 string(15111) string(2)

...3 200813 R long 3.504303 238672TRACE END [2016­10­19 16:59:07]

Page 66: Escaping Dependency Hell v2

DYNAMIC ANALYSIS

Parse the trace file and merge with static results

$> dephpend text src \ ­­dynamic=/path/to/trace­file.xt \ ­­filter­from=YourNamespace \ ­­exclude­regex='(Test)|(Mock)'

There are no secrets at runtime!

Page 67: Escaping Dependency Hell v2

@ m i c h a e l h a e u - PHP UG Munich 2016 - h t t p s : / / d e p h p e n d . c o m

CREDITSNDepend

Slide Coder Deviant Art

PDepend Artifacts scaled DSM

http://www.ndepend.com/docs/dependency-structure-matrix-dsm

http://rommeu.deviantart.com/art/Escape-from-Hell-Constantine-204115701

pdepend.org

https://erik.doernenburg.com/2010/04/dependency-structure-matrix/