Upgrade to Pro — share decks privately, control downloads, hide ads and more …

IceHawk - A PHP 7 micro framework respecting CQRS

IceHawk - A PHP 7 micro framework respecting CQRS

An introduction to the OSS micro framework "IceHawk" with a brief history of its emergence and approach, some facts about the project and its scope of application, an overview of features and code of the main component and a short look at additional components.

Talk presented at PHP Usergroup Münster

Holger Woltersdorf

November 21, 2017
Tweet

More Decks by Holger Woltersdorf

Other Decks in Programming

Transcript

  1. NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF

    View Slide

  2. HOLGER WOLTERSDORF
    CIO • FATHER • HUSBAND • PHP DEV WITH ♥
    github.com/hollodotme
    github.com/icehawk
    @hollodotme
    @F9T3ch

    View Slide

  3. View Slide

  4. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    CONTENTS
    4
    HISTORY
    FACTS
    FEATURES
    &
    CODE
    COMPONENTS
    DISCUSSION

    View Slide

  5. HISTORY

    View Slide

  6. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    6
    ๏ THE MVC PATTERN
    VIEW 1 VIEW 2
    CONTROLLER MODEL
    USER INPUT
    MODIFIES
    UPDATES

    View Slide

  7. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    7
    ๏ THE MVC PATTERN …THE PROBLEM!
    VIEW 1 VIEW 2
    CONTROLLER MODEL
    USER INPUT
    MODIFIES
    UPDATES
    !

    View Slide

  8. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    8
    ๏ THE MVC PATTERN …THE PROBLEM!
    VIEW 1 VIEW 2
    CONTROLLER MODEL
    USER INPUT
    MODIFIES
    HTTP

    View Slide

  9. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    9
    ๏ THE MVC PATTERN …THE SOLUTION?
    VIEW 1 VIEW 2
    CONTROLLER MODEL
    USER INPUT
    MODIFIES
    HTTP
    RESPONDS

    View Slide

  10. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    10
    "THE MVC PATTERN
    SOLVES A PROBLEM
    THAT DOESN’T EXIST
    IN THE WEB."

    View Slide

  11. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    11
    "THE MVC PATTERN
    SOLVES A PROBLEM
    THAT DOESN’T EXIST
    IN THE WEB."

    View Slide

  12. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    12
    CONTROLLER
    ๏ THE MVC PATTERN …CONTROLLER RESPONSIBILITIES
    ๏ VALIDATE USER INPUT
    ๏ FIND MODEL(S), REPO(S) 

    HELPER(S) OR SERVICE(S)
    ๏ ISSUE CHANGES
    ๏ EVALUATE CHANGE RESULT
    ๏ FIND MODEL(S), REPO(S)

    HELPER(S) OR SERVICE(S)
    ๏ QUERY AND FETCH DATA
    ๏ RENDER VIEW (2)
    ๏ RESPOND TO CLIENT

    View Slide

  13. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    13
    CONTROLLER
    ๏ THE MVC PATTERN …CONTROLLER RESPONSIBILITIES
    ๏ VALIDATE USER INPUT
    ๏ FIND MODEL(S), REPO(S) 

    HELPER(S) OR SERVICE(S)
    ๏ ISSUE CHANGES
    ๏ EVALUATE CHANGE RESULT
    ๏ FIND MODEL(S), REPO(S)

    HELPER(S) OR SERVICE(S)
    ๏ QUERY AND FETCH DATA
    ๏ RENDER VIEW (2)
    ๏ RESPOND TO CLIENT
    WRITE READ

    View Slide

  14. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    14
    WRITE
    CONTROLLER
    ๏ SEGREGATED RESPONSIBILITIES
    ๏ VALIDATE USER INPUT
    ๏ FIND MODEL(S), REPO(S) 

    HELPER(S) OR SERVICE(S)
    ๏ ISSUE CHANGES
    ๏ EVALUATE CHANGE RESULT
    ๏ FIND MODEL(S), REPO(S)

    HELPER(S) OR SERVICE(S)
    ๏ QUERY AND FETCH DATA
    ๏ RENDER VIEW (2)
    ๏ RESPOND TO CLIENT
    WRITE READ
    READ
    CONTROLLER

    View Slide

  15. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    15
    WRITE
    CONTROLLER
    ๏ RESPONSIBILITY SEGREGATION
    ๏ VALIDATE USER INPUT
    ๏ FIND MODEL(S), REPO(S) 

    HELPER(S) OR SERVICE(S)
    ๏ ISSUE CHANGES
    ๏ EVALUATE CHANGE RESULT
    ๏ FIND MODEL(S), REPO(S)

    HELPER(S) OR SERVICE(S)
    ๏ QUERY AND FETCH DATA
    ๏ RENDER VIEW (2)
    ๏ RESPOND TO CLIENT
    COMMAND QUERY
    READ
    CONTROLLER

    View Slide

  16. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    16
    WRITE
    CONTROLLER
    ๏ RESPONSIBILITY SEGREGATION
    ๏ VALIDATE USER INPUT
    ๏ FIND MODEL(S), REPO(S) 

    HELPER(S) OR SERVICE(S)
    ๏ ISSUE CHANGES
    ๏ EVALUATE CHANGE RESULT
    ๏ FIND MODEL(S), REPO(S)

    HELPER(S) OR SERVICE(S)
    ๏ QUERY AND FETCH DATA
    ๏ RENDER VIEW (2)
    ๏ RESPOND TO CLIENT
    COMMAND QUERY
    READ
    CONTROLLER
    RESPONSIBILITY SEGREGATION
    (CQRS)
    TA-DA!

    View Slide

  17. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    17
    ๏ THE CQRS PATTERN (respecting HTTP)
    WRITE
    REQUESTHANDLER
    APP
    STATE
    USER INPUT
    MODIFIES QUERIES
    POST
    CLIENT / BROWSER
    REDIRECT
    TO PAGE 2
    GET
    READ
    REQUESTHANDLER
    PAGE 1 PAGE 2
    RESPONDS

    View Slide

  18. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    PROJECTOR
    CHANGE
    PAGE 2
    HISTORY
    18
    ๏ OPTIMISATION FOR READ INTENSIVE APPLICATIONS
    WRITE
    REQUESTHANDLER
    APP
    STATE
    USER INPUT
    MODIFIES
    POST
    CLIENT / BROWSER
    REDIRECT
    TO PAGE 2
    GET
    PAGE 1
    PAGE 2
    CHANGE
    MESSAGE
    (ASYNC)
    PROJECTOR
    PREPARES
    PUB SUB
    QUERIES

    View Slide

  19. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    19
    […] the convention has been established
    that the GET and HEAD methods
    SHOULD NOT have the significance
    of taking an action other than retrieval.
    These methods ought
    to be considered "safe".
    https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

    View Slide

  20. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    HISTORY
    20
    ๏ RULES SUMMARISED (FOR WEBSITES)
    ๏ A POST request MUST NOT not respond with UI content
    ๏ A POST request SHOULD change the application state
    ๏ A POST request SHOULD respond with a redirect
    ๏ A GET request SHOULD respond with UI content 

    representing the current application state
    ๏ A GET request MUST NOT change the application state (read-only)

    View Slide

  21. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    21
    «We need a name!»
    «Yeah, something cool and fast!»
    HISTORY

    View Slide

  22. ICEHAWK

    View Slide

  23. FACTS

    View Slide

  24. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FACTS
    24
    ๏ COMPONENTS
    STABLE v2.2.0 STABLE v1.1.1 STABLE v1.0.0 STABLE v1.0.1
    ICEHAWK SESSION FORMS PUBSUB
    COVERAGE 100% COVERAGE 100% COVERAGE 100% COVERAGE 100%

    View Slide

  25. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FACTS
    25
    ๏ MAIN COMPONENT: icehawk/icehawk
    1.533 Logical lines of code (LLOC)
    0 composer dependencies
    0 third-party php extension dependencies
    v2.2.0 current stable
    v7.x.x php compatibility (only)
    STRICT typed
    MIT license
    FULLY documented at icehawk.github.io
    LOC
    Lines of code 1965
    Logical lines of code 1533
    Comment lines of code 433
    Average volume 71.86
    Average comment weight 21.28
    Average intelligent content 21.28
    Logical lines of code by class 29
    Logical lines of code by method 5
    Object oriented programming
    Classes 52
    Interface 33
    Methods 279
    Methods by class 5.37
    Lack of cohesion of methods 0.62
    Average afferent coupling 0.66
    Average efferent coupling 1.15
    Average instability 0.61
    Complexity
    Average Cyclomatic complexity by class 1.65
    Average Relative system complexity 17.92
    Average Difficulty 2.6

    View Slide

  26. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FACTS
    26
    ๏ OBJECTIVES
    ๏ Producing comprehensible and readable code
    ๏ Avoiding inline comments where possible
    (includes PHPDoc)
    ๏ Applying the SOLID and CLEAN CODE principles
    ๏ Covering all code with tests
    ๏ Refactoring continuously
    ๏ Targeting the latest PHP version
    ๏ Doing open source
    ๏ APPLICABLE FOR
    ๏ WEBSITES
    ๏ (RESTful) APIs
    ๏ MICRO SERVICES
    ๏ EVENT SOURCING

    View Slide

  27. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FACTS
    27
    NAMING CONVENTION
    interface BypassesRequest {}
    class RequestBypass {}
    trait DefaultRequestBypassing {}

    View Slide

  28. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FACTS
    28
    POST
    PUT
    PATCH
    DELETE
    GET
    HEAD
    OPTIONS
    WRITE
    REQUEST
    HANDLER
    READ
    REQUEST
    HANDLER
    AUTO-RESPONSE
    BASIC ROUTING BEHAVIOUR

    View Slide

  29. FEATURES & CODE
    (SELECTION)

    View Slide

  30. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    30
    INSTALLATION

    View Slide

  31. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    31

    View Slide

  32. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    32
    class IceHawk
    {
    public function __construct( $config, $delegate )
    public function init() {}
    public function handleRequest() {}
    }

    View Slide

  33. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    33
    namespace MyVendor\MyProject;
    use IceHawk\IceHawk\IceHawk;
    require(__DIR__ . '/../vendor/autoload.php');
    $config = new IceHawkConfig();
    $delegate = new IceHawkDelegate();
    $application = new IceHawk( $config, $delegate );
    $application->init();
    $application->handleRequest();

    View Slide

  34. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    34
    DELEGATION

    View Slide

  35. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - DELEGATION
    35
    final class IceHawkDelegate implements SetsUpEnvironment
    {
    public function setUpGlobalVars()
    {
    # Change your global vars $_SERVER, $_GET, $_POST, etc.
    # here, before IceHawk will use them.
    }
    public function setUpErrorHandling( ProvidesRequestInfo $requestInfo )
    {
    # PHP's default error handling is used unless you set up
    # something else here.
    }
    public function setUpSessionHandling( ProvidesRequestInfo $requestInfo )
    {
    # PHP's default session handling is used unless you set up
    # something else here.
    }
    }

    View Slide

  36. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - DELEGATION
    36
    final class IceHawkDelegate implements SetsUpEnvironment
    {
    public function setUpGlobalVars()
    {
    # Change your global vars $_SERVER, $_GET, $_POST, etc.
    # here, before IceHawk will use them.
    }
    public function setUpErrorHandling( ProvidesRequestInfo $requestInfo )
    {
    # PHP's default error handling is used unless you set up
    # something else here.
    }
    public function setUpSessionHandling( ProvidesRequestInfo $requestInfo )
    {
    # PHP's default session handling is used unless you set up
    # something else here.
    }
    }

    View Slide

  37. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - DELEGATION
    37
    final class IceHawkDelegate implements SetsUpEnvironment
    {
    public function setUpGlobalVars()
    {
    # Change your global vars $_SERVER, $_GET, $_POST, etc.
    # here, before IceHawk will use them.
    }
    public function setUpErrorHandling( ProvidesRequestInfo $requestInfo )
    {
    # PHP's default error handling is used unless you set up
    # something else here.
    }
    public function setUpSessionHandling( ProvidesRequestInfo $requestInfo )
    {
    # PHP's default session handling is used unless you set up
    # something else here.
    }
    }

    View Slide

  38. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    38
    CONFIGURATION

    View Slide

  39. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - CONFIGURATION
    39
    final class IceHawkConfig implements ConfiguresIceHawk
    {
    use Defaults\Traits\DefaultRequestInfoProviding;
    use Defaults\Traits\DefaultReadRouting;
    use Defaults\Traits\DefaultWriteRouting;
    use Defaults\Traits\DefaultRequestBypassing;
    use Defaults\Traits\DefaultEventSubscribing;
    use Defaults\Traits\DefaultCookieProviding;
    use Defaults\Traits\DefaultFinalReadResponding;
    use Defaults\Traits\DefaultFinalWriteResponding;
    }

    View Slide

  40. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - CONFIGURATION
    40
    final class IceHawkConfig implements ConfiguresIceHawk
    {
    use Defaults\Traits\DefaultRequestInfoProviding;
    use Defaults\Traits\DefaultReadRouting;
    use Defaults\Traits\DefaultWriteRouting;
    use Defaults\Traits\DefaultRequestBypassing;
    use Defaults\Traits\DefaultEventSubscribing;
    use Defaults\Traits\DefaultCookieProviding;
    use Defaults\Traits\DefaultFinalReadResponding;
    use Defaults\Traits\DefaultFinalWriteResponding;
    }

    View Slide

  41. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - CONFIGURATION
    41
    final class MyHawkConfig extends IceHawkConfig
    {
    public function getReadRoutes()
    {
    return [
    new ReadRoute(
    new Literal( '/' ),
    new SayHelloRequestHandler()
    ),
    ];
    }
    public function getWriteRoutes()
    {
    return [
    new WriteRoute(
    new Literal( '/do-something' ),
    new DoSomethingRequestHandler()
    ),
    ];
    }
    }

    View Slide

  42. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - CONFIGURATION
    42
    # Literal route
    new ReadRoute(
    new Literal( '/' ),
    new SayHelloRequestHandler()
    );
    # RegExp route with parameters from matches
    new ReadRoute(
    new RegExp("#^/post/([0-9]+)/?$#i", ['postId'] ),
    new ShowBlogPostRequestHandler()
    );
    # NamedRegExp route with named matches
    new ReadRoute(
    new NamedRegExp("^/post/(?[0-9]+)/?$", 'i'),
    new ShowBlogPostRequestHandler()
    );

    View Slide

  43. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - CONFIGURATION
    43
    # Literal route
    new ReadRoute(
    new Literal( '/' ),
    new SayHelloRequestHandler()
    );
    # RegExp route with parameters from matches
    new ReadRoute(
    new RegExp("#^/post/([0-9]+)/?$#i", ['postId'] ),
    new ShowBlogPostRequestHandler()
    );
    # NamedRegExp route with named matches
    new ReadRoute(
    new NamedRegExp("^/post/(?[0-9]+)/?$", 'i'),
    new ShowBlogPostRequestHandler()
    );

    View Slide

  44. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - CONFIGURATION
    44
    # Literal route
    new ReadRoute(
    new Literal( '/' ),
    new SayHelloRequestHandler()
    );
    # RegExp route with parameters from matches
    new ReadRoute(
    new RegExp("#^/post/([0-9]+)/?$#i", ['postId'] ),
    new ShowBlogPostRequestHandler()
    );
    # NamedRegExp route with named matches
    new ReadRoute(
    new NamedRegExp("^/post/(?[0-9]+)/?$", 'i'),
    new ShowBlogPostRequestHandler()
    );

    View Slide

  45. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - CONFIGURATION
    45
    final class MyHawkConfig extends IceHawkConfig
    {
    public function getReadRoutes()
    {
    $routes = require(__DIR__ . '../config/ReadRoutes.php');
    # $routes = [
    # "^/post/(?[0-9]+)/?"
    # => MyVendor\...\ShowPostRequestHandler::class,
    # ];
    foreach ($routes as $pattern => $handlerClass)
    {
    yield new ReadRoute(
    new NamedRegExp($pattern),
    new $handlerClass()
    ),
    }
    }
    }

    View Slide

  46. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    46
    REQUEST BYPASSING

    View Slide

  47. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - REQUEST BYPASSING
    47
    final class IceHawkConfig implements ConfiguresIceHawk
    {
    use Defaults\Traits\DefaultRequestInfoProviding;
    use Defaults\Traits\DefaultReadRouting;
    use Defaults\Traits\DefaultWriteRouting;
    use Defaults\Traits\DefaultRequestBypassing;
    use Defaults\Traits\DefaultEventSubscribing;
    use Defaults\Traits\DefaultCookieProviding;
    use Defaults\Traits\DefaultFinalReadResponding;
    use Defaults\Traits\DefaultFinalWriteResponding;
    }

    View Slide

  48. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - REQUEST BYPASSING
    48
    final class MyHawkConfig extends IceHawkConfig
    {
    public function getRequestBypasses()
    {
    return [
    new RequestBypass(
    new Literal('/came/via/get'),
    '/do/some/write/action',
    HttpMethod::POST
    ),
    ];
    }
    public function getWriteRoutes()
    {
    return [
    new WriteRoute(
    new Literal( '/do/some/write/action' ),
    new DoSomethingRequestHandler()
    ),
    ];
    }
    }

    View Slide

  49. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - REQUEST BYPASSING
    49
    final class MyHawkConfig extends IceHawkConfig
    {
    public function getRequestBypasses()
    {
    return [
    new RequestBypass(
    new Literal('/came/via/get'),
    '/do/some/write/action',
    HttpMethod::POST
    ),
    ];
    }
    public function getWriteRoutes()
    {
    return [
    new WriteRoute(
    new Literal( '/do/some/write/action' ),
    new DoSomethingRequestHandler()
    ),
    ];
    }
    }

    View Slide

  50. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    50
    REQUEST HANDLERS

    View Slide

  51. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - REQUEST HANDLERS
    51
    final class SayHelloRequestHandler
    implements HandlesGetRequest
    {
    public function handle(ProvidesReadRequestData $request)
    {
    $info = $request->getInfo();
    $input = $request->getInput();
    $cookies = $request->getCookies();
    (new Page())->respond('Hello!', HttpCode::OK);
    }
    }

    View Slide

  52. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - REQUEST HANDLERS
    52
    final class DoSomethingRequestHandler
    implements HandlesPostRequest
    {
    public function handle(ProvidesWriteRequestData $request)
    {
    $info = $request->getInfo();
    $input = $request->getInput();
    $cookies = $request->getCookies();
    $files = $input->getAllUploadedFiles();
    (new Redirect())->respond('/done');
    }
    }

    View Slide

  53. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    53
    EVENTS & SUBSCRIBERS

    View Slide

  54. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - EVENTS & SUBSCRIBERS
    54
    final class IceHawkConfig implements ConfiguresIceHawk
    {
    use Defaults\Traits\DefaultRequestInfoProviding;
    use Defaults\Traits\DefaultReadRouting;
    use Defaults\Traits\DefaultWriteRouting;
    use Defaults\Traits\DefaultRequestBypassing;
    use Defaults\Traits\DefaultEventSubscribing;
    use Defaults\Traits\DefaultCookieProviding;
    use Defaults\Traits\DefaultFinalReadResponding;
    use Defaults\Traits\DefaultFinalWriteResponding;
    }

    View Slide

  55. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - EVENTS & SUBSCRIBERS
    55
    final class MyHawkConfig extends IceHawkConfig
    {
    public function getEventSubscribers() : array
    {
    return [
    new LogSubscriber(),
    ];
    }
    }

    View Slide

  56. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - EVENTS & SUBSCRIBERS
    56
    final class LogSubscriber extends AbstractEventSubscriber
    {
    private $startTime;
    public function getAcceptedEvents() : array
    {
    return [
    IceHawk\IceHawk\Events\HandlingReadRequestEvent::class,
    IceHawk\IceHawk\Events\ReadRequestWasHandledEvent::class,
    ];
    }
    public function whenHandlingReadRequest(HandlingReadRequestEvent $event)
    {
    $this->startTime = microtime(true);
    }
    public function whenReadRequestWasHandled(ReadReqeustWasHandledEvent $event)
    {
    error_log( sprintf(
    "Your app took %f seconds to handle the request on URI: %s",
    (microtime(true) - $this->startTime),
    $event->getRequestInfo()->getUri()
    ), 3, 'file.log' );
    }
    }

    View Slide

  57. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE
    57
    FINAL RESPONDING

    View Slide

  58. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - FINAL RESPONDING
    58
    final class IceHawkConfig implements ConfiguresIceHawk
    {
    use Defaults\Traits\DefaultRequestInfoProviding;
    use Defaults\Traits\DefaultReadRouting;
    use Defaults\Traits\DefaultWriteRouting;
    use Defaults\Traits\DefaultRequestBypassing;
    use Defaults\Traits\DefaultEventSubscribing;
    use Defaults\Traits\DefaultCookieProviding;
    use Defaults\Traits\DefaultFinalReadResponding;
    use Defaults\Traits\DefaultFinalWriteResponding;
    }

    View Slide

  59. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - FINAL RESPONDING
    59
    final class MyHawkConfig extends IceHawkConfig
    {
    public function getFinalReadResponder()
    : RespondsFinallyToReadRequest
    {
    return MyFinalReadResponder();
    }
    }

    View Slide

  60. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FEATURES & CODE - FINAL RESPONDING
    60
    final class MyFinalReadResponder
    implements RespondsFinallyToReadRequest
    {
    public function handleUncaughtException(
    \Throwable $throwable,
    ProvidesReadReqeustData $request
    )
    {
    try
    {
    throw $throwable;
    }
    catch (UnresolvedRequest $e)
    {
    (new NotFound())->respond();
    }
    catch (\Throwable $e)
    {
    (new InternalServerError())->respond();
    }
    }
    }

    View Slide

  61. COMPONENTS

    View Slide

  62. SESSION

    View Slide

  63. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    SESSION
    63
    ๏ ABSTRACT REGISTRY
    ๏ Needs inheritance
    ๏ Implement typed getters, setters, issets and unsets
    ๏ Allows registration of data mappers

    View Slide

  64. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    SESSION - EXAMPLE IMPLEMENTATON
    64
    final class Session extends AbstractSession
    {
    const KEY_SOME_STRING_VALUE = 'someStringValue';
    public function getSomeStringValue() : string
    {
    return $this->get( self::KEY_SOME_STRING_VALUE );
    }
    public function setSomeStringValue( string $value )
    {
    $this->set( self::KEY_SOME_STRING_VALUE, $value );
    }
    public function isSomeStringValueSet() : bool
    {
    return $this->isset( self::KEY_SOME_STRING_VALUE );
    }
    public function unsetSomeStringValue()
    {
    $this->unset( self::KEY_SOME_STRING_VALUE );
    }
    }

    View Slide

  65. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    SESSION - DATA MAPPER
    65
    final class MyDataMapper implements MapsSessionData
    {
    public function toSessionData( $value )
    {
    return base64_encode( $value );
    }
    public function fromSessionData( $sessionData )
    {
    return base64_decode( $sessionData );
    }
    }

    View Slide

  66. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    SESSION - REGISTER DATA MAPPERS
    66
    # Add the data mapper for all keys in the registry
    $session->addDataMapper( new MyDataMapper() );
    # Add the data mapper for one specific key in the registry
    $session->addDataMapper(
    new MyDataMapper(),
    [Session::KEY_SOME_STRING_VALUE]
    );
    # Add the data mapper for multiple keys in the registry
    $session->addDataMapper(
    new MyDataMapper(),
    [
    Session::KEY_SOME_STRING_VALUE,
    Session::KEY_SOME_OTHER_VALUE
    ]
    );

    View Slide

  67. FORMS

    View Slide

  68. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FORMS
    68
    ๏ DTO TO BE STORED IN SESSION
    ๏ CSRF protection using tokens (with expiration)
    ๏ Data pre-setting
    ๏ Gets input validation feedback (with severity) on write side
    ๏ Provides input validation feedback on read side

    View Slide

  69. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FORMS - READ SIDE INITIALISATION
    69
    $form = new Form( new FormId('profile') );
    $form->renewToken();
    if ( !$form->wasDataSet() )
    {
    # Set up some default data (single)
    $form->set( 'username', 'johndoe' );
    }

    View Slide

  70. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    FORMS - WRITE SIDE
    70
    $form = $session->getForm( new FormId('profile') );
    $token = Token::fromString($input->get('token'));
    $form->guardTokenIsValid($token);
    $form->addFeedback(
    'user',
    new Feedback(
    'Invalid username',
    Feedback::ERROR
    )
    );

    View Slide

  71. PUBSUB

    View Slide

  72. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    PUBSUB
    72
    ๏ BASIC PUBLISH SUBSCRIBE SYSTEM TO DECOUPLE BUSINESS LOGIC
    ๏ Defines interfaces for messages, channels and subscribers
    ๏ Provides a message bus for publishing and subscribing
    ๏ Provides abstract message subscriber with auto-method resolving

    View Slide

  73. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    PUBSUB - A MESSAGE
    73
    final class YourMessage implements CarriesInformation
    {
    public function getMessageId() : IdentifiesMessage
    {
    return new MessageId('message-xyz');
    }
    public function getMessageName() : NamesMessage
    {
    return new MessageName('User email address changed');
    }
    public function getContent() : string
    {
    return '[email protected]';
    }
    }

    View Slide

  74. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    PUBSUB - A SUBSCRIBER
    74
    final class YourSubscriber extends AbstractMessageSubscriber
    {
    protected function whenSomethingHadHappened(
    YourMessage $yourMessage,
    Channel $channel
    )
    {
    printf(
    'Message named "%s" with ID "%s" '
    . 'was published on channel "%" with content: "%s"',
    $yourMessage->getMessageName(),
    $yourMessage->getMessageId(),
    $channel,
    $yourMessage->getContent()
    );
    }
    }

    View Slide

  75. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    PUBSUB - THE SUBSCRIPTION
    75
    $messageBus = new MessageBus();
    $messageBus->subscribe(
    new Channel('ListenToMe'),
    new YourSubscriber()
    );

    View Slide

  76. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    PUBSUB - PUBLISH
    76
    $messageBus->publish(
    new Channel('ListenToMe'),
    new YourMessage()
    );

    View Slide

  77. FULL DOCUMENTATION AT
    icehawk.github.io

    View Slide

  78. QUESTIONS?

    View Slide

  79. THANK YOU!
    github.com/hollodotme
    @hollodotme / @F9T3ch
    fortuneglobe.com
    phpug-dresden.org
    @phpugdd
    HOLGER WOLTERSDORF
    https://hollo.me
    icehawk.github.io
    speakerdeck.com/hollodotme
    Slides available at:

    View Slide

  80. ICEHAWK FRAMEWORK • NOVEMBER 21st 2017 • PHP USERGROUP MÜNSTER HOLGER WOLTERSDORF
    LINKS / REFERENCES
    80
    ๏ MVC-Pattern: http://martinfowler.com/eaaDev/uiArchs.html
    ๏ CQRS-Pattern: http://martinfowler.com/bliki/CQRS.html
    ๏ GET/HEAD methods: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
    ๏ SOLID principles https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
    ๏ "Clean Code" by Robert C. Martin: http://amzn.to/2dthxQn
    ๏ "Traitful" Configs: https://hollo.me/php/traitful-configs.html
    ๏ IceHawk Gitter chat: https://gitter.im/icehawk/Lobby

    View Slide