Slide 1

Slide 1 text

ྫ֎Λ౤͛ΔͷΛ΍ΊͯΈͳ͍͔ʁ ͋Δ͍͸ड͚ೖΕͯΈͳ͍͔ʁ — PHP Conference hokkaido 2024 — uzulla

Slide 2

Slide 2 text

ࣗݾ঺հ — id: uzulla — ৭ʑͰੜܭΛཱ͍ͯͯ·͢ — PHPͰΦϨΦϨFWΛ࡞ͬͨΓɺ ࢖ͬͨΓͯ͠·͢ — ͭ·Γ अಓͳPHP Λॻ͍͍ͯ· ͢(ʁʁʁ) — ʁʮԦಓͱ͸ʁʁʁʯ ࢲʮब৬Ͱ໾ཱͭΑ͏ͳ…ʯ

Slide 3

Slide 3 text

DISCLAIMER — अಓͳਓؒͷτʔΫͰ͢(2౓໨) — ࢀߟʹ͢Δͱ͖ʹ͸ɺνʔϜ΍ઌഐͱ૬ஊ͠·͠ΐ͏ — ݹདྷΑΓʮྫ֎͸੍ޚߏ଄౳ʹ͔ͭͬͯ͸ͳΒͳ͍ʯ http://somabo.de/talks/200705_chicago_spl_update.pdf 101pࢀর — ͳͷͰ अಓͰ͢(3ճ໨) — ղΒͣʹ͔ͭ͏ͷ͸ʮ৮Δͳةݥɺ΍͚Ͳ஫ҙʯͰ͢ — ͋Δ͍͸ʮղΔ͕Զ͸΍Βͳ͍ʯ౳ʹͳΔ͸ͣͰ͢

Slide 4

Slide 4 text

(৺ͷ੠ɺ͋Δ͍͸ಠࣗղऍɺ΋͘͠͸ݴ͍༁) ʮͤ΍͔ͯɺѱ༻ෆՄ͸throw΍try/catchογϣʁ ɹGOTOېࢭΈ͍ͨͳ΋Μογϣʁ ɹException objectͷऔΓѻ͍͸ࣗ༝ογϣʁʯ

Slide 5

Slide 5 text

15෼͔͕࣌ؒ͠ͳ͍ͷͰ ଈअಓ ʂ — PHPͷException͸ɺnewͨ͠ΠϯελϯεͰ͢ɻ — ͭ·ΓɺValueͳͷͰ — returnͰ͖Δ͠ — Ҿ਺ʹ΋Ͱ͖Δ͠ — ഑ྻʹ٧ΊΔ͜ͱ΋Ͱ͖·͢ — ʮ͜Ε͸ศརʹ ѱ༻ ׆༻Ͱ͖ΔΑφʂʂʂʯ

Slide 6

Slide 6 text

ʮྫ֎Λ֦ு͠ɺ୅ೖ͢Δϋφ γʯ

Slide 7

Slide 7 text

Slide 8

Slide 8 text

Slide 9

Slide 9 text

Slide 10

Slide 10 text

0) { throw new UserEditException('Invalid input', 0, null, $exceptions); }

Slide 11

Slide 11 text

ʮ…͜Εɺͳʹ͕͏Ε͍͠ͷʁʯ — ྫ֎ͬͯɺجຊҰ͔ͭ͠౤͛ΒΕͳ͍ΜͰ͢Α(ॏཁ) — try/catchͱthrow͸ɺGOTOΈ͍ͨͳ΋ͷͳͷͰ — ஋ݕূͱ͔ɺ୔ࢁͷྫ֎͕͋Γ͏Δ͡Όͳ͍Ͱ͔͢ʁ ·ͱΊͯऔΓѻ͑Δͱศར͡Όͳ͍Ͱ͔͢ʁ — ͳͷͰɺྫ֎Λ഑ྻͰ࣋ͯΔྫ֎͕͋Δͱศར͔ͳͬͯ… (࣮ࡍศར)

Slide 12

Slide 12 text

## Controllerͱ͔Ͱ function run(array $post){ try{ $exceptions = []; // ͖ͬ͞ͷίʔυͱಉ͡ͳͷͰলུ if (count($exceptions) > 0) { throw new UserEditException('Invalid input', 0, null, $exceptions); } return render_success_page(); }catch (UserEditException $e){ return render_error_page($e->exceptions); } }

