例外を投げるのをやめてみないか? あるいは受け入れてみないか? - How to use exceptions other than throwing
by
uzulla
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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
(৺ͷɺ͋Δ͍ಠࣗղऍɺ͘͠ݴ͍༁) ʮ͔ͤͯɺѱ༻ෆՄthrowtry/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
ศརʂʂʂ — ϝοηʔδͱ͔ΛPOJSONͰ࣋ͭΑΓɺ ίʔυʹೖΕΕόϥόϥʹͳΒͣʹࡁΉʂ — ੩తղੳͰʮΘΕ͍ͯͳ͍ΤϥʔϝοηʔδʯΛ ಛఆͰ͖·͢ — ͍ͭࢦఆ͠ͳ͍ɺྫ֎ͷ Code Λ׆༻͢Ε ಠࣗྫ֎ͷΛͪΐͬͱݮΒͤ·͢
Slide 22
Slide 22 text
͜͏͍͏ΦϨΦϨͳ͜ͱΛ͢Δͱ… — ʁʮ͍…ීஈݟͳ͍ελΠϧʹੜཧతݏѱײ͕…ʯ — ࢲʮΦϨΦϨͷҰछ͔ͩΒͶ…ʯ — (ଟ)͜ͷίʔυ10ੜ͖Δࣄ͕Ͱ͖Δʂʂʂ (͕͜͜ݸਓతʹॏཁ) — ࢲɺศརϥΠϒϥϦʹͨΑΒͣɺPOPOͰද͍ͨ͠ɺ ͜Ε͘Β͍ͷදݶศརϥΠϒϥϦʹཔΒͣͰ͖Δ
Slide 23
Slide 23 text
ʮ͜Εɺྫ֎Ͱ͋Δඞཁ͕͋Δͷ͔ʁʯ — ʁʮUserEditExceptionͱ͔͘ɺதExceptionͰͳ ͍΄͏͕Α͍ͷͰʁ͜Εཞ༻Ͱʁʯ ࢲʮΫϥεͳΜ͔ͩΒศརϝιουੜͯ͑͑͠Ζʯ — ʮExceptionExceptionͳͷͰɺؒҧ͑Α͏͕ͳ͍ʯͱ͍ ͏ϝϦοτ(ओ؍)͕͋Δ — ͛ͳ͍ྫ֎ʹҧײ͋ΔͰ͠ΐ͏͕ɺมͱͯ͠औΓ ճͯ͠ɺେ࠷ޙ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ଟΛ͔ͭͬͨ — LaravelFormͱɺ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ʯ => ఘ؍ظ