Build Your Framework Like Constructicons - PHPKonf 2017

Build Your Framework Like Constructicons - PHPKonf 2017

While we have a strong offering of full-stack frameworks and microframeworks, the rise of components and libraries combined with Composer allows us to easily build our own framework without reinventing the wheel. In this talk, I'll show you how the total can be more than the sum of the parts, such as Devastator was stronger than the individual Constructicons in Transformers.

A8f72e32766355f12a56ede9aaa0ee78?s=128

Stefan Koopmanschap

May 20, 2017
Tweet

Transcript

  1. None
  2. BUILD YOUR FRAMEWORK LIKE CONSTRUCTICONS

  3. HI, I'M STEFAN

  4. HI, I'M STEFAN > Ingewikkeld

  5. HI, I'M STEFAN > Ingewikkeld > former Zend Framework translator

  6. HI, I'M STEFAN > Ingewikkeld > former Zend Framework translator

    > former Symfony Community Manager
  7. None
  8. None
  9. None
  10. None
  11. None
  12. WAIT, WHAT?

  13. WHO HAS BUILT THEIR OWN FRAMEWORK?

  14. None
  15. WHO IS STILL USING A CUSTOM-BUILT FRAMEWORK?

  16. None
  17. THE CASE FOR A FRAMEWORK

  18. None
  19. None
  20. OPEN SOURCE

  21. SO WHY WOULD WE BUILD IT OURSELVES?

  22. SO WHY WOULD WE BUILD IT OURSELVES? > Custom needs

  23. SO WHY WOULD WE BUILD IT OURSELVES? > Custom needs

    > Because we're developers (NIH)
  24. SO WHY WOULD WE BUILD IT OURSELVES? > Custom needs

    > Because we're developers (NIH) > An open source framework is overkill
  25. SO WHY WOULD WE BUILD IT OURSELVES? > Custom needs

    > Because we're developers (NIH) > An open source framework is overkill > As a learning experience
  26. SURELY, THERE ARE MANY MORE REASONS

  27. IT IS OK TO BUILD YOUR OWN FRAMEWORK

  28. IT IS NOT OK TO WRITE ALL THE CODE FOR

    IT YOURSELF
  29. WELL, ACTUALLY...

  30. None
  31. WRONG MOVIE

  32. YESTERDAY

  33. YESTERDAY

  34. COMPOSER

  35. None
  36. None
  37. REQUEST AND RESPONSE

  38. ROUTING

  39. TEMPLATING

  40. HANDLING CONFIGURATION

  41. SENDING E-MAIL

  42. DOING VALIDATION

  43. THE CODE

  44. COMPOSER INIT

  45. COMPOSER INIT { "name": "stefankoopmanschap/constructicons", "authors": [ { "name": "Stefan

    Koopmanschap", "email": "stefan@ingewikkeld.net" } ], "require": {} }
  46. REQUEST AND RESPONSE

  47. REQUEST AND RESPONSE > react/http

  48. REQUEST AND RESPONSE > react/http > illuminate/http

  49. REQUEST AND RESPONSE > react/http > illuminate/http > symfony/http-foundation

  50. REQUEST AND RESPONSE composer require symfony/http-foundation

  51. REQUEST AND RESPONSE "require": { "symfony/http-foundation": "^3.1" }

  52. INDEX.PHP AND COMPOSER require( __DIR__ . ' /../vendor/autoload.php' );

  53. REQUEST AND RESPONSE use \Symfony\Component\HttpFoundation\Request; use \Symfony\Component\HttpFoundation\Response; $request = Request::createFromGlobals();

    $response = new Response( 'Hello ' . $request->get('name', 'world') ); $response->send();
  54. /?NAME=STEFAN RESULTS IN HELLO STEFAN

  55. None
  56. ROUTING

  57. ROUTING > symfony/routing

  58. ROUTING > symfony/routing > illuminate/routing

  59. ROUTING > symfony/routing > illuminate/routing > zendframework/zend-router

  60. ROUTING > symfony/routing > illuminate/routing > zendframework/zend-router > nikic/fast-route

  61. ROUTING composer require nikic/fast-route

  62. ROUTING $dispatcher = FastRoute\simpleDispatcher( function(FastRoute\RouteCollector $r) use ($request) { $r->addRoute('GET',

    '/hello', function() use ($request) { $response = new Response( 'Hello ' . $request->get('name', 'world') ); $response->send(); }); $r->addRoute('GET', '/goodbye', function() use ($request) { $response = new Response( 'Goodbye ' . $request->get('name', 'world') ); $response->send(); }); } );
  63. ROUTING $dispatcher = FastRoute\simpleDispatcher( function(FastRoute\RouteCollector $r) use ($request) { //

    do stuff here } );
  64. ROUTING $r->addRoute('GET', '/hello', function() use ($request) { $response = new

    Response( 'Hello ' . $request->get('name', 'world') ); $response->send(); }); $r->addRoute('GET', '/goodbye', function() use ($request) { $response = new Response( 'Goodbye ' . $request->get('name', 'world') ); $response->send(); });
  65. ROUTING $r->addRoute('GET', '/hello', function() use ($request) { $response = new

    Response( 'Hello ' . $request->get('name', 'world') ); $response->send(); }); $r->addRoute('GET', '/goodbye', function() use ($request) { $response = new Response( 'Goodbye ' . $request->get('name', 'world') ); $response->send(); });
  66. ROUTING $r->addRoute('GET', '/hello', function() use ($request) { $response = new

    Response( 'Hello ' . $request->get('name', 'world') ); $response->send(); }); $r->addRoute('GET', '/goodbye', function() use ($request) { $response = new Response( 'Goodbye ' . $request->get('name', 'world') ); $response->send(); });
  67. ROUTING $routeInfo = $dispatcher->dispatch( $request->getMethod(), $request->getRequestUri() ); if (is_callable($routeInfo[1])) {

    $routeInfo[1](); }
  68. ROUTING $routeInfo = $dispatcher->dispatch( $request->getMethod(), $request->getRequestUri() ); if (is_callable($routeInfo[1])) {

    $routeInfo[1](); }
  69. ROUTING $r->addRoute('GET', '/hello[/{name}]', function($params) use ($request) { $name = $params['name']

    ?? 'world'; $response = new Response( 'Hello ' . $name ); $response->send(); }); $r->addRoute('GET', '/goodbye[/{name}]', function() use ($request) { $name = $params['name'] ?? 'world'; $response = new Response( 'Goodbye ' . $name ); $response->send(); });
  70. ROUTING if (is_callable($routeInfo[1])) { $params = ($routeInfo[2]) ?? []; $routeInfo[1]($params);

    }
  71. None
  72. TEMPLATING

  73. TEMPLATING > zetacomponents/template

  74. TEMPLATING > zetacomponents/template > eden/template

  75. TEMPLATING > zetacomponents/template > eden/template > twig/twig

  76. TEMPLATING composer require twig/twig

  77. TEMPLATING $twigLoader = new Twig_Loader_Filesystem( __DIR__ . '/../views/' ); $twig

    = new Twig_Environment($twigLoader, [ 'cache' => '/tmp/twigCache', ]);
  78. TEMPLATING function(FastRoute\RouteCollector $r) use ( $request, $twig ) {

  79. TEMPLATING $r->addRoute( 'GET', '/hello[/{name}]', function($params) use ($request, $twig) { $name

    = $params['name'] ?? 'world'; $response = new Response( $twig->render('hello.twig', [ 'name' => $name ] ) ); $response->send(); });
  80. TEMPLATING hello.twig Hello {{ name }} goodbye.twig Goodbye {{ name

    }}
  81. HANDLING CONFIGURATION

  82. HANDLING CONFIGURATION > symfony/config

  83. HANDLING CONFIGURATION > symfony/config > illuminate/config

  84. HANDLING CONFIGURATION > symfony/config > illuminate/config > packaged/config

  85. HANDLING CONFIGURATION > symfony/config > illuminate/config > packaged/config > werx/config

  86. HANDLING CONFIGURATION $r->addRoute( 'GET', '/contact', function() use ($request, $twig) {

    $response = new Response( $twig->render('contact.twig', []) ); $response->send(); } );
  87. HANDLING CONFIGURATION <form method="post" action="/contact"> Your name: <input type="name"><br> Your

    e-mailaddress: <input type="email"><br> Your message:<br> <textarea></textarea><br> <input type="submit" name="submit" value="Contact Us"> </form>
  88. HANDLING CONFIGURATION $r->addRoute( 'POST', '/contact', function() use ($request) { $mailbody

    = 'An e-mail was sent through the site with the following message: ' . $request->get('message') . "\n"; $mailbody .= 'Sent by: ' . $request->get('name') . '<' . $request->get('email') . '>'; mail( 'my-email@example.org', 'An e-mail from the site', $mailbody ); $response = new \Symfony\Component\HttpFoundation\RedirectResponse('/hello'); $response->send(); } );
  89. HANDLING CONFIGURATION composer require werx/config

  90. HANDLING CONFIGURATION $configProvider = new \werx\Config\Providers\JsonProvider( __DIR__ . '/../config/' );

    $config = new \werx\Config\Container($configProvider); $config->load('config');
  91. HANDLING CONFIGURATION $r->addRoute( 'POST', '/contact', function() use ($request, $config) {

  92. HANDLING CONFIGURATION mail( $config->get('contact_email'), 'An e-mail from the site', $mailbody

    );
  93. HANDLING CONFIGURATION mail( $config->get('contact_email'), 'An e-mail from the site', $mailbody

    );
  94. SENDING E-MAIL

  95. SENDING E-MAIL > nette/mail

  96. SENDING E-MAIL > nette/mail > zetacomponents/mail

  97. SENDING E-MAIL > nette/mail > zetacomponents/mail > swiftmailer/swiftmailer

  98. SENDING E-MAIL composer require swiftmailer/swiftmailer

  99. SENDING E-MAIL $mailerTransport = Swift_SmtpTransport::newInstance( $config->get('mailer_server'), $config->get('mailer_port') ) ->setUsername($config->get('mailer_username')) ->setPassword($config->get('mailer_password'))

    ; $mailer = Swift_Mailer::newInstance($mailerTransport);
  100. SENDING E-MAIL $message = Swift_Message::newInstance('An e-mail from the site') ->setFrom([

    $request->get('email'), $request->get('name'), ]) ->setTo([ $config->get('contact_email'), ]) ->setBody('An e-mail was sent through the site with the following message: ' . $request->get('message')) ; $mailer->send($message);
  101. DOING VALIDATION

  102. DOING VALIDATION > symfony/validator

  103. DOING VALIDATION > symfony/validator > zendframework/zend-validator

  104. DOING VALIDATION > symfony/validator > zendframework/zend-validator > particle/validator

  105. DOING VALIDATION composer require particle/validator

  106. DOING VALIDATION $contactMailValidator = new \Particle\Validator\Validator(); $contactMailValidator ->required('name') ->string(); $contactMailValidator

    ->required('email') ->email(); $contactMailValidator ->required('message') ->string();
  107. DOING VALIDATION $r->addRoute( 'POST', '/contact', function() use ($request, $config, $mailer,

    $contactMailValidator, $twig) { $result = $contactMailValidator->validate([ 'name' => $request->get('name'), 'email' => $request->get('email'), 'message' => $request->get('message') ]); if ($result->isValid()) { // send e-mail and redirect } return new Response($twig->render('contact.twig', [ 'errors' => $result->getMessages(), ])); });
  108. SUMMARY

  109. SUMMARY > valid use cases for building your own framework

  110. SUMMARY > valid use cases for building your own framework

    > Leverage the power of open source components and composer
  111. SUMMARY > valid use cases for building your own framework

    > Leverage the power of open source components and composer > Only need a few lines of glue
  112. SOME OTHER COMPONENTS YOU COULD THINK OF

  113. SOME OTHER COMPONENTS YOU COULD THINK OF > DI (bitexpert/disco,

    symfony/dependency-injection, league/container)
  114. SOME OTHER COMPONENTS YOU COULD THINK OF > DI (bitexpert/disco,

    symfony/dependency-injection, league/container) > Console (symfony/console, illuminate/console, webmozart/console)
  115. SOME OTHER COMPONENTS YOU COULD THINK OF > DI (bitexpert/disco,

    symfony/dependency-injection, league/container) > Console (symfony/console, illuminate/console, webmozart/console) > http requests (Guzzle)
  116. SOME OTHER COMPONENTS YOU COULD THINK OF > DI (bitexpert/disco,

    symfony/dependency-injection, league/container) > Console (symfony/console, illuminate/console, webmozart/console) > http requests (Guzzle) > logging (monolog/monolog)
  117. None
  118. PHOTO CREDITS

  119. PHOTO CREDITS > Foundation by Paul Sableman, cc-by 2.0

  120. PHOTO CREDITS > Foundation by Paul Sableman, cc-by 2.0 >

    Constructicon icons: James Peng
  121. THANK YOU! QUESTIONS? HTTPS://LEFTONTHEWEB.COM @SKOOP