Slide 13

Slide 13 text

ͱ͜ΖͰಠࣗྫ֎ͬͯศརͰ͢Ͷ — ಠࣗྫ֎(Ϋϥε)ͳΒinstanceofͰʮͦΕ͕Ͳͷྫ֎ͳͷ ͔ʯΛؒҧ͍ͳ͘൑ఆͰ͖ΔΜͰ͢Α — (ॻ͘ʹͯ͠΋ɺϦϑΝΫλϦϯάͰফ͢ͱͯ͠΋) ܕͱ੩తղੳͰ΍͍͖͍ͬͯͨ͡Όͳ͍Ͱ͔͢ʁ — ʮશ෦Exceptionͱ͔RuntimeExceptionʹͯ͠ɺ ɹgetMessageΛจࣈྻϚονͰ෼ذʯͱ͔͸΍ΊΑ͏

Slide 14

Slide 14 text

// ྫ֎ͷ഑ྻ͔Βɺࢦఆͷྫ֎͚ͩΛൈ͖ग़͢܅ function get_list_contain_exception(array $exceptions, string $exception_name): array { return array_reduce( $exceptions, function($carry, $e){ if($e instanceof $exception_name) $carry[] = $e; }, [] ): array; } 0){ ?>
getMessage()?>

Slide 15

Slide 15 text

ࢲʮྫ֎Λ஋ͱͯ͠࢖͏ͷศརʂʂʂʯ — օ͞Μʮ…ʁʁʁʁʁʯ — ͍͖͍͚͓ͭͯͯͨͩͯΓ·͢Ͱ͠ΐ͏͔… — ෆ໌఺͸ɺ࿓ԼͰ࣭໰͍ͯͩ͘͠͞ɻ — (ཁ๬͋Ε͹ɺYouTubeͳͲͰ࠶ԋ͠·͢…)

Slide 16

Slide 16 text

΋ͬͱྫ֎Λ֦ுɾ׆༻ɾ ѱ༻ ͍ͨ͠ʂʂʂ — (ࢲʮ͞Βʹअಓʹͯ͠ɺօ͞ΜΛ͓͖͟Γʹ͍ͯ͘͠ʯ) — ྫ֎͸σόοάͱ͔ௐࠪʹ࢖͏͔Β(ී௨)දࣔ༻ʹ͸࢖͑ͳ ͍… Ͱ΋ɺදࣔ༻ͷจষͱ͔ͬ͠Γඥ෇͚͍ͨɻ — ͞Βʹ͍͑͹ɺଟࠃޠରԠͱ͔͍ͨ͠ɻ — ʁʮಠࣗྫ֎Λ୔ࢁͭ͘Δͷ͕େม͗͢ΔΜͰ͕͢ʯ ࢲʮCodeͬͯ΋ͷ͕͋ΔͷͰɺͦΕ࢖͑Δʯ ʁʮCodeͬͯͳʹʁʯࢲʮ͋͞…ʁʯ

Slide 17

Slide 17 text

ྫ֎ʹػೳࡌͤͪΌ͏ϋφγ

Slide 18

Slide 18 text

value => [ self::SHORT => "୹͍Ͱ͢Ͷʙʙ", self::AVOID_LF => "վߦ͸ແཧʂ", ], ExceptionLang::EN->value => [ self::SHORT => "Too Short", self::AVOID_LF => "LF is not allowed", ], ]; }

Slide 19

Slide 19 text

trait I18NMessageTrait // ͖ͬ͞use͍ͯͨ͠΋ͷ { public function getI18NMessage(ExceptionLang $lang) { return self::MESSAGES[$lang->value][$this->code] ?? throw new \OutOfBoundException( "Undefined message." . self::class ." code:{$this->code}" ); } } // -- enum ExceptionLang: string { case JA = 'ja'; // ผʹENUMʹ͠ͳͯ͘΋ɺจࣈྻͷjaͰΑ͍ͷͰ͸ʁʁʁ case EN = 'en'; }

Slide 20

Slide 20 text

