Build Your Framework Like Constructicons - 010PHP

Build Your Framework Like Constructicons - 010PHP

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 11, 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. COMPOSER

  33. None
  34. None
  35. REQUEST AND RESPONSE

  36. ROUTING

  37. TEMPLATING

  38. HANDLING CONFIGURATION

  39. SENDING E-MAIL

  40. DOING VALIDATION

  41. THE CODE

  42. COMPOSER INIT

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

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

  45. REQUEST AND RESPONSE > react/http

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

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

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

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

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

  51. 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();
  52. /?NAME=STEFAN RESULTS IN HELLO STEFAN

  53. None
  54. ROUTING

  55. ROUTING > symfony/routing

  56. ROUTING > symfony/routing > illuminate/routing

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

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

  59. ROUTING composer require nikic/fast-route

  60. 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(); }); } );
  61. ROUTING $dispatcher = FastRoute\simpleDispatcher( function(FastRoute\RouteCollector $r) use ($request) { //

    do stuff here } );
  62. 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(); });
  63. ROUTING $routeInfo = $dispatcher->dispatch( $request->getMethod(), $request->getRequestUri() ); if (is_callable($routeInfo[1])) {

    $routeInfo[1](); }
  64. 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(); });
  65. ROUTING if (is_callable($routeInfo[1])) { $params = ($routeInfo[2]) ?? []; $routeInfo[1]($params);

    }
  66. None
  67. TEMPLATING

  68. TEMPLATING > zetacomponents/template

  69. TEMPLATING > zetacomponents/template > eden/template

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

  71. TEMPLATING composer require twig/twig

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

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

  74. 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(); });
  75. TEMPLATING hello.twig Hello {{ name }} goodbye.twig Goodbye {{ name

    }}
  76. HANDLING CONFIGURATION

  77. HANDLING CONFIGURATION > symfony/config

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

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

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

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

    $response = new Response( $twig->render('contact.twig', []) ); $response->send(); } );
  82. 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>
  83. HANDLING CONFIGURATION $r->addRoute( 'POST', '/contact', function() use ($request, $twig) {

    $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(); } );
  84. HANDLING CONFIGURATION composer require werx/config

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

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

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

    );
  88. SENDING E-MAIL

  89. SENDING E-MAIL > nette/mail

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

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

  92. SENDING E-MAIL composer require swiftmailer/swiftmailer

  93. 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);
  94. 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);
  95. DOING VALIDATION

  96. DOING VALIDATION > symfony/validator

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

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

  99. DOING VALIDATION composer require particle/validator

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

    ->required('email') ->email(); $contactMailValidator ->required('message') ->string();
  101. 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(), ])); });
  102. SUMMARY

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

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

    > Leverage the power of open source components and composer
  105. 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
  106. SOME OTHER COMPONENTS YOU COULD THINK OF

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

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

    symfony/dependency-injection, league/container) > Console (symfony/console, illuminate/console, webmozart/console)
  109. 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)
  110. 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)
  111. None
  112. PHOTO CREDITS

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

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

    Constructicon icons: James Peng
  115. THANK YOU! QUESTIONS? HTTP://LEFTONTHEWEB.COM @SKOOP HTTPS://JOIND.IN/TALK/79FDE