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

大統一PHP

uzulla
March 09, 2018

 大統一PHP

PHPerKaigi 2018 前夜祭🍻
2018/03/09 at coconeri

uzulla

March 09, 2018
Tweet

More Decks by uzulla

Other Decks in Programming

Transcript

  1. 1, PHPͰΞϓϦΛಈ͔͢ʮ؀ڥʯ͕৭ʑ͋Γ͗͢Δ • Apache(+mod_php) • Apache with php-cgi • nginx

    with php-fpm+php-cgi(FastCGI) • IIS with php-cgi(FastCGIɺPM͸IIS͕ߦ͏) • h2o with php-cgi(FastCGIɺPM͸h2o͕ߦ͏ʣ • PHP on GAE(ṖͷPHP) • HHVMʢ୯ମͰhttpdʹͳΔʣ
  2. 2, PHPΛͲ͔͜Β΋ͬͯ͘Δʁ • yum,apt (Linux • installer (Windows • Brew

    (Mac • php-build (Linux,Mac • ໺ྑϏϧυ (tar ball͔ΒϏϧυ • ࣗՈrpm
  3. ී௨͸ʢʁʣ • ʮcpanm -lͰϥΠϒϥϦͱಉ༷ʹϩʔΧϧʹೖΕΔʯ • ʮbundlerͰϥΠϒϥϦͱಉ༷ʹϩʔΧϧʹೖΕΔʯ • ຋༁ɿʢcomposer install͢ΔͱɺvendorͷԼʹC֦ு͕ ೖΔʣ

    • ※ ·͋͜Ε͸ඞͣͦ͏Ͱ͸ͳ͍͕ɺPHPͩͱ໰౴ແ༻Ͱάϩʔ όϧʹೖΔΑͶ • ʮpeclͬͯͳʹʁcomposerͰ͸͍Βͳ͍ͷʁΉ͔͍ͣ͠ʂʯ
  4. ී௨ͷslim <?php require "vendor/autoload.php"; $container = new \Slim\Container; $app =

    new \Slim\App($container); $app->get('/', function ($request, $response) { return $response->write("hello!!"); }); $app->run();
  5. <?php require "vendor/autoload.php"; $loop = React\EventLoop\Factory::create(); $server = new \React\Http\Server(array(

    function (\Psr\Http\Message\ServerRequestInterface $request) { $container = new \Slim\Container; $app = new \Slim\App($container); // !slimͷrequestΛɺreact͕࡞ͬͨ΋ͷʹࠩ͠ସ͑ $container['request'] = function ($container) use ($request) { return $request; }; // ಛʹมΘΒͣ $app->get('/', function ($request, $response) { return $response->write("hello!!"); }); // !Ҿ਺ʹtrueΛ͍ΕΔͱɺresponse ͕ؼ͖ͬͯ·͢ͷͰɺͦΕΛreact΁ return $app->run(true); } )); $socket = new React\Socket\Server(8888, $loop); $server->listen($socket); $loop->run();
  6. $server = new Server(array( // ΤϥʔϋϯυϦϯά ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ function (ServerRequestInterface $request,

    callable $next) { return $next($request); /* ུ */ }, // ੩తϑΝΠϧ഑৴ ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ new WebrootPreloadMiddleware(__DIR__."/htdocs"), // ηογϣϯ ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ new SessionMiddleware(/* ུ */), // csrfରࡦ ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ function (ServerRequestInterface $request, callable $next) { /* ུ */ return $next($request); }, // ΢ΣϒΞϓϦ ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ function (ServerRequestInterface $request) { $app = new \Slim\App($container); /* ུ */ return $app->run(true); // ΢Σϒຊମ࣮ߦɺ͜͜Ͱ͸next͕ͳ͍ } ));
  7. $server = new Server([ // ഑ྻͰϛυϧ΢ΣΞΛొ࿥͢Δ // !ΤϥʔϋϯυϦϯάϛυϧ΢ΣΞ ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ function

    ($request, $next) { // " req(Πϯελϯε)ͱnext(ؔ਺)ΛऔΔ return $next($request); // " ࣍΁ /* ུ */ }, // !੩తϑΝΠϧ഑৴ϛυϧ΢ΣΞ ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ new WebrootPreloadMiddleware(__DIR__."/htdocs"), // " $this->__invoke() // !΢ΣϒΞϓϦʢϛυϧ΢ΣΞʣ ʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹʹ function ($request) { // ͜͜͸࣍(next)͕ͳ͍ $app = new \Slim\App($container); /* ུ */ return $app->run(true); // " ΢ΣϒΞϓϦຊମ࣮ߦɺres(Πϯελϯε)Λreturn } ]);
  8. function (ServerRequestInterface $request, callable $next) { $promise = new React\Promise\Promise(

    function ($resolve) use ($next, $request) { $resolve($next($request)); } ); // ྫ֎͕͕͋ͬͨΒɺ!͕࣮ߦ͞ΕΔ return $promise->then(null, function (Throwable $e) { // Throwable͕ྑ͍ͱࢥ͏ error_log("ERROR:" . $e->getMessage()); error_log($e->getTraceAsString()); return new Response( 500, array(), 'Internal Server error' ); }); },
  9. // ηογϣϯ new \WyriHaximus\React\Http\Middleware\SessionMiddleware( 'MySessionCookie', $cache, // Instance implementing React\Cache\CacheInterface

    [ // Optional array with cookie settings, order matters 0, // expiresAt, int, default '', // path, string, default '', // domain, string, default false, // secure, bool, default false // httpOnly, bool, default ] ),
  10. function (ServerRequestInterface $request, callable $next) { // read session $session_obj

    = $request->getAttribute(SessionMiddleware::ATTRIBUTE_NAME); $session = $session_obj->getContents(); if(!isset($session['csrf_token'])) { $session_obj->setContents(['csrf_token' => random_bytes(64)]); } $csrf_token = $session_obj->getContents()['csrf_token']; // POSTͳΒcheck token if(strtolower($request->getMethod()) === "post"){ $params = $request->getParsedBody(); if(!isset($params['csrf_token']) || $params['csrf_token'] !== $csrf_token){ error_log("invalid csrf_token"); return new Response(400, [], "invalid csrf_token"); } } // attributeʹtokenΛอ࣋ $request = $request->withAttribute('csrf_token', $csrf_token); return $next($request); },
  11. // ίϯςφʹcsrf_tokenΛอ࣋͠ɺTwigϔϧύʹ౉͢ $csrf_token = $request->getAttribute('csrf_token'); $container['csrf_token'] = function ($c) use

    ($csrf_token){ return $csrf_token; }; $container->view->addExtension(new CsrfExtension($csrf_token));
  12. <?php // TwigΤΫεςϯγϣϯྫ class CsrfExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface {

    protected $csrf; public function __construct($token) { $this->csrf = $token; } public function getGlobals() { return ['csrf' => ['value' => $this->csrf] ]; } public function getName() { return 'my/csrf'; } }
  13. <?php require "vendor/autoload.php"; require __DIR__ . '/config.php'; use \Psr\Http\Message\ServerRequestInterface; use

    \React\Http\Server; use \React\Http\Response; $loop = React\EventLoop\Factory::create(); $cache = new \React\Cache\ArrayCache(); $server = new Server(array( // ΤϥʔϋϯυϦϯά function (ServerRequestInterface $request, callable $next) { $promise = new React\Promise\Promise(function ($resolve) use ($next, $request) { $resolve($next($request)); }); return $promise->then(null, function (Throwable $e) { error_log("ERROR:" . $e->getMessage()); error_log($e->getTraceAsString()); return new Response( 500, array(), 'Internal Server error' ); }); }, // ੩తϑΝΠϧ഑৴ new \WyriHaximus\React\Http\Middleware\WebrootPreloadMiddleware(__DIR__."/htdocs"), // ηογϣϯ new \WyriHaximus\React\Http\Middleware\SessionMiddleware( 'MySessionCookie', $cache, // Instance implementing React\Cache\CacheInterface [ // Optional array with cookie settings, order matters 0, // expiresAt, int, default '', // path, string, default '', // domain, string, default false, // secure, bool, default false // httpOnly, bool, default ] ), // csrfରࡦ function (ServerRequestInterface $request, callable $next) { // read session $session_obj = $request->getAttribute(\WyriHaximus\React\Http\Middleware\SessionMiddleware::ATTRIBUTE_NAME); $session = $session_obj->getContents(); if(!isset($session['csrf_token'])) { $csrf_token = base64_encode(random_bytes(64)); $session_obj->setContents([ 'csrf_token' => $csrf_token, ]); }else{ $csrf_token = $session['csrf_token']; } // check token if(strtolower($request->getMethod()) === "post"){ $params = $request->getParsedBody(); if(!isset($params['csrf_token']) || $params['csrf_token'] !== $csrf_token){ error_log("invalid csrf_token"); return new Response(400, [], "invalid csrf_token"); } } $request = $request->withAttribute('csrf_token', $csrf_token); return $next($request); }, // ΢ΣϒΞϓϦ function (ServerRequestInterface $request) { // SlimॳظԽ $container = new \Slim\Container; $app = new \Slim\App($container); // reactͷrequestΛಥͬࠐΉ $container['request'] = function ($container) use ($request) { return $request; }; // Register Twig View helper $container['view'] = function ($c) { $view = new \Slim\Views\Twig(TEMPLATES_DIR_PATH, []); $view->addExtension(new \Slim\Views\TwigExtension($c['router'], '/')); return $view; }; // csrf_tokenΛอ࣋͠ɺTwigϔϧύʹ౉͢ $csrf_token = $request->getAttribute('csrf_token'); $container['csrf_token'] = function ($c) use ($csrf_token){ return $csrf_token; }; $container->view->addExtension(new \Tinitter\Misc\TwigExt\CsrfExtension($csrf_token)); // SlimʹϧʔτΛొ࿥ \Tinitter\Route::registration($container); // ࣮ߦ return $app->run(true); } )); $socket = new React\Socket\Server(8888, $loop); $server->listen($socket); $loop->run();
  14. • max_execution_time, memory_limitͰอޢ͕Ͱ͖ͳ͍ • ʢ౰ͨΓલ͕ͩʣൿ఻ͷhtaccess͸͔ͭ͑·ͤΜ͆ • ࣗ࡞ͨ͠ΓɺલஈͷhttpdͰ͔͚͹ྑ͍ • ҆શੑ΍ߴ଎ԽΛߟ͑ΔͱPDOར༻ͨΊΒ͏ •

    react΍amphp͕ఏڙ͢Δ΋ͷΛ͔ͭ͏ • ʮmysql͸͋ΔͬΆ͍͚ͲϙεάϨ͸ʁʯʮ…redisͱ͔͸ ͋ΔΑʯʮ΋͏͓ͬͨͧ͜͏ʂʂʯ(ຊՈͰͳ͚Ε͹͋Δࣄ ΋͋Δ)
  15. • Static file serving • WebSockets • Dynamic app endpoint

    routing • Name-based virtual hosting • Full TLS support • Customizable GZIP output compression • HTTP/2.0 support (pushͳͲ) • Middleware hooks
  16. • άϨʔεϑϧϦελʔτ • ϓϩηεϚωʔδϟʢϫʔΧ਺มߋͳͲʣ • Unix domain sock • ࣮ߦϢʔβʔมߋ

    • Ϧόϓϩ • ηογϣϯʢRedisʣɺCookie • ͔ͳΓϓϩμΫγϣϯϨσΟײ͋ΔʢݸਓͷײͰ͢ʣ
  17. <?php use Aerys\{ Host, Request, Response, Websocket, function root, function

    router, function websocket }; $router = router() ->route("GET", "/", function(Request $req, Response $res) { $res->end("<html><body><h1>Hello, world.</h1></body></html>"); }); $root = root($docrootPath = __DIR__);// static file serving $fallback = function(Request $req, Response $res) { $res->end("<html><body><h1>Fallback \o/</h1></body></html>"); }; return (new Host)->expose("*", 1337)->use($router)->use($root)->use($fallback);
  18. swoole • https://github.com/swoole/swoole-src • จ۟ͳ͠ʢʁʣͷproduction readyʂʢςϯηϯτ͕࣮༻͠ ͍ͯΔʣ • high performance

    for PHP. • த՚+C֦ுύϫʔͰ̎̌̌ສഅྗʢʁʣ • ͪΌΜͱߋ৽͞ΕͯΔʂ͍͢͝ʂʂ
  19. $chan = new Channel(1024 * 256); co::create(function () use ($chan){

    while(1){ co::sleep(0.5); $chan->push("A"); } }); co::create(function () use ($chan) { while(1){ $ret = $chan->pop(); if(!$ret){ co::sleep(0.1); continue; } echo $ret; } });
  20. ͔͠͠ɺੈք͸autoload͚ͩͰ͸ͳ ͍ • ex: ςϯϓϨʔτϑΝΠϧɺίϯϑΟά • (Tinitterͷ৔߹͸sqlite΋) ྫ PHP Warning:

    file_get_contents(public/index.html): failed to open stream: No such file or directory in phar:///tmp/example.phar/bin/main on line 26
  21. ͔͠͠PDO͕͏͔͝ͳ͍ $ php react_tinitter.phar Slim Application Error: Type: PDOException Code:

    14 Message: SQLSTATE[HY000] [14] unable to open database file
  22. ग़མͪʣ΍ͬͨʂେ౷Ұͩʂʂ $ cat /usr/bin/php main.php > one-pack-php $ ./one-pack-php -r

    \ "eval('?>'.substr(file_get_contents('one-pack-php'), strlen(file_get_contents('/usr/bin/php'))));" • ̍ϑΝΠϧʹόΠφϦͱPHP͕͸͍ͬͨͧʂʂ
  23. $ ./one-pack-php \ # ͭͳ͛ͨPHP -r \ # Ҿ਺ͰPHPίʔυࢦఆɺҎԼPHPίʔυ "eval('?>'.

    // '<?php' Ͱ࢝·ΔίʔυΛಡΈࠐ·ͤΔҝ substr( // όΠφϦ෼ɺಡΈඈ͹͢ file_get_contents('one-pack-php'), // ͭͳ͛ͨPHPΛಡΈࠐΉ strlen( // ಡΈඈ͹͢௕͞ΛܾΊΔɺ͜͜͸ຊ౰͸਺஋ʹॻ͖׵͑Δ file_get_contents('/usr/bin/php')) // ඈ͹͢௕͞ ) );" ! [PHPόΠφϦ|PHPίʔυ] ^~~͜͜·ͰεΩοϓͯ͠ɺ ͦΕΛeval͍ͯ͠Δ
  24. #!/bin/bash # head.sh function finish () { cd - rm

    -rf $RUNPACKDIR exit } RUNPACKDIR=`mktemp -d /tmp/runpackphp.XXXXX` trap 'finish' {1,2,3,15} ENDLINE=$(grep --line-number --text "^##ENDLINE$" $0 | sed 's/:.*//') : $((ENDLINE++)) tail -n +$ENDLINE $0 | tar xzf - --directory $RUNPACKDIR cd $RUNPACKDIR $RUNPACKDIR/php $RUNPACKDIR/main.php cd - exit ##ENDLINE
  25. #!/bin/sh # build.sh cd ../ tar czf --exclude-vcs selfbash/ball.tgz \

    htdocs lib templates vendor \ sqlite.db main.php config.php php cd - cat head.sh ball.tgz > selfbash.sh chmod +x selfbash.sh
  26. Φν ʢ7.2.2ͷྫͰ͢ʣ ./configure \ --disable-cgi --without-pear \ --enable-ftp --enable-mbstring --enable-mysqlnd

    \ --with-openssl --with-zlib \ --disable-libxml --disable-dom --disable-xml \ --disable-simplexml --disable-xmlreader --disable-xmlwriter • ެࣜͷDockerFileΛվ଄͢Δͱָ • όΠφϦΛ͵͍ͯɺૉͷ΋ͷʹίϐʔ͔ͯͭ͠͏ • SSL͸࠷ॳ͔Β͋Δɺlibxml͕Ͱ͔͍ΜͩΑͳ͋…
  27. ݁Ռ $ docker image list REPOSITORY TAG IMAGE ID CREATED

    SIZE miniphp latest fa69ea70ab4c 2 weeks ago 11.4MB php-7.2.2-cli-alpine3.7 1.0 0a93147b91ab 2 weeks ago 62.5MB