# όϦσʔγϣϯՕॴ౳Ͱ… $e = new InvalidPasswordException( "ྫ֎ϝοηʔδΛϢʔβʔʹදࣔ͢Δͷ͸Ξϯνύλʔϯ ͜͜͸Ϣʔβʔʹදࣔ͠ͳ͍ͷͰɺσόοά৘ใ͍ΕͯΑ͍", InvalidPasswordException::SHORT ); # ςϯϓϨʔτͱ͔Ͱ… echo $e->getI18NMessage(ExceptionLang::JA); // => "୹͍Ͱ͢Ͷʙʙ"

Slide 21

Slide 21 text

ศརʂʂʂ — ϝοηʔδͱ͔ΛPO΍JSONͰ࣋ͭΑΓɺ ίʔυʹೖΕΕ͹όϥόϥʹͳΒͣʹࡁΉʂ — ੩తղੳͰʮ࢖ΘΕ͍ͯͳ͍ΤϥʔϝοηʔδʯΛ ಛఆͰ͖·͢ — ͍ͭ΋͸ࢦఆ͠ͳ͍ɺྫ֎ͷ Code Λ׆༻͢Ε͹ ಠࣗྫ֎ͷ਺ΛͪΐͬͱݮΒͤ·͢

Slide 22

Slide 22 text

͜͏͍͏ΦϨΦϨͳ͜ͱΛ͢Δͱ… — ʁʮ͍΍…ීஈݟͳ͍ελΠϧʹ͸ੜཧతݏѱײ͕…ʯ — ࢲʮΦϨΦϨͷҰछ͔ͩΒͶ…ʯ — (ଟ෼)͜ͷίʔυ͸10೥ੜ͖Δࣄ͕Ͱ͖Δʂʂʂ (͕͜͜ݸਓతʹ͸ॏཁ) — ࢲ͸ɺศརϥΠϒϥϦʹͨΑΒͣɺPOPOͰද͍ͨ͠ɺ ͜Ε͘Β͍ͷදݶ౓͸ศརϥΠϒϥϦʹཔΒͣͰ͖Δ

Slide 23

Slide 23 text

ʮ͜Εɺྫ֎Ͱ͋Δඞཁ͕͋Δͷ͔ʁʯ — ʁʮUserEditException͸ͱ΋͔͘ɺத਎͸ExceptionͰͳ ͍΄͏͕Α͍ͷͰ͸ʁ͜Ε͸ཞ༻Ͱ͸ʁʯ ࢲʮΫϥεͳΜ͔ͩΒศརϝιουੜ΍ͯ͠΋͑͑΍Ζʯ — ʮException͸ExceptionͳͷͰɺؒҧ͑Α͏͕ͳ͍ʯͱ͍ ͏ϝϦοτ(ओ؍)͕͋Δ — ౤͛ͳ͍ྫ֎ʹ͸ҧ࿨ײ͋ΔͰ͠ΐ͏͕ɺม਺ͱͯ͠औΓ ճͯ͠΋ɺେ఍࠷ޙ͸TypeErrorͰམͪΔͷͰ݁ߏ҆શ (࣍ϖʔδ΁ଓ͘)

Slide 24

Slide 24 text

ྫ֎ΛҰ੾౤͛ͳ͍ϋφγ

Slide 25

Slide 25 text

class UserEntity { public function __construct( public int $id, public string $name, public string $email, public string $password ) { } }

Slide 26

Slide 26 text

# ྫ֎Λฦ஋ʹ࢖͏ѱ༻ʁྫ function getUserFromDB(int $id): UserEntity|null|\RuntimeException { try { $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', ''); $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id'); $stmt->execute(['id' => $id]); if(($res = $stmt->fetch(PDO::FETCH_ASSOC)) === false) return null; return new UserEntity(...$res); } catch (\Exception $e) { return \RuntimeException('ͳΜ͔DBͰΤϥʔ͕͓ͬͨ͜Θʙʙ', 0, $e); } } // --- $user = getUserFromDB(1); if (is_null($user)) { return show_notfound(); } elseif ($user instanceof \RuntimeException) { return show_internal_server_error($user/*== Exception*/); } return show_user_detail($user);

Slide 27

Slide 27 text

