Your next Web server will be written in... PHP

D6ccd6409910643d05ddaea3b2cd6f13?s=47 David Zuelke
February 16, 2017

Your next Web server will be written in... PHP

Talk presented at PHP UK Conference 2017 in London, England.

D6ccd6409910643d05ddaea3b2cd6f13?s=128

David Zuelke

February 16, 2017
Tweet

Transcript

  1. 1.

    YOUR NEXT WEB SERVER WILL BE WRITTEN IN... PHP MIGHT

    PHP UK Conference 2017 London, England
  2. 3.
  3. 6.

    CGI

  4. 8.
  5. 10.
  6. 12.
  7. 13.
  8. 14.

    HOW CGI WORKS 1. Web server parses request from client

    2. Web server sets request info into environment
 (PATH_INFO, REQUEST_METHOD, HTTP_ACCEPT, …) 3. Web server executes CGI script 4. CGI script echos status, headers, and body 5. Web server returns response to client
  9. 15.
  10. 17.

    #!/bin/bash cat <<EOF Content-Type: text/html <html> <head> <title>Hello World</title> </head>

    <body> <h1>Hello World</h1> <p>Your browser is ${HTTP_USER_AGENT:-unknown}</p> <p>This page was served by $(hostname)</p> </body> </html> EOF
  11. 18.

    CGI VARIABLES (SEE $_SERVER) • Server info:
 SERVER_SOFTWARE, SERVER_NAME, GATEWAY_INTERFACE

    • Request info:
 SERVER_PROTOCOL, SERVER_PORT, REQUEST_METHOD, PATH_INFO, PATH_TRANSLATED, QUERY_STRING, REMOTE_ADDR, CONTENT_TYPE, CONTENT_LENGTH, … • Request headers:
 HTTP_HOST, HTTP_ACCEPT, HTTP_USER_AGENT, …
  12. 22.

    PHP WEB SAPI (SIMPLIFIED) • populate $_SERVER and $_ENV •

    parse QUERY_STRING into $_GET • " application/x-www-form-urlencoded into $_POST • " multipart/form-data into $_FILES • return header() data as headers, anything echo()d as content
  13. 25.

    PHP CGI EXECUTION LIFECYCLE
 (SIMPLIFIED) 1. core init, load extensions

    etc 2. MINIT for all modules (extension initialization etc) 3. SAPI ready for (one and only) request 4. RINIT for all modules (e.g. ext-session if session.auto_start) 5. script executes 6. RSHUTDOWN 7. engine cleanup (unset all variables and state) 8. MSHUTDOWN
  14. 28.
  15. 30.

    mod_php EXECUTION LIFECYCLE
 (SIMPLIFIED) 1. SAPI ready for (next) request

    2. RINIT for all modules (e.g. ext-session if session.auto_start) 3. script executes 4. RSHUTDOWN 5. engine cleanup (unset all variables and state)
  16. 33.

    APACHE PROCESS MODELS • mpm_prefork creates worker processes
 (each with

    a PHP) • mpm_worker uses threads
 (so you need ZTS, and stuff will generally crash a lot) • mpm_event uses an event loop
 (best, but you can't embed something like PHP at all)
  17. 37.
  18. 48.

    RUBY/RACK app = Proc.new do |env| ['200', {'Content-Type' => 'text/plain'},

    ['Hello World']] end def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) yield 'Hello World\n' PYTHON/WSGI
  19. 49.

    THE RACK/WSGI STACKS 1. Web server:
 Unicorn, Gunicorn, Puma, Tornado,

    Phusion Passenger, mod_wsgi, ... 2. Middlewares:
 Routing, authentication, filtering, post-processing, ... 3. Application/framework:
 Rails, Django, Sinatra, Flask, ...
  20. 51.

    PHP IS NOW READY • PHP 7+ performance is amazing

    • Almost all engine errors are catchable since PHP 7 • Signal handling without ticks in PHP 7.1 • Concurrency frameworks and event lib extensions
  21. 53.

    IT'S ALL A REACTOR echo "-- before run()\n"; Amp\run(function() {

    Amp\repeat(function() { echo "tick\n"; }, $msInterval = 1000); Amp\once("Amp\stop", $msDelay = 5000); }); echo "-- after run()\n";
  22. 54.

    SIMPLE WEB SERVER, ReactPHP $app = function ($request, $response) {

    $response->writeHead(200, array('Content-Type' => 'text/plain')); $response->end("Hello World\n"); }; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $http = new React\Http\Server($socket, $loop); $http->on('request', $app); $socket->listen(1337); $loop->run();
  23. 56.

    $kernel = new AppKernel('prod', false); $reactHttp->on('request', function ($request, $response) use

    ($kernel) { $headers = $request->getHeaders(); if (in_array(strtoupper($request->getMethod()), ['POST','PUT','DELETE','PATCH']) && isset($headers['Content-Type']) && (0 === strpos($headers['Content-Type'], 'application/x-www-form-urlencoded')) ) parse_str($request->getBody(), $post); $sfRequest = new Symfony\Component\HttpFoundation\Request( $request->getQuery(), $post ?? [], array(), array(), $request->getFiles(), array(), $request->getBody() ); $sfRequest->setMethod($request->getMethod()); $sfRequest->headers->replace($headers); $sfRequest->server->set('REQUEST_URI', $request->getPath()); if (isset($headers['Host'])) $sfRequest->server->set('SERVER_NAME', explode(':', $headers['Host'])[0]); $sfResponse = $kernel->handle($sfRequest); $response->writeHead($sfResponse->getStatusCode(), $sfResponse->headers->all()); $response->end($sfResponse->getContent()); $kernel->terminate($request, $response); });
  24. 72.

    READING MATERIAL • http://blog.kelunik.com/2015/09/20/getting-started-with-amp.html • http://blog.kelunik.com/2015/10/21/getting-started-with-aerys.html • http://blog.kelunik.com/2015/10/20/getting-started-with-aerys- websockets.html •

    http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html • http://marcjschmidt.de/blog/2016/04/16/php-high-performance-reactphp- jarves-symfony-follow-up.html • https://gnugat.github.io/2016/04/13/super-speed-sf-react-php.html
  25. 73.