$30 off During Our Annual Pro Sale. View Details »

PHPでもserverless framework!?

Kaz Watanabe
October 09, 2017

PHPでもserverless framework!?

Kaz Watanabe

October 09, 2017
Tweet

More Decks by Kaz Watanabe

Other Decks in Technology

Transcript

  1. 1)1Ͱ΋TFSWFSMFTT
    GSBNFXPSL
    1)1ΧϯϑΝϨϯε50,:0
    ߹ಉձࣾEFDS୅දࣾһ
    ౉ลҰ޺ !LB[@

    View Slide

  2. WHO!?
    ౉ลҰ޺(@kaz_29)
    ߹ಉձࣾ decr ୅දࣾһ
    WebΞϓϦέʔγϣϯͷ։ൃ
    iOSΞϓϦέʔγϣϯͷ։ൃ
    Ϋϥ΢υΠϯϑϥͷߏஙɾӡ༻

    View Slide

  3. ຊ೔ͷ಺༰
    w αʔόʔϨεΞʔΩςΫνϟʔ
    w "[VSF'VODUJPOTͷ֓ཁ
    w 1)1Ͱ"[VSF'VODUJPOT
    w 4FSWFSMFTT'SBNFXPSL

    View Slide

  4. αʔόʔϨεΞʔΩςΫνϟʔ

    View Slide

  5. View Slide

  6. View Slide

  7. r4FSWFSMFTT$POG50,:0

    IUUQUPLZPTFSWFSMFTTDPOGJP
    lαʔόʔϨεΞʔΩςΫνϟ͸ɺ։ൃऀͷ૑଄ੑΛ
    ૉૣ͘8FC΍ϞόΠϧΞϓϦέʔγϣϯͱ࣮ͯ͠ݱ
    ͠ɺεέʔϥϏϦςΟ΍ηΩϡϦςΟɺΠϯϑϥͷอ
    कͱ͍ͬͨଟ਺ͷྗ࢓ࣄ͔Βղ์͞ΕΔͨΊͷ৽ͨͳ
    ύϥμΠϜγϑτͰ͢ɻz

    View Slide

  8. αʔόʔ؀ڥͷมԽ
    w Ϋϥ΢υͷొ৔Ͱॊೈʹੜ੒ɾഁغ͕Ͱ͖Δ༷ʹͳͬͨɻ
    w ελϯυΞϩϯΞϓϦ͔Βͷ୤٫ϚΠΫϩαʔϏεͷོ੝
    w αʔόʔ͕ͳ͘ͳΔͱ͍͏͜ͱͰ͸ͳ͍ɻ։ൃऀ͕αʔόʔʹ͍ͭͯ୔ࢁͷ
    ͜ͱΛߟ͑ͳͯ͘ྑ͘ͳΔͱ͍͏͜ͱɻ
    IUUQTSFBEXSJUFDPNXIZUIFGVUVSFPGTPGUXBSFBOEBQQTJTTFSWFSMFTT

    View Slide

  9. αʔόʔϨεͱ͸ʁ
    w αʔόʔΛҙࣝ͠ͳ͍Ͱྑ͍
    w ؆୯ʹεέʔϧͰ͖Δ
    w ར༻ྔʹΑΔ՝ۚ

    View Slide

  10. Α͘ޠΒΕΔϢʔεέʔε
    w 8FCΞϓϦέʔγϣϯ
    w ϞόΠϧόοΫΤϯυ
    w *P5όοΫΤϯυ
    w ετϦʔϜϓϩηογϯά΍&5-
    w νϟοτϘοτɺίάχςΟϒαʔϏε
    w όονॲཧ
    w ӡ༻ͷࣗಈԽ

    View Slide

  11. ࣄྫ೔ܦ৽ฉ
    IUUQTBXTBNB[PODPNKQTPMVUJPOTDBTFTUVEJFTOJLLFJ

    View Slide

  12. View Slide

  13. ࣄྫGBVMUMJOF
    IUUQLMPXIBUFOBCMPHDPNFOUSZ
    1)1ΧϯϑΝϨϯε෱ԬͰGBVMUMJOFʹ͍ͭͯൃද͖ͯ͠·ͨ͠QIQDPOGVL

    View Slide

  14. Α͘ޠΒΕΔϢʔεέʔε
    w 8FCΞϓϦέʔγϣϯ
    w ϞόΠϧόοΫΤϯυ
    w *P5όοΫΤϯυ
    w ετϦʔϜϓϩηογϯά΍&5-
    w νϟοτϘοτɺίάχςΟϒαʔϏε
    w όονॲཧ
    w ӡ༻ͷࣗಈԽ
    ࠷ॳʹࢼ͢ͷʹΦεεϝʂ

    View Slide

  15. Lambda

    View Slide

  16. rIUUQEPDTBXTBNB[PODPNKB@KQMBNCEBMBUFTUEHXFMDPNFIUNM
    lඞཁͳૢ࡞͸ɺ"84-BNCEB͕αϙʔτ͢Δ͍ͣ
    Ε͔ͷݴޠ ݱࡏ͸/PEFKTɺ+BWBɺ$͓Αͼ
    1ZUIPO
    ͰίʔυΛࢦఆ͢Δ͚ͩͰ͢ɻ
    z

    View Slide

  17. lඞཁͳૢ࡞͸ɺ"84-BNCEB͕αϙʔτ͢Δ͍ͣ
    Ε͔ͷݴޠ ݱࡏ͸/PEFKTɺ+BWBɺ
    $͓Αͼ1ZUIPO
    ͰίʔυΛࢦఆ͢Δͩ
    ͚Ͱ͢ɻ
    z
    "84-BNCEB
    rIUUQEPDTBXTBNB[PODPNKB@KQMBNCEBMBUFTUEHXFMDPNFIUNM
    ͸ͳ͍

    View Slide

  18. "[VSF'VODUJPOT

    View Slide

  19. rIUUQTEPDTNJDSPTPGUDPNKBKQB[VSFB[VSFGVODUJPOTGVODUJPOTPWFSWJFX
    l$ɺ'ɺ/PEFKTɺ1ZUIPOɺ1)1ɺ#BUDIɺ
    #BTIɺͦͷଞ࣮ߦՄೳͳݴޠΛ࢖ͬͯؔ਺Λهड़Ͱ
    ͖·͢ɻz
    Ͱॻ͚Δ

    View Slide

  20. "[VSF'VODUJPO

    View Slide

  21. "[VSF'VODUJPOT
    w ΊͬͪΌ͍҆
    w ೖྗɾग़ྗͷ࿈ܞػೳ͕๛෋ τϦΨʔɾόΠϯσΟϯά

    w 7474$PEFΛ࢖ͬͯͷσόοά ϩʔΧϧɾϦϞʔτʣ
    w ΦϯϓϨͰ΋͍͚Δ "[VSF4UBDL

    w ϥϯλΠϜ͕Φʔϓϯ IUUQTHJUIVCDPN"[VSF"[VSF'VODUJPOT

    w (JUIVC͔ΒσϓϩΠͰ͖Δ

    View Slide

  22. τϦΨʔͱόΠϯσΟϯά
    IUUQTEPDTNJDSPTPGUDPNKBKQB[VSFB[VSFGVODUJPOTGVODUJPOTUSJHHFSTCJOEJOHT

    View Slide

  23. -PHJD"QQT

    View Slide

  24. -PHJD"QQ

    View Slide

  25. .JDSPTPGU'MPX
    IUUQTqPXNJDSPTPGUDPN

    View Slide

  26. "[VSF'VODUJPOͷػೳ

    View Slide

  27. "[VSF'VODUJPO

    View Slide

  28. Ϟχλʔ

    View Slide

  29. 'VODUJPO1SPYZ QSFWJFX

    w ෳ਺ͷ'VODUJPOΛ౷߹Ͱ͖Δ
    w "1*.BOBHFNFOUͷ୅ΘΓʹ࢖͑Δ
    w 'VODUJPOҎ֎΁ͷϦΫΤετ΋ઃఆͰ͖Δ
    "[VSF"1*NBOBHFNFOU
    "[VSF'VODUJPOT
    "[VSF'VODUJPOT1SPYZ
    "[VSF'VODUJPOT
    ͪΐͬͱ
    ͓ߴ͍

    View Slide

  30. σϓϩΠϝϯτεϩοτ QSFWJFX

    #MVF(SFFO%FQMPZNFOU͕؆୯ʹͰ͖Δ

    View Slide

  31. $POTPMF

    View Slide

  32. "QQMJDBUJPO*OTJUF QSFWJFX

    ೖΕΔ͔ʜ
    IUUQTB[VSFNJDSPTPGUDPNKBKQVQEBUFTB[VSFGVODUJPOTOPXJOUFHSBUFEXJUIBQQMJDBUJPOJOTJHIUT

    View Slide

  33. %VSBCMF'VODUJPOT Ћ൛

    w ෳ਺ͷ'VODUJPOͷ࿈ܞΛൺֱత؆୯ʹ࣮૷Ͱ͖Δ
    w 'VODUJPODIBJOJOH
    w 'BOPVU'BOJO
    w "TZOD)551"1*T
    IUUQTB[VSFHJUIVCJPB[VSFGVODUJPOTEVSBCMFFYUFOTJPOJOEFYIUNM
    IUUQRJJUBDPN5TVZPTIJ6TIJP!HJUIVCJUFNTFBDCCCGEC

    View Slide

  34. 'VODUJPO$IBJOJOH
    public static async Task Run(DurableOrchestrationContext ctx)
    {
    try
    {
    var x = await ctx.CallFunctionAsync("F1");
    var y = await ctx.CallFunctionAsync("F2", x);
    var z = await ctx.CallFunctionAsync("F3", y);
    return await ctx.CallFunctionAsync("F4", z);
    }
    catch (Exception)
    {
    // error handling/compensation goes here
    }
    }

    View Slide

  35. public static async Task Run(DurableOrchestrationContext ctx)
    {
    var parallelTasks = new List>();
    // get a list of N work items to process in parallel
    object[] workBatch = await ctx.CallFunctionAsync("F1");
    for (int i = 0; i < workBatch.Length; i++)
    {
    Task task = ctx.CallFunctionAsync("F2", workBatch[i]);
    parallelTasks.Add(task);
    }
    await Task.WhenAll(parallelTasks);
    // aggregate all N outputs and send result to F3
    int sum = parallelTasks.Sum(t => t.Result);
    await ctx.CallFunctionAsync("F3", sum);
    }
    'BOPVU 'BOJO

    View Slide

  36. "TZOD)551"1*T
    public static async Task Run(
    HttpRequestMessage req,
    DurableOrchestrationClient starter,
    string functionName,
    TraceWriter log)
    {
    // Function name comes from the request URL.
    // Function input comes from the request content.
    dynamic eventData = await req.Content.ReadAsAsync();
    string instanceId = await starter.StartNewAsync(functionName, eventData);
    log.Info($"Started orchestration with ID = '{instanceId}'.");
    return starter.CreateCheckStatusResponse(req, instanceId);
    }

    View Slide

  37. -JHIUXFJHIU"DUPST
    public static async Task Run(DurableOrchestrationContext ctx)
    {
    int counterState = ctx.GetInput();
    string operation = await ctx.WaitForExternalEvent("operation");
    if (operation == "incr")
    {
    counterState++;
    }
    else if (operation == "decr")
    {
    counterState--;
    }
    ctx.ContinueAsNew(counterState);
    }

    View Slide

  38. )VNBO*OUFSBDUJPOBOE5JNFPVUT
    public static async Task Run(DurableOrchestrationContext ctx)
    {
    await ctx.CallFunctionAsync("RequestApproval");
    using (var timeoutCts = new CancellationTokenSource())
    {
    DateTime dueTime = ctx.CurrentUtcDateTime.AddHours(72);
    Task durableTimeout = ctx.CreateTimer(dueTime, timeoutCts.Token);
    Task approvalEvent = ctx.WaitForExternalEvent("ApprovalEvent");
    if (approvalEvent == await Task.WhenAny(approvalEvent, durableTimeout))
    {
    timeoutCts.Cancel();
    await ctx.CallFunctionAsync("HandleApproval", approvalEvent.Result);
    }
    else
    {
    await ctx.CallFunctionAsync("Escalate");
    }
    }
    }

    View Slide

  39. -PDBM։ൃ؀ڥ
    w 7JTVBM4UVEJP74$PEFΛ࢖ͬͯϩʔΧϧ։ൃ͕Մೳ
    w 7FSTJPO /&5'SBNFXPSLΛར༻
    ͔Β͸ɺ8JOEPXT
    Ҏ֎΋αϙʔτ

    View Slide

  40. -PDBM։ൃ؀ڥ
    IUUQLB[IBUFOBCMPHDPNFOUSZ

    View Slide

  41. 1)1Ͱ"[VSF'VODUJPOT

    View Slide

  42. όΠϯσΟϯάͷ࢖͍ํ
    // ೖྗόΠϯσΟϯάͷಡࠐΈ
    $data = file_get_contents(getenv(‘req’));
    // ग़ྗόΠϯσΟϯά΁ͷॻࠐΈ
    file_put_contents(getenv(‘res’), $data);
    var_dump(getenv('res'));
    string(72) “D:
    \local\Temp\Functions\Binding\[UUID]\res”
    ੲջ͔͍͠$(*෩

    View Slide

  43. "[VSF1PSUBMͰ࡞੒

    View Slide

  44. 1PSUBMͰ௚઀
    1SPEVDUJPO
    ίʔυमਖ਼⁉

    View Slide

  45. View Slide

  46. 6OJU5FTUͷ
    αϯϓϧ

    View Slide

  47. 0VUHPJOH8FCIPPLT IUUQ5SJHHFS
    IUUQPVUCJOEJOH

    View Slide

  48. 'VODUJPOͷߏ੒

    ᵓᴷᴷIPTUKTPO
    ᵓᴷᴷGVOD
    ᴹᵓᴷᴷGVODUJPOKTPO
    ᴹᵋᴷᴷSVOQIQ
    ᵋᴷᴷGVOD
    ᵓᴷᴷGVODUJPOKTPO
    ᵋᴷᴷSVOQIQ
    EJSFDUPSJFT pMFT
    'VODUJPOຖʹσΟϨΫτϦ
    τϦΨʔɾόΠϯσΟϯάͳͲͷఆٛ
    'VODUJPOͷΤϯτϦϙΠϯτ
    ϥϯλΠϜݻ༗ͷߏ੒
    IUUQTEPDTNJDSPTPGUDPNKBKQB[VSFB[VSFGVODUJPOTGVODUJPOTUSJHHFSTCJOEJOHT

    View Slide

  49. αϯϓϧ'VODUJPOͷߏ੒
    .
    ├── composer.json
    ├── composer.lock
    ├── slackbot
    │ ├── function.json
    │ └── run.php
    ├── src
    │ ├── Functions
    │ │ └── SlackBot.php
    │ └── Lib
    │ └── AzureFunction.php
    ├── tests
    │ ├── Lib
    │ │ └── TestCase.php
    │ └── TestCase
    │ └── Functions
    │ └── SlackBotTest.php
    ├── tmp
    └── vendor
    ├── autoload.php

    'VODUJPOͷσΟϨΫτϦ
    τϦΨʔɾόΠϯσΟϯάͳͲͷఆٛ
    'VODUJPOͷΤϯτϦϙΠϯτ
    'VODUJPOຊମ
    ؆୯ͳϥούʔΫϥε
    ςετΫϥε
    IUUQTHJUIVCDPNLB[QIQDPOLBOTBJB[VSFGVODUJPO

    View Slide

  50. GVODUJPOKTPO
    {
    "bindings": [
    {
    "type": "http",
    "direction": "out",
    "name": "res"
    },
    {
    "type": "httpTrigger",
    "name": "req",
    "authLevel": "function",
    "methods": [
    "post"
    ],
    "direction": "in"
    }
    ],
    "disabled": false
    }

    View Slide

  51. SVOQIQ
    require dirname(__DIR__) .
    DIRECTORY_SEPARATOR . 'vendor' .
    DIRECTORY_SEPARATOR . 'autoload.php';
    use App\Functions\SlackBot;
    $proc = new SlackBot();
    $proc->run();

    View Slide

  52. 4MBDL#PUQIQ
    namespace App\Functions;
    use App\Lib\AzureFunction;
    class SlackBot extends AzureFunction
    {
    public function __construct($req = 'req', $res = 'res')
    {
    $this->init(getenv($req), getenv($res));
    }

    }

    View Slide

  53. 4MBDL#PUQIQ
    public function run()
    {
    $params = $this->httpParams();
    if (!isset($params['user_name']) ||
    $params['user_name'] === 'slackbot') {
    return;
    }
    $response = [
    'text' => '',
    'username' => 'phpconkansai2017demo',
    'icon_emoji' => ':phpkansai:',
    'attachments' => [[
    "color" => '#00BFFF', // blue
    "title" => 'ϝοηʔδΛड৴͠·ͨ͠',
    "fields" => [[

    ]]
    ]]
    ];
    $this->writeResponse(json_encode($response));
    }

    View Slide

  54. 4-BDL#PU5FTUQIQ
    /**
    * @test
    */
    public function ௨ৗͷॻ͖ࠐΈͷςετ()
    {
    $params = [
    'token' => 'DUMMY',
    'user_name' => 'userA',
    'text' => 'Hello Azure Function'
    ];
    $this->makeHttpRequest($params);
    $this->makeResponse();

    View Slide

  55. 4-BDL#PU5FTUQIQ
    $this->slackBot = new SlackBot();
    $this->slackBot->run();
    $result = json_decode($this->getResponse(),
    true);
    $expected = [
    'username' => 'phpconkansai2017demo',
    'icon_emoji' => ':phpkansai:',
    'text' => '',
    'attachments' => [[
    "color" => '#00BFFF',
    "title" => 'ϝοηʔδΛड৴͠·ͨ͠',
    "fields" => [[
    // লུ
    ]]
    ]]
    ];
    $this->assertEquals($expected, $result);
    }

    View Slide

  56. 1)16OJUͰςετ

    View Slide

  57. 4FSWFSMFTT'SBNFXPSL

    View Slide

  58. IUUQTTFSWFSMFTTDPNGSBNFXPSL

    View Slide

  59. rIUUQTTFSWFSMFTTDPNGSBNFXPSL
    l4FSWFSMFTT'SBNFXPSLJTZPVSTJOHMFUPPMLJU
    GPSEFQMPZJOHTFSWFSMFTTBSDIJUFDUVSFTUP
    BOZQSPWJEFS:PVCVJMEUIFGFBUVSFT XF
    DPOpHVSFUIFJOGSBTUSVDUVSF%POFz

    View Slide

  60. rIUUQTTFSWFSMFTTDPNGSBNFXPSL
    l4FSWFSMFTT'SBNFXPSLJTZPVSTJOHMFUPPMLJU
    GPSEFQMPZJOHTFSWFSMFTTBSDIJUFDUVSFTUP
    BOZQSPWJEFS:PVCVJMEUIFGFBUVSFT XF
    DPOpHVSFUIFJOGSBTUSVDUVSF%POFz

    View Slide

  61. 4FSWFSMFTT'SBNFXPSL
    w OPEFKT੡
    w "84-BNCEB
    w "[VSF'VODUJPOTରԠ
    w ͨͩ͠OPEFKTͷΈʜ

    View Slide

  62. "[VSFͰࢼͯ͠ΈΔ
    ϑΝΠϧΛݻΊΔ
    "[VSFʹϩάΠϯ
    GVODUJPO࡞Δ
    CJOEJOHͷઃఆ
    GVODUJPOΛݻΊΔ
    VQMPBE
    QBDLBHFKTPOΛ্͛Δ
    OQNJOTUBMMΛ࣮ߦ

    View Slide

  63. View Slide

  64. "[VSFͰࢼͯ͠ΈΔ
    ϑΝΠϧΛݻΊΔ
    "[VSFʹϩάΠϯ
    GVODUJPO࡞Δ
    CJOEJOHͷઃఆ
    GVODUJPOΛݻΊΔ
    VQMPBE
    QBDLBHFKTPOΛ্͛Δ
    OQNJOTUBMMΛ࣮ߦ
    ͜ΕΛʜ

    View Slide

  65. "[VSFͰࢼͯ͠ΈΔ
    ϑΝΠϧΛݻΊΔ
    "[VSFʹϩάΠϯ
    GVODUJPO࡞Δ
    CJOEJOHͷઃఆ
    GVODUJPOΛݻΊΔ
    VQMPBE
    QBDLBHFKTPOΛ্͛Δ
    OQNJOTUBMMΛ࣮ߦ
    DPNQPTFSKTPOΛ্͛Δ
    DPNQPTFSJOTUBMMΛ࣮ߦ
    ͜͏͢Δʜ

    View Slide

  66. Ͱ͖ͨ‼

    View Slide

  67. Ͱ͖ͨ‼
    DVSM)$POUFOU5ZQFBQQMJDBUJPOKTPOE\aOBNFaa,B[a^IUUQTTMT
    QIQFYBNQMFB[VSFXFCTJUFTOFUBQJIUUQQIQ
    )FMMP,B[

    View Slide

  68. Ͱ͖ͨ‼
    TMTMPHTGIUUQQIQ

    View Slide

  69. 1)1Ͱ΋
    4FSWFSMFTT'SBNFXPSL
    ࢖͑ͦ͏
    IUUQTHJUIVCDPNLB[TFSWFSMFTTB[VSFQIQFYBNQMF

    View Slide

  70. ՝୊
    w ࠷৽ͷTFSWFSMFTTB[VSFGVODUJPOTΛऔΓࠐΉ
    w B[VSFQIQςϯϓϨʔτΛ࡞Δ
    w QSPYZ΍TMPUͷૢ࡞ΛTMT͔Β΍Γ͍ͨ

    View Slide

  71. "[VSF'VODUJPO݁ߏ͍͍Ͱ͢
    IUUQTGVODUJPOTB[VSFDPNUSZ

    View Slide

  72. ͓ΘΓ

    View Slide