΢ϫʔοʂʂʂ — ͻͲ͍ίʔυͩͱ͓΋͍·͔͢ʁ => ͸͍ɻ — ͨͩɺ୔ࢁͷTry/CatchΑΓಡΈ΍͍͢(ओ؍) (͜Ε͙Β͍୹͍ͱಡΔ͕ɺtry͕௕͔ͬͨΓɺ୔ࢁtry/ catch͕͋ΔͱࡶͳPokemon HandlingʹͳΓ͕ͪ) — (͜ͷྫͩͱ)show_user_detail ͰҾ਺͕ UserEntity Ͱ͋Δ ͱ໌هͯ͋͠Ε͹TypeErrorͰམͪΔͷͰɺ ઌʹਐΉ͜ͱ͸ͳ͍ͷͰͪΐͬͱ҆શ

Slide 28

Slide 28 text

# ී௨ͱअಓͷൺֱྫ ## ී௨ͷTry/Catch try{ $user = getUserFromDB(1); if (is_null($user)) show_notfound(); return show_user_detail($user); # ͜͜Β΁Μ͕ΊʔͬͪΌͳͳ͕͍͜ͱ͕ଟ͍ }catch(SomeException $e){ return show_internal_server_error($e->getMessage()); # ୔ࢁCatch͕͋ͬͨΓ… }catch(\RuntimeException $e){ return show_internal_server_error($e->getMessage()); # ͷͰɺ͕͍͜͜͢͝ԕ͍ } ## ઌ΄ͲͷअಓྫɺૣظReturnͬΆ͍Ͷʁ $user = getUserFromDB(1); if (is_null($user)) { return show_notfound(); } elseif ($user instanceof \RuntimeException) { return show_internal_server_error($user); } return show_user_detail($user);

Slide 29

Slide 29 text

΍ͬͺΓExceptionͰͳͯ͘Α͍ͷͰ͸ʁ — ͸͍ɻ — ͔͠͠ʮແ͍ʯʮ͋ΔʯʮΤϥʔʯ͸ผͷදݱʹ͍ͨ͠ — User,null,falseΈ͍ͨͳͷ͸֮͑Δͷ͕େม — ʮfalse͕ΤϥʔͰͳ͍έʔε΋͋Δʯ — Exception͸ɺ͔͋Β͞·ʹҟ࣭ͳͷͰɺΘ͔Γ΍͍͢ — ࠷ѱɺͳ͛Ε͹্Ґ͕Ωϟονͯ͘͠ΕΔ

Slide 30

Slide 30 text

·ͱΊ — Exception Λ͙͢ʹ౤ࣺ͛ͯͣɺड͚ೖΕΔɺ ͦΜͳअಓͰ͔ͭ͏ͱศརͳࣄ΋͋Δ — ʁʮ͜ΜͳͷҟৗʯࢲʮͦΜͳ͜ͱͳ͍Αʂʯ ʁʮࢀߟࢿྉʹ͔͍ͯ͋ͬͨʯࢲʮ͔ͨ͠ʹ…ʯ — ޻෉͢Δͱ৭ʑग़དྷָ͍ͯ͠Ͱ͢ΑͶɺ ʮ○○ͷڭ͑ʹ͸ͳ͍ʂʯͱ҆қʹ͸ ٫Լ͠ͳ͍ͱ͍͍ͱࢥ͍·͢ɻ — ͳͥͳΒɺੈք͸มΘΔ͔ΒͰ͢(࿝ਓͷܦݧ)

Slide 31

Slide 31 text

͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ — ࣭ٙԠ౴λΠϜ — ͋Δ͍͸ɺ͔͜͜Β͓·͚

Slide 32

Slide 32 text

໌೔͔Β࢖͑Δখωλ — ʮྫ֎ʹۭimplementsΛ͔ͭ͏ͱศརʂʂʯ — ಠࣗྫ֎ʹಛఆͷimplementsΛ͚ͭΔͱɺʮϚʔΫʯ͕ͭ ͚ΒΕΔ — ʮ͜ͷϚʔΫ͕͋ΔͳΒɺϩάʹͩ͢ʯͱ͔ɺʮίϨ͸Ϛ δக໋తʯͱ͔͕৭ʑ΍Γ΍͍͢ — ϦϑΝΫλϦϯάͷͱ͖ʹྫ֎Λ෇͚ସ͑Δͱ͔ةͳ͍͜ ͱΛ͠ͳͯ͘ྑ͍ — traitΈ͍ͨͳ΋ͷ(ॾઆ͋Γ·͢)

