diff --git a/public/sample/tips/Main.java b/public/sample/tips/Main.java new file mode 100644 index 0000000..ecf0229 --- /dev/null +++ b/public/sample/tips/Main.java @@ -0,0 +1,6 @@ +class Main { + public static void main(String[] args) { + String str = null; + System.out.println(str.length()); // ここでNullPointerExceptionが発生する + } +} diff --git a/public/sample/tips/fix-error.php b/public/sample/tips/fix-error.php new file mode 100644 index 0000000..c6b4bd2 --- /dev/null +++ b/public/sample/tips/fix-error.php @@ -0,0 +1,66 @@ + + */ +function getBoughtItemsAll() +{ + return [ + ['userId' => 1, 'name' => 'Apple', 'price' => 240, 'quantity' => 3], + ['userId' => 1, 'name' => 'Banana', 'price' => 210, 'quantity' => 6], + ]; +} + +/** + * 指定されたユーザーIDに紐づく購入商品の一覧を取得する + * + * @return array + */ +function getBoughtItemsByUserId($userId) +{ + $allItems = getBoughtItemsAll(); + $userItems = []; + + foreach ($allItems as $item) { + if ($item['userId'] === $userId) { + $userItems[] = $item; + } + } + + return $userItems; +} + +/** + * 購入商品の合計金額と平均金額を計算する + * + * @param array $boughtItems + * @return array{total: int, average: float} + */ +function summary($boughtItems) +{ + $total = 0; + foreach ($boughtItems as $item) { + $total += $item['price'] * $item['quantity']; + } + + $average = $total / count($boughtItems); + + return [ + 'total' => $total, + 'average' => $average, + ]; +} + +function main() +{ + $currentUserId = 2; + $boughtItems = getBoughtItemsByUserId($currentUserId); + + $data = summary($boughtItems); + + var_export($data); +} + +main(); diff --git a/public/sample/tips/sample.js b/public/sample/tips/sample.js new file mode 100644 index 0000000..cdfcb73 --- /dev/null +++ b/public/sample/tips/sample.js @@ -0,0 +1,2 @@ +const str = null; +console.log(str.length()); // ここでTypeErrorが発生する diff --git a/public/sample/tips/sample.php b/public/sample/tips/sample.php new file mode 100644 index 0000000..4127b7f --- /dev/null +++ b/public/sample/tips/sample.php @@ -0,0 +1,8 @@ + + +```java file=/sample/tips/Main.java +public/sample/tips/Main.java +``` + +
+ +```bash title="コンソールの出力例" "Exception" "NullPointerException" +$ javac Main.java +$ java Main +Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "" is null + at Main.main(Main.java:4) +``` + + + +
+ +
+ +```js file=public/sample/tips/sample.js +public/sample/tips/sample.js +``` + +
+ +```bash title="コンソールの出力例" "Error" "TypeError" +$ node sample.js +/path/to/project/public/tips/sample.js:2 +console.log(str.length); + ^ + +TypeError: Cannot read properties of null (reading 'length') + at Object. (/path/to/project/public/tips/sample.js:2:17) + at Module._compile (node:internal/modules/cjs/loader:1761:14) + at Object..js (node:internal/modules/cjs/loader:1893:10) + at Module.load (node:internal/modules/cjs/loader:1481:32) + at Module._load (node:internal/modules/cjs/loader:1300:12) + at TracingChannel.traceSync (node:diagnostics_channel:328:14) + at wrapModuleLoad (node:internal/modules/cjs/loader:245:24) + at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5) + at node:internal/main/run_main_module:33:47 +``` + +
+ +
+ +どちらも文字列のような`str`に対してその長さを取得しようとして、その`str`の中身が`null`であるためにエラーが発生しています。 +エラーメッセージをきちんと読んでみると、どちらもそのことが書かれているのがわかりますね。 + +このような直接的な原因は、`Exception`や`Error`のキーワードに続くメッセージに書かれていることが多いです。 +なにかエラーが発生したときは、まずこのキーワードを探してみましょう。 + +### Stack Trace + +
+ +```php file=public/sample/tips/sample.php +public/sample/tips/sample.php +``` + +
+ +```bash {3-5} +$ php sample.php +PHP Fatal error: Uncaught TypeError: Unsupported operand types: int + array in /path/to/project/public/sample/tips/sample.php:5 +Stack trace: +#0 /path/to/project/public/sample/tips/sample.php(8): add() +#1 {main} + thrown in /path/to/project/public/sample/tips/sample.php on line 5 +``` + +
+ +## エラーの原因を特定する + +以下の例を見てみましょう。 +これは購入した商品の合計金額と、その商品の数から平均金額を計算して表示するPHPのプログラムです。 + +実際はログなどの出力や、ユーザーからの報告によってエラーを知ることになります。 +それを想定して、先にコンソールに出力されるエラーメッセージを見てみましょう。 + +```bash title="コンソールの出力例" wrap +PHP Fatal error: Uncaught DivisionByZeroError: Division by zero in /path/to/project/public/sample/tips/fix-error.php:48 +Stack trace: +#0 /path/to/project/public/sample/tips/fix-error.php(61): summary() +#1 /path/to/project/public/sample/tips/fix-error.php(66): main() +#2 {main} + thrown in /path/to/project/public/sample/tips/fix-error.php on line 48 +``` + +
+ +以下がそのソースコードです。 +解説の前に、実際になにが問題なのかを見つけてみましょう。 + +
+ +```php file=public/sample/tips/fix-error.php +public/sample/tips/fix-error.php +``` + +
+ +では解説します。 + +まずはどんなエラーが発生しているのかを確認します。 + +```bash wrap "DivisionByZeroError" +PHP Fatal error: Uncaught DivisionByZeroError: Division by zero in /path/to/project/public/sample/tips/fix-error.php:48 +Stack trace: +#0 /path/to/project/public/sample/tips/fix-error.php(61): summary() +#1 /path/to/project/public/sample/tips/fix-error.php(66): main() +#2 {main} + thrown in /path/to/project/public/sample/tips/fix-error.php on line 48 +``` + +`DivisionByZeroError`というエラーが発生していることがわかります。 +これは割り算をする際に、割る数が`0`である場合に発生するエラーです。 + +このエラー名から、具体的にどんな問題が起きているのかを推測できました。 + +
+ +次に、そのエラーがどこで発生しているのかを確認します。 +エラーから、以下の2箇所がその発生箇所を示しています。 + +```bash wrap "in /path/to/project/public/sample/tips/fix-error.php:48" /thrown in.*$/ +PHP Fatal error: Uncaught DivisionByZeroError: Division by zero in /path/to/project/public/sample/tips/fix-error.php:48 +Stack trace: +#0 /path/to/project/public/sample/tips/fix-error.php(61): summary() +#1 /path/to/project/public/sample/tips/fix-error.php(66): main() +#2 {main} + thrown in /path/to/project/public/sample/tips/fix-error.php on line 48 +``` + +どちらも`fix-error.php`の48行目で発生していることを示していますね。 +その箇所には`$average = $total / count($bouthtItems);`というコードがあります。 + +```php file=public/sample/tips/fix-error.php {48} collapse={1-34, 55-66} +public/sample/tips/fix-error.php +``` + +PHPにおける`count`関数は、配列の要素数を返します。 +つまりこの引数になっている`$boughtItems`の要素数が`0`だったことが原因だと推測できます。 + +
+ +ここからは、なぜ`$boughtItems`の要素数が`0`になったのかの経緯を調べていきます。 + +まずこの変数がどこで設定されているのかを確認します。 +すると、関数の引数として外部から渡されていることがわかります。 + +```php file=public/sample/tips/fix-error.php collapse={1-34, 55-66} "$boughtItems" +public/sample/tips/fix-error.php +``` + +
+ +次に問題になるのは、この関数の引数に、空の配列を渡したのは誰か、ということです。 +ここで手がかりになるのが、スタックトレースです。 + +もう1度スタックトレースを見てみましょう。 + +```bash {2-5} +PHP Fatal error: Uncaught DivisionByZeroError: Division by zero in /path/to/project/public/sample/tips/fix-error.php:48 +Stack trace: +#0 /path/to/project/public/sample/tips/fix-error.php(61): summary() +#1 /path/to/project/public/sample/tips/fix-error.php(66): main() +#2 {main} + thrown in /path/to/project/public/sample/tips/fix-error.php on line 48 +``` + +これを見ると、summary関数が61行目で呼び出されていることがわかります。 +さらにその61行目はmain関数の処理のなかで呼び出されていて、このmain関数は66行目で呼び出されていることがわかります。 + +この情報をもとに、61行目付近のコードを見てみましょう。 + +```php file=public/sample/tips/fix-error.php {61} collapse={2-55} "$boughtItems" +public/sample/tips/fix-error.php +``` + +変数`$boughtItems`が渡されていて、この配列が空だったことが原因でエラーが発生したことがわかります。 +さらにはこの変数は`getBoughtItemsByUserId`関数の戻り値として設定されているところまでわかります。 + +
+ +ここまでの情報で、ここエラーはどういう状況で発生したのかがわかりました。 +これをまとめると、以下のようになります。 + +購入した商品が1つもない状態で平均金額を計算しようとしたために、`0`で割り算をしようとしてエラーが発生した。 + +これがこのエラーの原因です。 +これを踏まえて、今後の対策を考えていくことになるのです。 diff --git a/src/pages/tips/ide.mdx b/src/pages/tips/ide.mdx new file mode 100644 index 0000000..223e00f --- /dev/null +++ b/src/pages/tips/ide.mdx @@ -0,0 +1,12 @@ +--- +layout: "@/layouts/MarkdownLayout.astro" +--- + +import Details from "@/components/Details.astro"; +import Dialog from "@/components/Dialog.astro"; + +export const title = "開発環境を自分好みに整える"; + +# {title} + +## TOC