ANSWER
各カードのバグ箇所・エラー内容・修正例をすべて公開しています。プログラミング学習にもご活用ください。
弊社商品バグコードトランプ「bugCode:Cards」をお買い上げいただき、誠にありがとうございます。
本ページでは、カードに記載されたコードのうちどこに不具合の原因があるのか、そしてそれら不具合に対する解説と修正例を記載しています。
皆様のプログラミング学習や、レクリエーション等に活用いただければ幸いです。
コードが記載されているトランプカードのうち
につきましては、弊社による検証の結果
バグの原因となるコードが含まれておりませんでした。
ご期待頂いたにも関わらずこのような不備がありましたことを深くお詫び申し上げます。
SQL構文エラー
このコードでは「sample1」テーブルの「value1」、「value2」カラムにそれぞれデータを格納しようとしていますが、SQL文法ミスが発生しています。
エラー原因は複数カラム指定時のカンマ(,)が多いためです。 単純なミスですが、「後でカラムが増えるかもしれないから」と言ってカンマを付けたままにしていると、大量の文字の中から小さなカンマを見つけるのに時間が掛かりますので気を付けましょう。
PHPでの受け取り名前記述ミス
このコードでは何らかのサムネイル画像のようなファイルをフォームによって送受信しようとしています。
エラー原因は4行目にてinputのnameを「thumb」と設定しているにも関わらず、受け取り側のPHPコードでは「img」という名前でデータを受信しようとしているためエラーが発生しています。
ループ範囲が間違っている
range(1, 10)は1から9までの整数を生成しますが、i == 10という条件はループ内で決して真にならないため、print(chage)が実行されません。 そのためif i == 10の条件が無意味になっています。
ループ範囲をrange(1, 11)を使って拡大する必要があります。
数値計算の前にnumber_format関数を使用してはいけない
このコードでは数値「1234」と「5678」を足し合わせ、その結果を表示しようとしています。 その結果「6912」という解答を得たいところですが、最後に表示されるのは残念ながら「Sales: 6」となります。
コードを見ると、1,2行目で変数a,bはそれぞれnumber_format関数を使用して「1,234」「5,678」という文字列型に変わっています。 文字列型どうしで四則演算することは出来ませんので、そもそも本コードは動作させることができません。
そして注目すべきなのは、本コードを実行しても「Uncaught TypeError: Unsupported operand types: string + string」というエラー表示が出て処理が中断せず、最後まで実行できてしまう点にあります。 PHPは変数の宣言時に型の明示が必要なく他の言語に比べて比較的自由度が高い印象がありますが、この性質が厄介な場面があり、本事例がまさに良い例と言えるでしょう。 エラーが出て処理が中断しないと、間違いの場所を特定するのに時間が掛かってしまいますね。
異なるデータ型で計算しようとしている
aは文字列でbは整数です。異なるデータ型を直接足し合わせることは出来ません。
aを整数に変換することで計算することができるようになりますが、型管理は確実に行いましょう。
配列の要素へのアクセス失敗
このコードでは3行目にて2次元連想配列「$a['flag']['a']」に「1」が格納されている場合に「ok」という表示をするのが目的です。 しかしながら2行目にて1次元連想配列「$a['flag']」に対して文字列「'ok'」を格納しているため、この文字列に対して配列の処理を行う形になっておりエラーとなっています。
cronでのPHPプログラム実行時に他ファイルの参照を行う際、絶対パスを用いて指定する
このコードではcronを用いて毎日8時に「cr.php」を実行しようとしていますが、「cr.php」から「ex.php」を参照しています。
cron実行時とPHP実行時ではワークディレクトリ(プログラムを実行する際のホームディレクトリ)が異なることが大半であり、 その場合cronおよびPHPから見た「ex.php」はそれぞれ場所の指定が違うためエラーとなることが多いです。
これを防止するため、cronでのPHPプログラム実行時に他ファイルの参照を行う際は絶対パスを用いて指定するか、 もしくはPHP上でワークディレクトリをそのPHPファイルが置かれている場所に持っていくのが良いでしょう。 下記修正コードでは後者を採用しています。
form要素の入れ子は禁止
このコードではform要素が入れ子状態になっており、望み通りの挙動をしない可能性が高くなっています。
form要素の入れ子は禁止されています。 この場合、データの送信先は「create」ではなく「edit」となってしまい、場合によっては既に格納されている情報に重大な損失を与えてしまう可能性があります。
HTMLのidとcssの命名時、最初の文字に数字は不可
HTMLにおいて、idとクラス名には命名するにあたり以下のようなルールがあります。
・半角英数字記号のみを使用する。 ・使用できる記号は「-」(ハイフン)または「_」(アンダースコア)のみ ・機種依存文字を使用してはいけない😥 ・スペースは使用しない ・1文字目はアルファベットから開始する
また、idについては追加で下記のようなルールがあります。
・id名は1つのページに1つだけ ・複数のid名を同じ要素につけることはできない
PDOによりデータベースからデータを取り出す方法(フェッチパターン)に注意
このコードでは「sample1」テーブルの「n1」、「n2」カラムに格納されている数値を足し合わせるのが目的です。
しかしながら5行目で「fetchAll」とあるように、条件にあるデータをすべて取り出してカラム名をキーとする連想配列として構成する命令が記述されています。 すなわち配列「$re['n1']」「$re['n2']」の中身はどちらも配列となっているため、8行目では配列同士を加算処理することになり、結果はエラーではなく「0」と表示されてしまいます。
以下の修正コードは、条件に合致した「最初の1行」だけを取り出すことにより配列「$re['n1']」「$re['n2']」の中身を単一の値として構成しました。 もし「fetchAll」を使用して該当するデータすべてに加算処理を行いたい場合は「foerach」を使用して全要素に繰り返し同じ処理を行うか、結果として取得した配列の中身がすべて数値と分かっている場合に限り「array_sum」関数を使用するのも良いでしょう。
「0」と「null(なにもない)」は違う
このコードでは4行目にて変数「$a」「$b」ともに「NULL(空)」である場合に「success」を表示しようとしていますが、関数「is_null」はNULLである場合にのみtrueを返すため条件に合致しません。
PHPには他にも似たような機能を持つ関数があります。 下の表にまとめましたので、是非ご活用ください。
| is_null | empty | isset | if($a) | |
|---|---|---|---|---|
| $a=1 | false | false | true | true |
| $a='something' | false | false | true | true |
| $a='' | false | true | true | false |
| $a=NULL | true | true | false | false |
| $a=0 | false | true | true | false |
| $a='0' | false | true | true | false |
| $a=array() | false | true | true | false |
| $a=array(1) | false | false | true | true |
変数のスコープミス
PHPの変数にはローカル変数とグローバル変数という2種類の変数があり、特にPHPではどちらの変数も対象の範囲外からアクセスすることはできません。
このコードでは関数外で宣言されたグローバル変数「$a」を関数内のローカル変数としても使用しようとしており、このままでは関数「sample」をいくら実行しても何も表示することはできません。
関数の中で関数外の値を使うには引数として値を渡してあげるか、関数内にて関数外の変数を参照するグローバル宣言をすることがあります。 下記の修正されたコードでは後者のパターンを採用しています。
電算機特有の誤差が生じる恐れあり
このコードは正しく動作しているように見えますが、計算上の誤差の影響を受ける可能性があります。 実際、これを実行すると期待している「8」ではなく「7」が表示されてしまいます。
浮動小数点演算では、0.1や0.7という値を正確に表現できないため、「a + b」の計算結果が「0.7999999999999999」となり、それを10倍して「7.999999999999999」となり、「math.floor」関数によって「7」に切り捨てられてしまうためです。
小数どうしの演算は極力避けなければなりません。
mysql_connectはセキュリティの観点からPHP7.0.0で削除され、使用できなくなりました。
レガシーなシステムの保守をする際にmysql_connectが使用されていることが多々ありますが、mysql_connectはPHP5.5.0で非推薦となり、PHP7.0.0で削除されました。
代替としてmysqli_connectやPDOを使用することになりますが、PDOであればデータベースがMySQLでなくても使用できるので、特に理由が無ければPDOで実装します。
近年ではフレームワークを使用してアプリケーションを制作することがほとんどのため、このようにマニュアルでデータベースを扱う機会はほとんど無いかもしれませんが、プロジェクトのビルド時には使用する言語のバージョンやデータベースへのアクセス方法のほか、セキュリティ規約にも気を遣う必要があります。
インライン要素はサイズの指定ができない
HTMLの要素にはインライン要素とブロックレベル要素があり、インライン要素にはwidthやheightなどのサイズの指定ができません。 本コードのようにspanで作成されたインライン要素をボタンのように表示したい場合は、ブロック要素にしたうえでサイズ指定を行う必要があります。
なおインライン要素にはmarginは左右のみ設定可能で、paddingは上下左右の設定が可能です。
データを取りこぼす可能性あり
このコードでは2023年8月5日から同年8月6日の間に作成されたユーザー情報を取得しようとしていますが、実はこの記述の仕方では8月6日に作成されたデータは検索に引っ掛かりません。
これは時刻まで見てみると、下記のような比較式になっているためです。 (から)2023-08-05 00:00:00 (まで)2023-08-06 00:00:00 つまり2023年8月6日の0時0分0.000001秒~同日23時59分59.9999999秒までの結果を取得することができないのです。
これを防ぐには、検索するカラムの日時をDATE関数を使用して日付部分のみを取得してから比較します。
日付の比較はアプリケーションの制作では頻繁に使用する項目ですが、本コードのようにエラーとしてプログラムがストップしないことも多々あり、お客様の指摘があるまでミスに気が付かずご迷惑をお掛けしてしまうことになるため気を付けましょう。
配列の参照渡し
const total = src;でtotalにsrcの参照を渡しています。そのため、totalを変更するとsrcも変更されてしまいます。
これを防ぐには、srcのコピーを作成してtotalに代入する必要があります。
大変申し訳ありませんが、このコードに誤りはありませんでした。
電算機特有の誤差が生じる恐れあり
このコードでは2行目でnumの値が0.3になることを期待していますが、実際にはそうなりません。 これは電算機特有の問題で、0.1 + 0.2の結果が正確に0.3にならず、条件文がfalseとなるためです。
正確に比較するためには、ある程度の誤差を許容する方法をとることができます。
オブジェクトの比較は内容の比較ではない
このJavaScriptコードは、obj1とobj2が等しいかどうかを比較しようとしていますが、期待通りに動作しません。 JavaScriptでは、オブジェクトの比較は参照の比較になり、内容の比較ではありません。そのため、異なるオブジェクトであるobj1とobj2は常に等しくないと判断されます。
修正するには、オブジェクトの内容を比較する方法を使う必要があります。例えば、JSON文字列に変換して比較する方法があります
NaNを正しく比較するには、isNaN関数を使用する
このJavaScriptコードは、aがNaN(Not-a-Number)であるかどうかを比較しようとしていますが、NaNは自身と等しくないという特性があります。 つまり、NaN === NaNは常にfalseとなります。そのため、このコードは'ok'を出力しません。
NaNを正しく比較するには、isNaN関数を使用する必要があります。
文字列の結合には「.」(ピリオド)を使用する
このコードでは配列「$a」に格納されている文字列をすべて結合したものを表示しようとしています。
しかし、9行目で文字列の結合を行うのに「+」を用いているため、正しく文字列を結合できていません。 文字列の結合には「.」(ピリオド)を使用します。
PHPは変数の宣言時に型に対して自由度が非常に高い言語ですが、この自由度の高さが思わぬバグを生じさせる原因となりかねません。 少なくとも一連の処理の中で変数の中身の型を変えないようにするなど、型管理はしっかりと行いましょう。
未定義のオブジェクト(配列)にプロパティ(要素)を追加しようとしている
このJavaScriptコードの問題点は、listが未定義のままプロパティを追加しようとしている点です。list変数はundefinedのままであるため、list.count = 0;を実行するとエラーが発生します。
listを配列またはオブジェクトとして初期化する必要があります。
メソッド引数にselfが無い
msgメソッドはインスタンスメソッドとして定義されていますが、self引数がありません。そのため、インスタンスから呼び出すとエラーが発生します。
このselfは、インスタンス自身を示すものです。
selfはpythonの設計仕様で欠かすことはできません。なお、名前を”self”以外のキーワード(例えば、myselfなど)にすることは可能ですが、慣例としてselfを使用しています。
pythonでは作成されたコードを後から読む人が、どのように実装されているかが一目でわかるように書く事が重要である為、基本的には”self”とするのが良いでしょう。以降では、selfがどのように使われるかを確認します。
先に関数内でaに再代入されてしまう
このPythonコードは、関数exe内で変数aを参照しようとしたときにエラーが発生します。
これは、関数内で同じ名前の変数aが再代入されるため、Pythonはaをローカル変数として認識し、再代入前に参照することができないためです。
修正するには、関数内でグローバル変数aを使用することを明示する必要があります。
シングルクォーテーションの中で変数展開はできない
このコードでは変数「$msg」の中で変数「$a」「$b」の中身を展開して表示しようとしています。
しかしながら、シングルクォーテーションで記述されているため変数は展開されず、そのまま「{$a}」「{$b}」などと表示されてしまいます。 文中で変数を展開したい場合は「'」(シングルクォーテーション)ではなく「"」(ダブルクォーテーション)で囲むようにしましょう。
なお動作処理速度はシングルクォーテーションの方が速く、ダブルクォーテーションに比べて約1.2倍~約2倍ほど違うそうです。
タプル定義エラー
bをタプルとして定義する際に、単一の要素を持つタプルを作成するには、カンマを追加する必要があります。(6,)のように書くと、bはタプルとして認識されます。本コードのままでは、bは整数6として扱われます。
なおaとbを結合する際に、両方がタプルであることを確認する必要があります。
appendメソッドの戻り値はNone
list.append(3)メソッドはリストlistを更新しますが、戻り値はNoneです。そのため、updateにはNoneが代入され、print(update)はNoneを出力します。
SQL構文エラー
このエラーは、SELECT文のリストに含まれるフィールドがGROUP BY句に含まれていない場合に発生します。SELECTリストに含まれるすべてのフィールドをGROUP BY句に追加することで解消します。
NULLが許可されているカラムで何もデータが無いレコードを抽出する場合、「''」(空文字)を検索するだけでは取りこぼしが発生する
このSQLでは「users」テーブルの「mail」カラムにデータが入っていないユーザー情報を検索しようとしています。
一見このSQLクエリによって所望の結果が得られそうですが、mailカラムには「NULL」が許可されており、このNULLが格納されているレコードは「''」(空文字)判定では抽出できません。 NULLの検索には「IS NULL」を使用します。
NULL値の比較に「=」演算子を使用できない
SQLでNULL値の比較を行う際、「=」演算子を使用することができません。
NULL値がある場合は「IS NULL」を、NULL値が無い場合は「IS NOT NULL」を使用します。
メソッドのスコープミス
このコードでは「hiweb」クラス内に「set」および「print」の2つのメソッドがあり、printメソッド内でsetメソッドを呼び出そうとしています。
しかし、setメソッドをスコープ外から呼び出しているためエラーとなります。 正しくは「$this」を使って呼び出す必要があります。
非同期処理が完了する前にデータを利用しようとしている
ajax非同期処理が完了する前にデータを使用しようとすると、問題が発生する可能性があります。
データが利用可能になるまで待つか、コールバック関数内で処理を行いましょう。
関数内で発生した例外を、関数外で認識できない
calc関数内でZeroDivisionErrorをキャッチしてメッセージを表示しますが、例外を再度スローしていないため、外側のtryブロックでキャッチされません。その結果、print("end")が実行されてしまいます。
calc関数がZeroDivisionErrorをキャッチした場合、Noneを返すことになりますが、これは呼び出し元で処理されません。
修正するには、calc関数内で例外を再度スローするか、または外側のtryブロックで直接例外をキャッチする必要があります。
関数内部での変数xが未定義
関数func内で変数xを使用していますが、xは関数の内部で定義されておらず、関数が呼び出されたときにエラーが発生します。
関数内で変数xを使用したい場合は、xをグローバル変数として宣言するか、関数内で初期化する必要があります。
formからの配列としてのデータ送信
このコードではフォーム内に設置されたチェックボックスから複数の値を送信しようとしています。
しかしながらチェックボックスの「name」が「c[]」ではなく単に「c」となっているため、これでは配列形式でデータを送信することができません。 チェックボックスは選択肢の中からユーザーに複数の回答を送信してもらうために設置することがほとんどですので、nameには「[]」を付けて配列形式でデータを送信できるようにします。
2038年問題
PHPにおけるdate関数の使用は、2038年問題を回避するために控えるべきです。
2038年問題とは、date関数を使用した時に、2038年以降の日時がエラーとなる問題です。
Unixタイムスタンプは「符号つき32ビットの整数型」とされています。 そしてその符号つき32ビットの整数型が表現できる数値の範囲は「-2,147,483,647 ~ 2,147,483,647」となっており、上限である「2,147,483,647」が表す日時こそが「2038年1月19日3時14分7秒」なのです。
これを過ぎた場合、数値がオーバーフローしてマイナスの値「-2,147,483,647」となり、これは日時で表すと「1901年12月13日20時45分52秒」となります。 つまり「2038年1月19日3時14分7秒」の1秒後が「1901年12月13日20時45分52秒」となってしまうのです。
この現象はシステムを32ビットで動作させているときに発現しますので、64ビットに対応した環境に入れ替えることにより問題は回避することができます。 また、DateTimeクラスを使用することで回避することができます。特に指定が無い限りはPHPで日時を扱う際にはDateTimeクラスを使用する方が安全と言えます。
header関数の前にHTMLを出力してはいけない
header関数はHTTPヘッダを送信する機能があり、Locationを使用することで指定したURLに遷移させることができます。 このheader関数はすべての出力の前に呼び出す必要があり、本コードのようにHTMLが出力された後では機能しません。
またheader関数の前に不要なスペースや改行があってもHTMLを出力したとみなされ、動作しなくなります。
try文とfinally文の間にexcept文が抜けている
このコードではファイルを開いて内容を読み取った後に必ずファイルを閉じるという機能を記述してあります。tryブロック内でファイル操作を行い、finallyブロックでファイルを閉じることにより、例外が発生した場合でも確実にリソースが解放されます。
ただしこのままではファイルが存在しない場合にエラーが発生する可能性があるため、try文の中にファイルの存在を確認するコードを追加する必要があります。
lambda関数は実行されるまで変数iの値が変わらない
このコードでは、lambda関数がループ変数iの最終的な値を参照しているため、すべてのlambda関数が同じ値を返してしまいます。 つまり、lambda関数は実行されるまで変数iの値が変わらないということです。 この場合、リストfunc内のすべてのlambda関数がiの最終値(4)を返します。そのため、resultリストは[4, 4, 4, 4, 4]になります。
この問題を解決するには、lambda関数がループ変数の現在の値をキャプチャするようにする必要があります。
※ 全40枚(A〜10 × 4スート)の解答を掲載しています。