Slide 33

Slide 33 text

// ϚʔΧʔɺத਎͸ۭͰΑ͍ interface MustLogging{} // ಠࣗྫ֎ class MyException extends RuntimeException implements MustLogging{} // ྫ֎ॲཧͷͱ͖ɺΠϯϓϦ͞Ε͍ͯΔ͔ΛݟͯॲཧΛม͑Δ catch(Exception $e){ if($e instanceof MustLogging) error_log($e->getMessage()); } MustLogging ͷ෇͚֎͠͸҆શʹͰ͖Δʂ ͨͩ݁ߏن໿ײ͋Γɺଞਓʹ͸ಡΈͮΒ͍͔΋ (FWͬͯͦ͏͍͏΋ͷͰ͸ʁ)

Slide 34

Slide 34 text

## ׆༻࣮ྫ(ʁ) interface BailoutWhenBatchTag{ } // όονͷ৔߹͸ඞͣఀࢭ͢ΔϚʔΫ interface IgnoreOnProductionTag{ } // ຊ൪ͩͱແࢹ͢ΔϚʔΫ interface InternalServerErrorTag{ } // 500ʹ͍ͨ͠ϚʔΫɺBadRequestͱ͔΋͋Δ

Slide 35

Slide 35 text

ਖ਼͍͠ྫ֎(ʁ)Λֶͼ͍ͨͳΒ — ͍͔ͭ͘ࢿྉ΁ͷϦϯΫ — (ࣗ෼ͷݴ༿Ͱ΋৭ʑॻ͜͏ͱ͓΋͚ͬͨͲɺͲ͏ͤԶͷݴ ͏͜ͱͳΜͯΈΜͳ৴༻ͤΜ΍Ζ͔ΒͳʂΨϋϋʂʂ)

Slide 36

Slide 36 text

ͱΓ͋͑ͣɺkoriym͞Μͷهࣄ — ΤΩαΠτ Advent Calendar 2016 15೔໨ʮPHPͷྫ֎ʯ — https://qiita.com/koriym/items/17cfd1bbbaead1a2e5a9

Slide 37

Slide 37 text

࣍ʹt-wada͞Μͷൃද — PHP7 Ͱݎ࿚ͳίʔυΛॻ͘ - ྫ֎ॲཧɺද໌ϓϩάϥϛ ϯάɺܖ໿ʹΑΔઃܭ / PHP Conference 2016 — https://speakerdeck.com/twada/php-conference-2016 — ྫ֎ઃܭʹ͓͚Δେࡑ — https://www.slideshare.net/t_wada/exception-design- by-contract

Slide 38

Slide 38 text

ಠࣗྫ֎ͷઃܭྫ — PHPʹ͓͚Δಠࣗྫ֎ઃܭΛߟ͑Δ - hgsgtk͞Μ — https://speakerdeck.com/hgsgtk/a-way-to-design- user-defined-exception-with-php — (SymfonyελΠϧͷྫղઆͰ໘ന͍Ͱ͢Ͷ)

Slide 39

Slide 39 text

— Laravel ϚχϡΞϧ — https://laravel.com/docs/10.x/errors — ݁ߏྫ֎Λ׆༻͍ͯ͠Δ — ΠϕϯτόϒϦϯάΈ͍ͨʹɺͰ͖Δ͚ͩྫ֎͸Laravel(֎ ଆ)ʹ೚ͤΔελΠϧ — ϚʔΫΛ෇͚Δ͜ͱͰɺLaravelଆ͕Α͠ͳʹ͢ΔͳͲ͋Δ — ͚Ͳɺʮศརͳ෼ن໿͕͔ͳΓଟ͍ʯͷͰ֮͑Δͷ͸গʑ େม

Slide 40

Slide 40 text

