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

【改訂版】PHP7で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計 / PHP Conference 2016 Revised

【改訂版】PHP7で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計 / PHP Conference 2016 Revised

2016/12/15 @ PHPカンファレンス2016再演
https://saien.connpass.com/event/45318/

Takuto Wada
PRO

December 15, 2016
Tweet

More Decks by Takuto Wada

Other Decks in Programming

Transcript

  1. ʲվగ൛ʳ
    1)1Ͱݎ࿚ͳίʔυΛॻ͘
    ྫ֎ॲཧɺද໌ϓϩάϥϛϯάɺܖ໿ʹΑΔઃܭ
    ࿨ా୎ਓ !U@XBEB

    %FD !1)1ΧϯϑΝϨϯε࠶ԋ

    View Slide

  2. ࿨ా୎ਓ
    JEUXBEB
    !U@XBEB
    HJUIVCUXBEB

    View Slide

  3. ひとり歩きするスタンド
    ஫Ͱ΋ࠓ೔͸ςετͷ࿩͸͠·ͤΜ

    View Slide

  4. class BugRepository
    {
    public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    }
    print_r(BugRepository::findAll([
    'assignedTo' => '12',
    'status' => 'OPEN'
    ]));
    ஫ʰ42-ΞϯνύλʔϯʱͷͻͲ͍
    ίʔυྫΛΞϨϯδͯ͠ॻ͍͍ͯ·͢
    ɹͱ͋Δͱ͜Ζʹɺ͜Μͳίʔυ͕͋Γ·ͨ͠
    #
    "
    %

    View Slide

  5. $ php example.php
    Array
    (
    [0] => Bug Object
    (
    [bug_id] => 842
    [summary] => 保存処理でクラッシュする
    [date_reported] => 2016-10-26
    )
    [1] => Bug Object
    (
    [bug_id] => 5150
    [summary] => XMLのサポート
    [date_reported] => 2016-10-26
    )
    [2] => Bug Object
    (
    [bug_id] => 6060
    [summary] => パフォーマンスの向上
    [date_reported] => 2016-10-26
    )
    )
    ɹॲཧ͕੒ޭ͢Δͱɺ͜Μͳײ͡ɻ

    View Slide

  6. ಥવͰ͕͢
    ͜͜Ͱ
    ΫΠζͰ͢

    View Slide

  7. public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    ⏰੍ݶ࣌ؒඵ
    ɹॲཧࣦഊͷݪҼʹͳΔՄೳੑ͕͋Δͷ͸Ͳͷߦ
    #
    "
    %

    View Slide

  8. public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    #
    "
    %
    σʔλϕʔε઀ଓཱࣦ֬ഊ
    AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ

    View Slide

  9. public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    #
    "
    %
    ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    ͜͜Ͱ
    σʔλϕʔε઀ଓΤϥʔ
    Fatal error: Call to a member function execute() on a non-object

    View Slide

  10. public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    #
    "
    %
    QBSBNT͕OVMM
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    ͜͜Ͱ
    σʔλϕʔε઀ଓΤϥʔ

    View Slide

  11. public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    #
    "
    %
    #VHΫϥε͕ະఆٛ
    ͜͜Ͱ
    σʔλϕʔε઀ଓΤϥʔ ˞ઃఆʹΑΔ

    View Slide

  12. public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    ɹॲཧࣦഊͷݪҼʹͳΔՄೳੑ͕͋Δͷ͸Ͳͷߦ
    #
    "
    %




    ͑ͬɺ͜Μͳʹ͋Δͷ

    View Slide

  13. ɹ͜ͷͳ͔Ͱ΋ಛʹखڧ͍ঢ়گ͸ͲΕ
    σʔλϕʔε઀ଓཱࣦ֬ഊ
    AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ
    ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    QBSBNT͕OVMM
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ

    View Slide

  14. ɹࢲݟͰ͸QBSBNTͷΩʔ໊΍਺ͷෆҰக
    public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    #
    "
    %

    View Slide

  15. $ php example.php
    PHP Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid
    parameter number: parameter was not defined in example.php
    Array
    (
    )
    ɹΩʔ໊΍਺Λؒҧ࣮͑ͯߦ͢ΔͱͲ͏ͳΔ
    ཪͰͬͦ͜Γܯࠂ͕ग़Δ͚ͩͳͷ
    ۭ഑ྻ͕ฦͬͯ͘Δͷ
    ਖ਼ৗܥͱݟ෼͚͕͔ͭͳ͘ͳ͍

    View Slide

  16. ෆ۩߹ͷൃݟ͕
    ஗ΕΕ͹஗ΕΔ΄Ͳ
    ই͸ਂ͘ͳΔ
    IUUQTXXXBTURCPSHQSFTTSPPN*452#@$FSUJpDBUJPO@/FXT@@IUNM

    View Slide

  17. public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    ɹॲཧࣦഊͷݪҼʹͳΔՄೳੑ͕͋Δͷ͸Ͳͷߦ
    #
    "
    %




    ݱ࣮ੈք΁Α͏ͦ͜

    View Slide

  18. lݡ໌ͳιϑτ΢ΣΞٕज़ऀʹ
    ͳΔͨΊͷୈҰา͸ɺಈ͘ϓϩ
    άϥϜΛॻ͘͜ͱͱਖ਼͍͠ϓϩ
    άϥϜΛద੾ʹ࡞੒͢Δ͜ͱͷ
    ҧ͍Λೝࣝ͢Δ͜ͱz
    Š."+BDLTPO

    View Slide

  19. ɹݎ࿚ͳίʔυ͸Ҏ্͕ΤϥʔϋϯυϦϯά

    View Slide

  20. Agenda
    ୈ෦༧๷తϓϩάϥϛϯά
    ୈ෦߈ܸతϓϩάϥϛϯά
    ୈ෦ܖ໿ϓϩάϥϛϯά

    View Slide

  21. ɹࣦഊͷݪҼʹ͍ͭͯߟ͑࢝ΊΑ͏
    ❔σʔλϕʔε઀ଓཱࣦ֬ഊ
    ❔AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ
    ❔ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    ❔QBSBNT͕OVMM
    ❔QBSBNTͷΩʔ໊΍਺ͷෆҰக
    ❔QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    ❔#VHΫϥε͕ະఆٛ
    ❔్தͰσʔλϕʔε઀ଓΤϥʔ

    View Slide

  22. σʔλϕʔε઀ଓཱࣦ֬ഊ
    AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ
    ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    QBSBNT͕OVMM
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ɹࣦഊͷݪҼΛΧςΰϦ෼͚ͯ͠ΈΔ

    View Slide

  23. σʔλϕʔε઀ଓཱࣦ֬ഊ
    AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ
    ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    QBSBNT͕OVMM
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ɹؒҧͬͨ࢖ΘΕํΛ͞Ε΍͍͢ɺͱ͍͏໰୊͕͋Γͦ͏



    View Slide

  24. ରॲ༧๷
    ༧๷తϓϩάϥϛϯά

    View Slide

  25. w ʮ๷ޚతϓϩάϥϛϯάʯͱ͸ɺϓϩάϥϛϯάʹ
    ରͯ͠๷ޚతʹͳΔ͜ͱɺͭ·Γʮͦ͏ͳΔ͸ͣ
    ͩʯͱܾΊ͚ͭͳ͍͜ͱ
    w ๷ޚతϓϩάϥϛϯάͷࠜఈʹ͋Δͷ͸ɺϧʔνϯ
    ʹෆਖ਼ͳσʔλ͕౉͞Εͨͱ͖ʹɺͦΕ͕ଞͷϧʔ
    νϯͷ͍ͤͰ͋ͬͨͱͯ͠΋ɺඃ֐Λड͚ͳ͍Α͏
    ʹ͢Δ͜ͱ
    w ͜ͷͨΊɺ
    w ֎෦ιʔε͔Βͷσʔλͷ஋Λ͢΂ͯ֬ೝ͢Δ
    w ϧʔνϯͷ͢΂ͯͷೖྗҾ਺ͷ஋Λ֬ೝ͢Δ
    w ෆਖ਼ͳೖྗΛॲཧ͢Δํ๏Λܾఆ͢Δ
    ๷ޚతϓϩάϥϛϯά

    View Slide

  26. ڱٛͷ
    ๷ޚతϓϩά
    ϥϛϯά΁ͷޡղ

    View Slide

  27. public static function findAll($params)
    {
    if (is_null($params)) {
    throw new InvalidArgumentException('params should not be null');
    }
    if (!is_array($params)) {
    throw new InvalidArgumentException('params should be an array');
    }
    if (count($params) !== 2) {
    throw new InvalidArgumentException('params should be have exact two items');
    }
    if (!array_key_exists('assignedTo', $params) ||
    !array_key_exists('status', $params)) {
    throw new InvalidArgumentException('params should have key `assignedTo` and `status`
    only');
    }
    if (!is_int($params['assignedTo'])) {
    throw new InvalidArgumentException('params[`assignedTo`] should be an integer');
    }
    if (!is_string($params['status'])) {
    throw new InvalidArgumentException('params[`status`] should be a string');
    }
    if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) {
    throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`');
    }
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    ɹͨͩͻͨ͢ΒೖྗΛνΣοΫ͠Α͏ͱͨ͠Γ
    #
    "
    %

    View Slide

  28. global $CONF;
    $dsn = $CONF['dsn'] ?? $CONF['ds'] ?? $CONF['dataSource'];
    $user = $CONF['usr'] ?? $CONF['user'];
    $password = $CONF['passwd'] ?? $CONF['password'];
    $pdo = new PDO($dsn, $user, $password,
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $safeParams = [
    'assignedTo' => $params['assignedTo'] ?? $params['assigned_to'],
    'status' => $params['status'] ?? 'OPEN',
    ];
    $stmt->execute($safeParams);
    $className = class_exists('Bug') ? 'Bug' : 'BugModel';
    return $stmt->fetchAll(PDO::FETCH_CLASS, $className);
    ɹෆਖ਼ͳೖྗ͕͋ͬͯ΋ࣗ෼ͰͳΜͱ͔͠Α͏ͱͨ͠Γ
    #
    "
    %

    View Slide

  29. /**
    * 担当者, ステータスに合致する Bug を検索し、ヒットした全件を Bug オブジェクト
    の配列として返す。
    *
    * @param array $params 格納した検索条件の連想配列。キー `assignedTo` にユーザID
    をintで, キー `status` にステータス文字列をstringで指定すること。キー、値それぞ
    れNULLは不可とする。
    * @return Bug[] 検索結果を Bug オブジェクトにマッピングして返す。検索結果が0件
    のときは空配列を返す。
    */
    public static function findAll($params)
    {
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    ɹυΩϡϝϯτͰؒҧ͍΍͢͞Λิ͓͏ͱͨ͠Γ
    #
    "
    %

    View Slide

  30. ๷ޚతϓϩάϥϛϯάͱ͸
    ѱ͍ίʔυʹឺ૑ߣΛ͋ͯ
    Δ͜ͱͰ͸ͳ͍

    View Slide

  31. w ʮ๷ޚతϓϩάϥϛϯάʯͱ͸ɺ໰୊ൃੜΛ
    ࣄલʹ๷͝͏ͱ͍͏ίʔσΟϯάελΠϧ
    w Մಡੑͷߴ͍ίʔυͱద੾ͳ໋໊نଇ
    w શͯͷؔ਺ͷ໭Γ஋ΛνΣοΫ
    w σβΠϯύλʔϯͷ࠾༻
    w ཁ͢Δʹɺྑࣝ͋Δ࣮ફͷੵΈॏͶͰ͋Δ
    w ๷ޚతϓϩάϥϛϯά͸ɺਖ਼͍͠ίʔυ࡞੒
    ͷͨΊͷن཯ΛϓϩάϥϚ͕Ұ؏ͯ͠ద༻͢
    ΔͨΊͷҰछͷίʔσΟϯάඪ४
    ๷ޚతϓϩάϥϛϯά

    View Slide

  32. wྑ͍ΠϯλϑΣʔεͱ͸࣍ͷͭͷ
    ৚݅Λຬͨ͢ΠϯλϑΣʔε
    wਖ਼͘͠࢖༻͢Δํ͕ૢ࡞ϛεΛ
    ͢ΔΑΓ؆୯
    wޡͬͨ࢖͍ํΛ͢Δ͜ͱ͕ࠔ೉
    ͖ͷ͜ਖ਼͍͠࢖͍ํΛ؆୯ʹɺ
    ޡͬͨ࢖͍ํΛࠔ೉ʹ

    View Slide

  33. ܕͷ੍ݶ
    ܕએݴ

    View Slide

  34. lʮग़དྷͯ͸ͳΒ͵͜ͱΛې͡Δʯͷ
    Ͱ͸ͳ͘ɺ͸͡Ί͔Βʮग़དྷ͍͍ͯ͜
    ͱ͚ͩΛग़དྷΔΑ͏ʹ͢Δʯͱߟ͑Δ
    ͷͰ͢z
    ͖ͷ͜+
    ݟ஌Β͵ਓͱ΋͏·͘΍Δʹ͸

    View Slide

  35. public static function findAll(int $assignedTo, string $status)
    {
    if (is_null($params)) {
    throw new InvalidArgumentException('params should not be null');
    }
    if (!is_array($params)) {
    throw new InvalidArgumentException('params should be an array');
    }
    if (count($params) !== 2) {
    throw new InvalidArgumentException('params should be have exact two items');
    }
    if (!array_key_exists('assignedTo', $params) ||
    !array_key_exists('status', $params)) {
    throw new InvalidArgumentException('params should have key `assignedTo` and
    `status` only');
    }
    if (!is_int($params['assignedTo'])) {
    throw new InvalidArgumentException('params[`assignedTo`] should be an
    integer');
    }
    if (!is_string($params['status'])) {
    throw new InvalidArgumentException('params[`status`] should be a string');
    }
    if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) {
    throw new InvalidArgumentException('params[`status`] should be in
    `OPEN`,`NEW`,`FIXED`');
    }
    ɹܕએݴʹΑͬͯʮग़དྷ͍͍ͯ͜ͱ͚ͩΛग़དྷΔʯΑ͏ʹ
    1
    )
    1

    View Slide

  36. public static function findAll(int $assignedTo, string $status)
    {
    if (!in_array($status, ['OPEN', 'NEW', 'FIXED'], true)) {
    throw new InvalidArgumentException('params[`status`] should
    be in `OPEN`,`NEW`,`FIXED`');
    }
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT);
    $stmt->bindValue(':status', $status, PDO::PARAM_STR);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    ɹ૝ఆ͠ͳ͚Ε͹ͳΒͳ͍ঢ়گ͕ݮͬͨ
    1
    )
    1

    注: さらに堅くしたい場合は
    呼び出し側ファイルで厳密な型チェックを有効にする
    declare(strict_types=1);

    View Slide

  37. ஋ͷ੍ݶ
    &OVN

    View Slide

  38. ͖ͷ͜
    ؔ਺ͷʮαΠζʯΛখ͘͢͞Δ
    wݴޠ૊ΈࠐΈͷܕ JOU TUSJOH౳
    Λ
    ࢖͏ͱɺऔΓಘΔ஋ͷ૊Έ߹Θ͕ͤ
    ๲େʹͳΔ
    w໰୊ྖҬͷ஌ࣝΛ׆༻ͯ͠ݻ༗ͷܕ
    Λ࡞Δ͜ͱͰɺऔΓಘΔ૊Έ߹Θͤ
    Λେ෯ʹݮΒͤΔ

    View Slide

  39. IUUQRJJUBDPN)JSBLVJUFNTFCEDBBGF

    View Slide

  40. abstract class Enum
    {
    private $scalar;
    public function __construct($value)
    {
    $ref = new ReflectionObject($this);
    $constants = $ref->getConstants();
    if (!in_array($value, $constants, true)) {
    throw new InvalidArgumentException("value [{$value}] is not defined");
    }
    $this->scalar = $value;
    }
    final public function value()
    {
    return $this->scalar;
    }
    final public function __toString()
    {
    return (string)$this->scalar;
    }
    }
    ɹ)JSBLV͞Μͷ&OVN࣮૷Λ গ͠ΞϨϯδͯ͠
    ࢖ͬͯΈΔ

    View Slide

  41. final class Status extends Enum
    {
    const OPEN = 'OPEN';
    const NEW = 'NEW';
    const FIXED = 'FIXED';
    }
    $status = new Status(Status::OPEN);
    $status = new Status('OPEN');
    // "InvalidArgumentException: value [HOGE] is not defined"
    $status = new Status('HOGE');
    ɹ͋Β͔͡Ίఆٛ͞Εͨ஋͚ͩΛΠϯελϯεԽͰ͖Δ

    View Slide

  42. public static function findAll(int $assignedTo, Status $status)
    {
    if (!in_array($status, ['OPEN', 'NEW', 'FIXED'], true)) {
    throw new InvalidArgumentException('params[`status`] should
    be in `OPEN`,`NEW`,`FIXED`');
    }
    global $CONF;
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT);
    $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    ɹ૝ఆ͠ͳ͚Ε͹ͳΒͳ͍ঢ়گ͕͞Βʹݮͬͨ

    View Slide

  43. σʔλϕʔε઀ଓཱࣦ֬ഊ
    AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ
    ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    QBSBNT͕OVMM
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ɹޡΓ΍͍͢ΠϯλʔϑΣΠεʹىҼ͢ΔॲཧࣦഊΛ๾໓



    View Slide

  44. σʔλϕʔε઀ଓཱࣦ֬ഊ
    AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ
    ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    QBSBNT͕OVMM
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ɹ࣍ͷ૬ख͸ɺ஌Γ͗͢ɺ੹຿ͷଟ͗͢ʹىҼ͢Δॲཧࣦഊ


    View Slide

  45. ஌Γ͗͢ͳ͍
    ΍Γ͗͢ͳ͍
    ੹຿ͷ࠶഑ஔ

    View Slide

  46. class BugRepository
    {
    private $pdo;
    public function __construct(PDO $pdo)
    {
    $this->pdo = $pdo;
    }
    // 以下省略
    }
    // 設定者 (DI コンテナ等)
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [
    PDO::ATTR_EMULATE_PREPARES => false,
    ]);
    $repo = new BugRepository($pdo);
    // 利用者
    print_r($repo->findAll(12, new Status(Status::OPEN)));
    ɹ1%0ੜ੒ͱઃఆͷ੹຿Λ֎෦ʹग़͠ɺίϯετϥΫλͰड͚औΔ

    View Slide

  47. σʔλϕʔε઀ଓཱࣦ֬ഊ
    AVTSA AQBTTXEA౳Ωʔ໊͕มߋ͞Εͨ
    ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    QBSBNT͕OVMM
    QBSBNTͷΩʔ໊΍਺ͷෆҰக
    QBSBNTͷ஋͕จࣈྻʹม׵ෆೳ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ɹ஌Γ͗͢ɺ੹຿ͷଟ͗͢ʹىҼ͢ΔॲཧࣦഊΛ๾໓


    View Slide

  48. public static function findAll($params)
    {
    if (is_null($params)) {
    throw new InvalidArgumentException('params should not be null');
    }
    if (!is_array($params)) {
    throw new InvalidArgumentException('params should be an array');
    }
    if (count($params) !== 2) {
    throw new InvalidArgumentException('params should be have exact two items');
    }
    if (!array_key_exists('assignedTo', $params) ||
    !array_key_exists('status', $params)) {
    throw new InvalidArgumentException('params should have key `assignedTo` and `status` only');
    }
    if (!is_int($params['assignedTo'])) {
    throw new InvalidArgumentException('params[`assignedTo`] should be an integer');
    }
    if (!is_string($params['status'])) {
    throw new InvalidArgumentException('params[`status`] should be a string');
    }
    if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) {
    throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`');
    }
    global $CONF;
    if (!isset($CONF['dsn'])) {
    throw new LogicException('config key `dsn` not found');
    }
    if (!isset($CONF['usr'])) {
    throw new LogicException('config key `usr` not found');
    }
    if (!isset($CONF['passwd'])) {
    throw new LogicException('config key `passwd` not found');
    }
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    if (!class_exists('Bug')) {
    throw new LogicException('class `Bug` does not exist');
    }
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    public function __construct(PDO $pdo)
    {
    $this->pdo = $pdo;
    }
    public function findAll(int $assignedTo, Status $status)
    {
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $this->pdo->prepare($sql);
    $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT);
    $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    ୈ෦·ͱΊ༧๷ʹউΔ๷ޚͳ͠

    View Slide

  49. ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ɹݱࡏͷ৺഑ࣄϦετ

    View Slide

  50. Agenda
    ୈ෦༧๷తϓϩάϥϛϯά
    ୈ෦߈ܸతϓϩάϥϛϯά
    ୈ෦ܖ໿ϓϩάϥϛϯά

    View Slide

  51. GBJMGBTU
    ߈ܸతϓϩάϥϛϯά

    View Slide

  52. w ίʔυதʹʮ͋Γಘͳ͍ʯͱࢥΘΕΔԿ͔
    ͕ൃੜͨ͠৔߹ɺͦͷ࣌఺ͰϓϩάϥϜ͸
    ΋͸΍࣮ߦՄೳͳ΋ͷͱ͸ͳ͍ͬͯͳ͍
    w ԿΒ͔ͷ͍͕ٙ͋ΔͷͰ͋Ε͹ɺͲͷΑ͏
    ͳ৔߹Ͱ΋଎΍͔ʹఀࢭͤ͞Δ΂͖ɻ௨ৗ
    ͷ৔߹ɺো֐Λ๊͑ͯத్൒୺ʹಈ͍͍ͯ
    ΔϓϩάϥϜΑΓ΋ࢮΜͩϓϩάϥϜͷ΄
    ͏͕μϝʔδ͸গͳ͍
    ώϯτૣΊʹΫϥογϡͤ͞Δ͜ͱ

    View Slide

  53. IUUQTXXXBTURCPSHQSFTTSPPN*452#@$FSUJpDBUJPO@/FXT@@IUNM
    ো֐Λ๊͑ͯ
    த్൒୺ʹಈ͍͍ͯΔϓϩάϥϜΑΓ΋
    ࢮΜͩϓϩάϥϜͷ΄͏͕
    μϝʔδ͸গͳ͍

    View Slide

  54. ͦΜͳʹؾָʹ
    γεςϜΛམͱͯ͠
    ͍͍ͷ

    View Slide

  55. w ࠷దͳΤϥʔॲཧ͸Τϥʔ͕ൃੜͨ͠ιϑτ΢ΣΞ
    ͷछྨʹΑΓҟͳΔ
    w ਖ਼౰ੑͱ͸ɺෆਖ਼֬ͳ݁ՌΛܾͯ͠ฦ͞ͳ͍͜ͱΛ
    ҙຯ͢Δɻෆਖ਼֬ͳ݁ՌΛฦ͘͢Β͍ͳΒɺԿ΋ฦ
    ͞ͳ͍ํ͕·͠Ͱ͋Δ
    w ݎ࿚ੑͱ͸ɺιϑτ΢ΣΞͷ࣮ߦΛܧଓͰ͖ΔΑ͏
    ʹखΛਚ͘͢͜ͱͰ͋ΔɻͦΕʹΑͬͯෆਖ਼͕֬݁
    Ռ͕΋ͨΒ͞ΕΔ͜ͱ͕͋ͬͯ΋͔·Θͳ͍
    w ҆શੑ ΍ਖ਼֬ੑ
    Λॏࢹ͢ΔΞϓϦέʔγϣϯͰ
    ͸ɺݎ࿚ੑΑΓ΋ਖ਼౰ੑ͕༏ઌ͞ΕΔ܏޲ʹ͋Δ
    w ίϯγϡʔϚΞϓϦέʔγϣϯͰ͸ɺਖ਼౰ੑΑΓ΋
    ݎ࿚ੑ͕༏ઌ͞ΕΔ܏޲ʹ͋Δ
    ݎ࿚ੑͱਖ਼౰ੑ

    View Slide

  56. ݸʑͷΫϥε͸ਖ਼౰ੑΛॏࢹ͠ɺ
    ݎ࿚ੑ͸ΞʔΩςΫνϟϑϨʔϜϫʔΫ౳Ͱ
    อূ͢Δͷ͕Φεεϝ
    ྫݸʑͷΫϥε͸GBJMGBTUݪଇͰॻ͖ɺ8FC
    ϑϨʔϜϫʔΫ΍άϩʔόϧϋϯυϥ͕Ωϟον
    ͯ͠Τϥʔը໘౳Λग़͢੹຿Λෛ͏
    ɹϛΫϩͰ͸ਖ਼౰ੑΛॏࢹ͠ɺϚΫϩͰ͸ݎ࿚ੑΛॏࢹ͢Δ

    View Slide

  57. ʮ͔΋͠Εͳ͍ʯ
    ྫ֎తঢ়گʹରॲ
    ͢Δ

    View Slide

  58. ͖ͷ͜
    ΤϥʔΛແࢹ͢Δͳ
    wΤϥʔΛແࢹͯ͠΋Կ΋ྑ͍͜ͱ͸ແ͍
    wෆ҆ఆͳίʔυ
    wηΩϡϦςΟ্໰୊ͷ͋Δίʔυ
    wශऑͳߏ଄ͱΠϯλʔϑΣΠε
    wͲ͏͢Δ
    w໭Γ஋Λ࢖͏
    wྫ֎Λ࢖͏

    View Slide

  59. public function findAll(int $assignedTo, Status $status)
    {
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    if (($stmt = $this->pdo->prepare($sql)) !== false) {
    if ($stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT) !== false) {
    if ($stmt->bindValue(':status', $status->value(), PDO::PARAM_STR) !== false) {
    if ($stmt->execute() !== false) {
    if (($bugs = $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class)) !== false) {
    return $bugs;
    }
    }
    }
    }
    }
    return false;
    }
    ɹ1%0Τϥʔ࣌ͷ໭Γ஋ GBMTF
    ΛνΣοΫ͢Δ
    #
    "
    %
    ೾ಈݓ
    NJYFEฦ͠

    View Slide

  60. public function findAll(int $assignedTo, Status $status)
    {
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    if (($stmt = $this->pdo->prepare($sql)) === false) {
    $error = $this->pdo->errorInfo();
    report_error('prepare: ' . $error[2]);
    return false;
    }
    if ($stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT) === false) {
    $error = $stmt->errorInfo();
    report_error('bindValue: ' . $error[2]);
    return false;
    }
    if ($stmt->bindValue(':status', $status->value(), PDO::PARAM_STR) === false) {
    $error = $stmt->errorInfo();
    report_error('bindValue: ' . $error[2]);
    return false;
    }
    if ($stmt->execute() === false) {
    $error = $stmt->errorInfo();
    report_error('execute: ' . $error[2]);
    return false;
    }
    if (($bugs = $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class)) === false) {
    $error = $stmt->errorInfo();
    report_error('fetchAll: ' . $error[2]);
    return false;
    }
    return $bugs;
    }
    ɹ໭Γ஋ GBMTF
    ΛνΣοΫͯ͠ૣظϦλʔϯ
    #
    "
    %
    ஫SFQPSU@FSSPS͸ϩάʹग़ࣗ͢࡞ؔ਺
    NJYFEฦ͠

    View Slide

  61. ໭Γ஋ͷऑ఺
    ໭Γ஋͸
    ɹίʔυ͕ංେԽ͕ͪ͠
    ɹແࢹ͞Ε΍͍͢
    ɹͨͱ͑ॏେͳ໰୊͕જΜͰ͍ͯ
    ΋ɺ໭Γ஋͚ͩͰ͸఻ΘΓʹ͍͘
    ࣮ࡍ$ݴޠͷؔ਺ͷ໭Γ஋ͷதʹ͸ʮແࢹ͢Δͷ͕ී
    ௨ʯͱ͞Ε͍ͯΔ΋ͷ͑͋͞Δ

    View Slide

  62. ෆ۩߹ͷൃݟ͕
    ஗ΕΕ͹஗ΕΔ΄Ͳ
    ই͸ਂ͘ͳΔ
    IUUQTXXXBTURCPSHQSFTTSPPN*452#@$FSUJpDBUJPO@/FXT@@IUNM

    View Slide

  63. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION それだ!!
    1%0Λྫ֎Ϟʔυʹ͠Α͏
    IUUQQIQOFUNBOVBMKBQEPFSSPSIBOEMJOHQIQ

    View Slide

  64. public function findAll(int $assignedTo, Status $status)
    {
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $this->pdo->prepare($sql);
    $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT);
    $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    // 設定者
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'], [
    PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]);
    $repo = new BugRepository($pdo);
    ɹGBMTFͷ୅ΘΓʹ1%0&YDFQUJPO͕ൃੜ͢ΔΑ͏ʹͳΔ
    $ php example.php
    PHP Fatal error: Uncaught PDOException: SQLSTATE[42S22]: Column not
    found: 1054 Unknown column 'date_reported' in 'field list' in /path/
    to/example.php:63

    View Slide

  65. wྫ֎ͷ࠷େͷར఺͸ɺແࢹͰ͖ͳ͍
    ํ๏ͰΤϥʔঢ়ଶΛ஌ΒͤΔ͜ͱͰ
    ͋Δ ΦϒδΣΫτࢦ޲ೖ໳ୈ൛

    wྫ֎ΛނҙʹѲΓͭͿ͢͜ͱ͸Մೳ
    ͕ͩɺͦ͏͍͏ίʔυ͕ॻ͔Ε͍ͯ
    Δͱ͖͸ɺॻ͖खͷ࢟੎ʹ໰୊͕͋
    Δͱ͙͢ʹΘ͔Δ ͖ͷ͜

    ྫ֎ͷར఺

    View Slide

  66. ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ

    ɹݱࡏͷ৺഑ࣄϦετ
    ͕ͩɺͪΐͬͱ଴ͬͯ΄͍͠

    View Slide

  67. ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ΋͠΋1%0͕ྫ֎ϞʔυͰͳ͔ͬͨΒ
    ɹ҉໧ͷલఏ͕ՃΘΓɺ৺഑ࣄ͕૿͍͑ͯͳ͍͔

    View Slide

  68. ҉໧ͷલఏΛ໌ࣔ͠
    όάΛ͋ͿΓग़͢
    ද໌ϓϩάϥϛϯά

    View Slide

  69. ʮى͜Δ͸͕ͣͳ͍ʯͱࢥͬͯ
    ͍Δ͜ͱ͕͋Ε͹ɺͦΕΛνΣο
    Ϋ͢ΔίʔυΛ௥Ճͯͩ͘͠͞
    ͍ɻද໌ BTTFSUJPO
    Λ༻͍Δͷ
    ͕࠷΋؆୯ͳํ๏Ͱ͢
    ώϯτ΋͠ى͜Γಘͳ͍ͱ͍͏ͷͰ͋Ε͹ɺ
    ද໌Λ༻͍ͯͦΕΛอূ͢Δ͜ͱ

    View Slide

  70. 1)1ͷBTTFSU
    1
    )
    1

    IUUQQIQOFUNBOVBMKBGVODUJPOBTTFSUQIQ

    View Slide

  71. public function findAll(int $assignedTo, Status $status)
    {
    assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) ===
    PDO::ERRMODE_EXCEPTION);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $this->pdo->prepare($sql);
    ɹൃੜͨ͠҉໧ͷલఏΛBTTFSUʹධՁࣜͰॻ͖ɺ໌ࣔ͢Δ
    あらたに PDO::ERRMODE_EXCEPTION に
    依存するようになったことを assert で明示する

    View Slide

  72. $ php example.php
    PHP Warning: assert(): assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) ===
    PDO::ERRMODE_EXCEPTION) failed in /path/to/example.php on line 59
    PHP Fatal error: Uncaught Error: Call to a member function bindValue() on
    boolean in /path/to/example.php:63
    ɹͬͦ͘͞ද໌ೖΓͷίʔυΛ࣮ߦͯ͠ΈΔ
    ධՁ͕ࣜग़ͯΘ͔Γ΍͍͢
    Ͱ΋ܯࠂ͕ग़Δ͚ͩͰɺམͪͳ͍
    མͪͳ͍ͷͰͦͷઌͰผͷΤϥʔʹͳͬͨ


    1
    )
    1

    View Slide

  73. 1)1ͷBTTFSUͷઃఆ
    1
    )
    1

    PHP5 との互換性を保つためデフォルトでは警告になっている。
    つまり php.ini で assert.exception = 1 にすべし
    IUUQQIQOFUNBOVBMKBGVODUJPOBTTFSUQIQ

    View Slide

  74. $ php example.php
    PHP Fatal error: Uncaught AssertionError: assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE)
    === PDO::ERRMODE_EXCEPTION) in /path/to/example.php:59
    Stack trace:
    #0 /path/to/example.php(59): assert(false, 'assert($this->p...')
    #1 /path/to/example.php(75): BugRepository->findAll(12, Object(Status))
    #2 {main}
    thrown in /path/to/example.php on line 59
    ɹBTTFSUFYDFQUJPOʹͯ͠࠶࣮ߦ
    ͖ͪΜͱද໌ҧ൓ͰམͪΔ
    Α͏ʹͳͬͨ

    1
    )
    1

    View Slide

  75. w ද໌͸ίϛϡχέʔγϣϯͱσόοάͷπʔϧͱ
    ͯ͠ಇ͘
    w ίϛϡχέʔγϣϯͷ؍఺Ͱ͸ɺද໌Λॻ͘͜ͱ
    ʹΑͬͯɺͦͷίʔυΛॻ͍ͨͱ͖ͷલఏΛίʔ
    υͷಡΈख͕ཧղ͠΍͘͢ͳΔ
    w σόοάͷ؍఺Ͱ͸ɺόάΛͦͷݪҼʹ͍ۙͱ͜
    ΖͰൃݟ͠΍͘͢ͳΔ
    w ςετίʔυΛॻ͍ͯ͋Ε͹ɺσόοάͷࢧԉ͸
    ͞΄ͲॏཁͰ͸ͳ͍͕ɺͦΕͰ΋ίϛϡχέʔ
    γϣϯͷ؍఺ʹ͓͚Δද໌ͷՁ஋͸ɺґવͱͯ͠
    ༗ޮ
    ද໌ͷϝϦοτ

    View Slide

  76. ྫ֎ͱද໌ͷ࢖͍෼͚
    w ຊདྷͷΤϥʔॲཧʹද໌Λ࢖ͬͯ͸͍
    ͚·ͤΜɻද໌͸ى͜Γಘͳ͍͜ͱΛ
    νΣοΫ͢ΔͨΊͷ΋ͷͰ͢ ৽૷൛
    ୡਓϓϩάϥϚʔ

    w ൃੜ͕༧૝͞ΕΔঢ়گʹ͸Τϥʔॲཧ
    ίʔυΛ࢖༻͠ɺൃੜͯ͠͸ͳΒͳ͍
    ঢ়گʹ͸ΞαʔγϣϯΛ࢖༻͢Δ
    $0%&$0.1-&5&ୈ൛

    View Slide

  77. w͋·Γʹ΋๷ޚతͳϓϩάϥϛϯ
    ά΋ɺͦΕ͸ͦΕͰ໰୊
    wҾ਺Λߟ͑ΒΕΔݶΓͷ৔ॴͰ
    ߟ͑ΒΕΔݶΓͷํ๏ͰνΣο
    Ϋ͢Ε͹ɺϓϩάϥϜ͸ංେԽ
    ͠௿଎ʹͳΔ
    BTTFSUJPOΛೖΕ͗͢Δͱ
    ௿଎ʹͳΔͷͰ͸ʁ
    ʮ๷ޚతϓϩάϥϛϯάʹର͢Δ๷ޚʯΑΓ

    View Slide

  78. ͦ͜Ͱ1)1Ͱ͢Α
    1
    )
    1

    php.ini の zend.assertions で表明のオン/オフを制御できる
    IUUQQIQOFUNBOVBMKBGVODUJPOBTTFSUQIQ

    View Slide

  79. ɹ[FOEBTTFSUJPOT ද໌Φϯ

    1
    )
    1

    public function findAll(int $assignedTo, Status $status)
    {
    assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) ===
    PDO::ERRMODE_EXCEPTION);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $this->pdo->prepare($sql);

    View Slide

  80. public function findAll(int $assignedTo, Status $status)
    {
    assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) ===
    PDO::ERRMODE_EXCEPTION);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $this->pdo->prepare($sql);
    ɹ[FOEBTTFSUJPOT ද໌Φϑ

    1
    )
    1

    php.ini に zend.assertions = -1 と設定すれば表明を除去できる

    View Slide

  81. ද໌ʹҾ͖౉͢৚݅ʹ͸෭࡞༻͕
    ͋ͬͯ͸͍͚·ͤΜɻίϯύΠϧ࣌ʹ
    ද໌͕Φϑʹ͞ΕΔ৔߹΋͋Δͱ͍
    ͏఺Λ๨Εͯ͸͍͚·ͤΜɻ
    ͭ·ΓBTTFSUதʹ͸࣮ߦʹඞཁͳ
    ίʔυΛهड़ͯ͠͸͍͚ͳ͍ͷͰ͢
    ʮώϯτ΋͠ى͜Γಘͳ͍ͱ͍͏ͷͰ͋Ε͹ɺද໌Λ༻͍ͯͦΕΛอূ͢Δ͜ͱʯΑΓ
    ɹΦϑʹ͞ΕΔ͜ͱΛҙࣝͯ͠ॻ͘
    副作用の例: assert(end($users));

    View Slide

  82. w Ξαʔγϣϯ͸ɺQVCMJDϝιου಺ͷҾ਺νΣοΫ
    ʹ࢖༻͠ͳ͍Ͱ͍ͩ͘͞
    w Ҿ਺ͷνΣοΫ͸௨ৗɺϝιουͷ࢓༷ ·ͨ͸ن໿

    ͷҰ෦ʹͳ͓ͬͯΓɺΞαʔγϣϯͷ༗ޮແޮʹ͔
    ͔ΘΒͣɺ͜ͷ࢓༷ʹ४ڌ͢Δඞཁ͕͋Γ·͢ɻ
    w ΞϓϦέʔγϣϯͷਖ਼͍͠ಈ࡞ʹඞཁͳॲཧΛ࣮ߦ
    ͢ΔͨΊʹΞαʔγϣϯΛ࢖༻͠ͳ͍Ͱ͍ͩ͘͞
    IUUQTEPDTPSBDMFDPNKBWBTFKQEPDTUFDIOPUFTHVJEFTMBOHVBHFBTTFSUIUNM
    ʮΞαʔγϣϯΛ࢖༻ͨ͠ϓϩάϥϛϯάʯΑΓ
    ɹඞਢͷҾ਺νΣοΫ΍όϦσʔγϣϯʹ࢖Θͳ͍
    +BWB

    View Slide

  83. IUUQTXXXBTURCPSHQSFTTSPPN*452#@$FSUJpDBUJPO@/FXT@@IUNM
    ୈ෦·ͱΊlGBJMGBTUz
    ো֐Λ๊͑ͯ
    த్൒୺ʹಈ͍͍ͯΔϓϩάϥϜΑΓ΋
    ࢮΜͩϓϩάϥϜͷ΄͏͕
    μϝʔδ͸গͳ͍

    View Slide

  84. ςʔϒϧ໊΍ΧϥϜ໊͕୭͔ʹมߋ͞Εͨ
    #VHΫϥε͕ະఆٛ
    ్தͰσʔλϕʔε઀ଓΤϥʔ
    ΋͠΋1%0͕ྫ֎ϞʔυͰͳ͔ͬͨΒ
    ɹݱࡏͷ৺഑ࣄϦετ


    View Slide

  85. Agenda
    ୈ෦༧๷తϓϩάϥϛϯά
    ୈ෦߈ܸతϓϩάϥϛϯά
    ୈ෦ܖ໿ϓϩάϥϛϯά

    View Slide

  86. ͩΕͷ੹຿͔
    ϋοΩϦͤ͞Δ
    ܖ໿ʹΑΔઃܭ

    View Slide

  87. ܖ໿ʹΑΔઃܭ

    View Slide

  88. wܖ໿ʹΑΔઃܭͱ͸ɺιϑτ΢ΣΞ
    Ϟδϡʔϧͷݖརͱ੹೚ΛจॻԽʢͦ
    ͯ͠ঝ୚ʣ͠ɺϓϩάϥϜͷਖ਼͠͞
    Λอূ͢ΔͨΊͷ؆͔ܿͭύϫϑϧ
    ͳٕ๏Ͱ͢
    wͰ͸ɺਖ਼͍͠ϓϩάϥϜͱ͸ҰମԿ
    Ͱ͠ΐ͏͔ʁ͜Ε͸ɺཁٻ͞Εͨ͜
    ͱҎ্ͷ͜ͱ΋ɺͦΕҎԼͷ͜ͱ΋
    ߦΘͳ͍ͱ͍͏΋ͷͰ͢
    ώϯτܖ໿Λ༻͍ͯઃܭΛߦ͏͜ͱ

    View Slide

  89. \1^"\2^
    ਖ਼͠͞ͷެࣜ )PBSF5SJQMF

    ࣄલ৚݅1͕੒Γཱͭͱ͖ʹɺϓϩάϥϜ"Λ࣮ߦ͢
    Δͱͦͷ࣮ߦޙʹ͸ඞͣࣄޙ৚݅2͕੒ΓཱͭͳΒ
    ͹ɺϓϩάϥϜ"͸ɺࣄલ৚݅1ͱࣄޙ৚݅2ͱʹ
    ؔͯ͠෦෼తʹਖ਼౰ʢQBSUJBMMZDPSSFDUʣͰ͋Δ

    View Slide

  90. w΋ͦͪ͠Β͕ࣄલ৚݅Λຬͨͨ͠ঢ়
    ଶͰࢲΛݺͿͱ໿ଋͯ͠Լ͞ΔͳΒ
    ͹ɺ͓ฦ͠ʹࣄޙ৚݅Λຬͨ͢ঢ়ଶ
    Λ࠷ऴతʹ࣮ݱ͢Δ͜ͱΛ͓໿ଋ͠
    ·͢
    ܖ໿ʹΑΔઃܭ
    %FTJHOCZ$POUSBDU %C$

    View Slide

  91. ੒ޭࣦഊόάྫ֎
    IUUQRJJUBDPN,PLVEPSJJUFNTBDGBC

    View Slide

  92. Throwable
    ├── Error
    │ ├── ArithmeticError
    │ │ └── DivisionByZeroError
    │ ├── AssertionError
    │ ├── ParseError
    │ └── TypeError
    └── Exception
    ├── ErrorException
    ├── LogicException
    │ ├── BadFunctionCallException
    │ │ └── BadMethodCallException
    │ ├── DomainException
    │ ├── InvalidArgumentException
    │ ├── LengthException
    │ └── OutOfRangeException
    └── RuntimeException
    ├── OutOfBoundsException
    ├── OverflowException
    ├── PDOException
    ├── RangeException
    ├── UnderflowException
    └── UnexpectedValueException
    1
    )
    1

    3VOUJNF&YDFQUJPOܥ
    ྫ֎
    -PHJD&YDFQUJPOܥόά
    &SSPSܥόά

    View Slide

  93. w࣮ߦ࣌ͷද໌ҧ൓ ΍ɺόάΛࣔͨ͢Ί
    ͷྫ֎
    ͸ɺͦͷιϑτ΢ΣΞʹόά͕
    ͋ΔূڌͰ͋Δ
    wࣄલ৚݅ҧ൓͸ݺͼग़͠ଆʹόά͕
    ͋ΔূڌͰ͋Δ
    wࣄޙ৚݅ҧ൓͸ڙڅऀଆʹόά͕͋
    ΔূڌͰ͋Δ
    ܖ໿ʹΑΔઃܭ
    %FTJHOCZ$POUSBDU %C$

    View Slide

  94. ɹ#VHΫϥε͕ະఆٛͷঢ়گʹ͸Ͳ͏උ͑Δ
    $ php example.php
    PHP Fatal error: Class 'Bug' not found
    in /path/to/example.php on line 85
    ౴͑ͳʹ΋͠ͳ͍ɻ
    ͜Ε͸ྫ֎తঢ়گͰ͸ͳ͘ʢจࣈ௨ΓʣόάͰ
    ͋ΓɺϓϩάϥϛϯάϛεͳͷͰɺΤϥʔϋϯ
    υϦϯάͯ͠͸ͳΒͳ͍ɻͳʹ΋ͤͣɺ଎΍͔
    ʹམͱ͢ɻGBJMGBTU͕ॏཁɻ

    View Slide

  95. ίϯετϥΫλͷ
    ܖ໿

    View Slide

  96. /**
    * 検索処理に使用する PDO インスタンスを渡し、バグリポジトリを初期化する。
    *
    * @param PDO $pdo PDO インスタンス。 PDO::ATTR_ERRMODE が PDO::ERRMODE_EXCEPTION に
    設定されていること
    * @throws InvalidArgumentException PDO::ATTR_ERRMODE が適切に設定されていない場合
    */
    public function __construct(PDO $pdo)
    {
    if ($pdo->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_EXCEPTION) {
    throw new InvalidArgumentException('requires PDO::ERRMODE_EXCEPTION');
    }
    $this->pdo = $pdo;
    }
    ɹίϯετϥΫλͷࣄલ৚݅Λදݱ͢Δ

    View Slide

  97. pOE"MMϝιου
    ͷܖ໿

    View Slide

  98. /**
    * 指定された担当者 ID およびステータスに合致する Bug を検索し、ヒットした全件を Bug
    オブジェクトの配列として返す。
    *
    * @param int $assignedTo 担当者ID
    * @param Status $status ステータス
    * @return Bug[] 条件に合致した Bug オブジェクトの配列を返す。検索に合致するものがな
    い場合は空配列を返す
    * @throws LogicException カラム名違いや文法エラー等SQLのミスが存在する場合
    * @throws PDOException データベースとのやりとりに何らかの障害が発生した場合
    */
    public function findAll(int $assignedTo, Status $status): array
    {
    assert($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    try {
    $stmt = $this->pdo->prepare($sql);
    $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT);
    $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    } catch (PDOException $e) {
    if ($this->isGrammaticalError($e->getCode())) {
    throw new LogicException($e->getMessage(), $e->errorInfo[1], $e);
    }
    throw $e;
    }
    }
    ɹpOE"MMϝιουͷࣄޙ৚݅Λදݱ͢Δ

    View Slide

  99. } catch (PDOException $e) {
    if ($this->isGrammaticalError($e->getCode())) {
    throw new LogicException($e->getMessage(), $e->errorInfo[1], $e);
    }
    throw $e;
    }
    ɹpOE"MMϝιουͷࣄޙ৚݅Λදݱ͢Δ
    SQLSTATE の値を調査し、 PDOException の内容が例外で
    はなくあきらかにバグの場合は、
    バグを示す LogicException で包んで投げ直している。
    その際に第3引数を忘れずに設定し、スタックトレースを
    つなぐ

    View Slide

  100. ୈ෦·ͱΊόάͱྫ֎Λ۠ผ͠ɺ
    ͞Βʹ୭ͷ੹೚͔΋ݟ෼͚ΒΕΔΑ͏ʹ͢Δ
    IUUQRJJUBDPN,PLVEPSJJUFNTBDGBC

    View Slide

  101. ·ͱΊ

    View Slide

  102. public static function findAll($params)
    {
    if (is_null($params)) {
    throw new InvalidArgumentException('params should not be null');
    }
    if (!is_array($params)) {
    throw new InvalidArgumentException('params should be an array');
    }
    if (count($params) !== 2) {
    throw new InvalidArgumentException('params should be have exact two items');
    }
    if (!array_key_exists('assignedTo', $params) ||
    !array_key_exists('status', $params)) {
    throw new InvalidArgumentException('params should have key `assignedTo` and `status` only');
    }
    if (!is_int($params['assignedTo'])) {
    throw new InvalidArgumentException('params[`assignedTo`] should be an integer');
    }
    if (!is_string($params['status'])) {
    throw new InvalidArgumentException('params[`status`] should be a string');
    }
    if (!in_array($params['status'], ['OPEN', 'NEW', 'FIXED'], true)) {
    throw new InvalidArgumentException('params[`status`] should be in `OPEN`,`NEW`,`FIXED`');
    }
    global $CONF;
    if (!isset($CONF['dsn'])) {
    throw new LogicException('config key `dsn` not found');
    }
    if (!isset($CONF['usr'])) {
    throw new LogicException('config key `usr` not found');
    }
    if (!isset($CONF['passwd'])) {
    throw new LogicException('config key `passwd` not found');
    }
    $pdo = new PDO($CONF['dsn'], $CONF['usr'], $CONF['passwd'],
    [ PDO::ATTR_EMULATE_PREPARES => false ]);
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    if (!class_exists('Bug')) {
    throw new LogicException('class `Bug` does not exist');
    }
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    public function __construct(PDO $pdo)
    {
    $this->pdo = $pdo;
    }
    public function findAll(int $assignedTo, Status $status)
    {
    $sql = 'SELECT bug_id, summary, date_reported FROM Bugs
    WHERE assigned_to = :assignedTo AND status = :status';
    $stmt = $this->pdo->prepare($sql);
    $stmt->bindValue(':assignedTo', $assignedTo, PDO::PARAM_INT);
    $stmt->bindValue(':status', $status->value(), PDO::PARAM_STR);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_CLASS, Bug::class);
    }
    ୈ෦·ͱΊ༧๷ʹউΔ๷ޚͳ͠

    View Slide

  103. IUUQTXXXBTURCPSHQSFTTSPPN*452#@$FSUJpDBUJPO@/FXT@@IUNM
    ୈ෦·ͱΊlGBJMGBTUz
    ো֐Λ๊͑ͯ
    த్൒୺ʹಈ͍͍ͯΔϓϩάϥϜΑΓ΋
    ࢮΜͩϓϩάϥϜͷ΄͏͕
    μϝʔδ͸গͳ͍

    View Slide

  104. ୈ෦·ͱΊόάͱྫ֎Λ۠ผ͠ɺ
    ͞Βʹ୭ͷ੹೚͔΋ݟ෼͚ΒΕΔΑ͏ʹ͢Δ
    IUUQRJJUBDPN,PLVEPSJJUFNTBDGBC

    View Slide

  105. lݡ໌ͳιϑτ΢ΣΞٕज़ऀʹ
    ͳΔͨΊͷୈҰา͸ɺಈ͘ϓ
    ϩάϥϜΛॻ͘͜ͱͱਖ਼͍͠
    ϓϩάϥϜΛద੾ʹ࡞੒͢Δ
    ͜ͱͷҧ͍Λೝࣝ͢Δ͜ͱz
    Š."+BDLTPO

    ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

    View Slide