ͦ΋ͦ΋ʮྫ֎Ͱͳ͚Ε͹ͳΒͳ͍ͷ͔ʁʯ — https://go.dev/doc/faq#exceptions — ௒ҙ༁ʮྫ֎ॲཧ͕هड़Ͱ͖Δʹ༧૝Մೳ ͳͷʹʰྫ֎ʱͳͷʁifͰॻ͚ͳ͍ʁʯ => (͔ͨ͠ʹฦ஋Ͱ൑ఆ͸ϛεΓ΍͍͢ɺ๨ΕͨΓɺΤ ϥʔͱ஋͕ಉҰࢹ͞ΕͨΓ(go͸ଟ஋Λ͔ͭͬͨ — Laravel͸FormͱɺMessageBag

Slide 41

Slide 41 text

ͱ͸͍͑ɺྫ֎͸ศར — Τϥʔ࣌ʹɺΤϥʔॲཧ΁ҰؾʹδϟϯϓͰ͖Δ — ≒ʮਖ਼ৗ࣌͸ແࢹ͢Δίʔυʯָ͕ʹॻ͚Δɺ ͨͱ͑͹ΤϥʔϩάɺDBૢ࡞࣌ͷϩʔϧόοΫɺ ͖Ε͍ͳΤϥʔը໘ — (ݸਓతʹ͸ʮ(ճ෮Մೳͳ)ΤϥʔॲཧʯΛॻͨ͘Ίͷ΋ ͷ͡Όͳ͍ͱࢥͬͯΔ) — (ͷͰɺಡΈ΍͘͢͢ΔͨΊʹ࢖͏΂͖Ͱ͋Γɺ ʰݎ࿚ʱʹ͍ͨ͠ͳΒɺී௨ʹifͰஸೡʹॻ͍ͯ΄͍͠)

Slide 42

Slide 42 text

͔͠͠ɺ(ࢥ͏ʹ)͏·͘࢖͏ͷ͸Ή͔͍ͣ͠ — tryͱcatch(ܕ)ͷ૊Έ߹Θͤ෼ΤϥʔॲཧΛॻ͚Δ͕… — ʮ໘౗Ͱશ෦(Runtime)Exceptionʯ — ʮແݶ૿Ճ͢Δಠࣗྫ֎ʯ(ෳࡶԽ) — try͔Βԕ͗ͯ͢ɺͲ͜Ͱcatch͞ΕΔ͔ಡΈͮΒ͍ — ྫ֎ॲཧ͸Ͱ͖Δ͚ͩগͳ͍΄͏͕Α͍ — catch͕୔ࢁ͋ͬͨΒ஫ҙ͕ඞཁ — (օ͕௨Δ ʮ͸͔͠ʯ Ͱ͸͋Δ)

Slide 43

Slide 43 text

࢖͍Ͳ͜Ζ — ೖྗ஋ɾҾ਺ݕূ — (DB΍ϑΝΠϧͳͲ)ετϨʔδૢ࡞ — ֎෦௨৴ॲཧ — ͜ΕΒҎ֎Ͱtry/catch͔ͭ͏ࣄ͸͋Μ·Γͳ͘ͳ͍͔ʁ — (ͦͯ͠ɺ͜ΕΒ͸ʮ͖Ε͍ͳΤϥʔΛग़͢Ҏ֎Ͳ͏͠Α͏ ΋ͳ͍ʯͬ͠ΐʁ͍͍ͤͥϦτϥΠʁ)

Slide 44

Slide 44 text

PDOExceptionΛྫʹͨ͠ɺඍস·͍͠ਓؒͷ੒௕ྫ — ʮͳΜ͔ɺઌഐ΍AI͕͜͏ॻ͚͍ͬͯͬͨͷͰॻ͍ͨʯ => \Exception͔͔ͭ͠Θͳ͍ظ — ʮDBॲཧ͕ݪҼͱղͬͯศརʂʯ => ྫ֎Λ஌Γ͸͡Ίͯສೳײظ — ʮ͸ʁDBॲཧͷͲ͜ͰࢮΜ͔ͩΘ͔ΒΜ͕ʁม׵͢Μͧʯ => ಠࣗྫ֎Ͱͩ͜ΘΓຍ࠿ظ — ʮϩʔϧόοΫͰ͖Ε͹͍͍ΘɺҰԠϩάͯ͠re-throwʯ => ఘ؍ظ