ANSWER

バグコードトランプ 解答

各カードのバグ箇所・エラー内容・修正例をすべて公開しています。プログラミング学習にもご活用ください。

PHP Python HTML MySQL JavaScript

弊社商品バグコードトランプ「bugCode:Cards」をお買い上げいただき、誠にありがとうございます。

本ページでは、カードに記載されたコードのうちどこに不具合の原因があるのか、そしてそれら不具合に対する解説と修正例を記載しています。

皆様のプログラミング学習や、レクリエーション等に活用いただければ幸いです。

お知らせとお詫び

コードが記載されているトランプカードのうち

クラブ 8

につきましては、弊社による検証の結果

バグの原因となるコードが含まれておりませんでした。

ご期待頂いたにも関わらずこのような不備がありましたことを深くお詫び申し上げます。

SPADE A
PHP
1 $stmt = $dbh->prepare('
2 insert into sample1 (
3 value1, value2,
4 )values(
5 :value1, :value2,
6 )');
7
8 $stmt->execute(array(
9 ':value1' => 12,
10 ':value2' => 34
11 ));
12 print('Complete');

SQL構文エラー

エラー表示例
Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')values( '12', '34', )

このコードでは「sample1」テーブルの「value1」、「value2」カラムにそれぞれデータを格納しようとしていますが、SQL文法ミスが発生しています。

エラー原因は複数カラム指定時のカンマ(,)が多いためです。 単純なミスですが、「後でカラムが増えるかもしれないから」と言ってカンマを付けたままにしていると、大量の文字の中から小さなカンマを見つけるのに時間が掛かりますので気を付けましょう。

修正例
1 $stmt = $dbh->prepare('
2 insert into sample1 (
3 value1, value2
4 )values(
5 :value1, :value2
6 )');
7
8 $stmt->execute(array(
9 ':value1' => 12,
10 ':value2' => 34
11 ));
12 print('Complete');
SPADE 2
PHP
1 <!-- HTML -->
2 <input
3 type="file"
4 name="thumb">
5
6 <php
7 $dir = '/img/uploads';
8 move_uploaded_file(
9 $_FILES['img']['tmp_name'],
10 $dir.$_FILES['img']['name']
11 );
12 ?>

PHPでの受け取り名前記述ミス

エラー表示例
Undefined array key "img"

このコードでは何らかのサムネイル画像のようなファイルをフォームによって送受信しようとしています。

エラー原因は4行目にてinputのnameを「thumb」と設定しているにも関わらず、受け取り側のPHPコードでは「img」という名前でデータを受信しようとしているためエラーが発生しています。

修正例
1 <!-- HTML -->
2 <input
3 type="file"
4 name="thumb">
5
6 <php
7 $dir = '/img/uploads';
8 move_uploaded_file(
9 $_FILES['thumb']['tmp_name'],
10 $dir.$_FILES['thumb']['name']
11 );
12 ?>
SPADE 3
Python
1 price = 500
2 change = 0
3
4 for i in range(1, 10):
5 if i % 5 == 0:
6 change += price
7 if i == 10:
8 print(change)

ループ範囲が間違っている

range(1, 10)は1から9までの整数を生成しますが、i == 10という条件はループ内で決して真にならないため、print(chage)が実行されません。 そのためif i == 10の条件が無意味になっています。

ループ範囲をrange(1, 11)を使って拡大する必要があります。

修正例
1 price = 500
2 change = 0
3
4 for i in range(1, 10):
5 if i % 5 == 0:
6 change += price
7 if i == 11:
8 print(change)
SPADE 4
PHP
1 $a = number_format(1234);
2 $b = number_format(5678);
3 $ans = $a + $b;
4 $msg = 'Sales: ' .$ans;
5
6 echo $msg;

数値計算の前に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は変数の宣言時に型の明示が必要なく他の言語に比べて比較的自由度が高い印象がありますが、この性質が厄介な場面があり、本事例がまさに良い例と言えるでしょう。 エラーが出て処理が中断しないと、間違いの場所を特定するのに時間が掛かってしまいますね。

修正例
1 $a = 1234;
2 $b = 5678;
3 $ans = $a + $b;
4 $msg = 'Sales: ' .number_format($ans);
5
6 echo $msg;
SPADE 5
Python
1 a = '1,234,567'
2 b = 1234567
3 ans = a + b
4 print(ans)

異なるデータ型で計算しようとしている

aは文字列でbは整数です。異なるデータ型を直接足し合わせることは出来ません。

aを整数に変換することで計算することができるようになりますが、型管理は確実に行いましょう。

修正例
1 a = '1,234,567'
2 b = 1234567
+ a_int = int(a.replace(',', ''))
3 ans = a_int + b
4 print(ans)
SPADE 6
PHP
1 $a = array();
2 $a['flag'] = 'ok';
3 if($a['flag']['a'] == 1){
4 echo $a['flag'];
5 }

配列の要素へのアクセス失敗

エラー表示例
Uncaught TypeError: Cannot access offset of type string on string

このコードでは3行目にて2次元連想配列「$a['flag']['a']」に「1」が格納されている場合に「ok」という表示をするのが目的です。 しかしながら2行目にて1次元連想配列「$a['flag']」に対して文字列「'ok'」を格納しているため、この文字列に対して配列の処理を行う形になっておりエラーとなっています。

修正例
1 $a = array();
2 $a['flag'] = true;
3 if($a['flag'] === true){
4 echo $a['flag'];
5 }
SPADE 7
PHP
1 # cron
2 * * 8 * * php /app/cr.php
3
4 <?php
5 // cr.php
6 require_once('../ex.php')
7 $msg = 'cron-OK';
8 echo $msg;
9 ?>

cronでのPHPプログラム実行時に他ファイルの参照を行う際、絶対パスを用いて指定する

このコードではcronを用いて毎日8時に「cr.php」を実行しようとしていますが、「cr.php」から「ex.php」を参照しています。

cron実行時とPHP実行時ではワークディレクトリ(プログラムを実行する際のホームディレクトリ)が異なることが大半であり、 その場合cronおよびPHPから見た「ex.php」はそれぞれ場所の指定が違うためエラーとなることが多いです。

これを防止するため、cronでのPHPプログラム実行時に他ファイルの参照を行う際は絶対パスを用いて指定するか、 もしくはPHP上でワークディレクトリをそのPHPファイルが置かれている場所に持っていくのが良いでしょう。 下記修正コードでは後者を採用しています。

修正例
1 # cron
2 * * 8 * * php /app/cr.php
3
4 <?php
5 // cr.php
+ chdir(__DIR__);
6 require_once('../ex.php')
7 $msg = 'cron-OK';
8 echo $msg;
9 ?>
SPADE 8
HTML
1 <form action="edit">
2 <input type="text">
3 <input type="mail">
4 <form action="create">
5 <input type="text">
6 <input type="submit">
7 </form>
8 <input type"submit">
9 </form>

form要素の入れ子は禁止

このコードではform要素が入れ子状態になっており、望み通りの挙動をしない可能性が高くなっています。

form要素の入れ子は禁止されています。 この場合、データの送信先は「create」ではなく「edit」となってしまい、場合によっては既に格納されている情報に重大な損失を与えてしまう可能性があります。

修正例
1 <form action="edit">
2 <input type="text">
3 <input type="mail">
4 <input type"submit">
5 </form>
6 <form action="create">
7 <input type="text">
8 <input type="submit">
9 </form>
SPADE 9
HTML
1 <a id="btn">OK</a>
2 <input type="hidden"
3 id="1st">
4 <script>
5 $(document).on(
6 'click', '#btn',
7 function(){
8 $('#1st').attr(
9 'value','ok'
10 );
11 }
12 );
13 </script>

HTMLのidとcssの命名時、最初の文字に数字は不可

HTMLにおいて、idとクラス名には命名するにあたり以下のようなルールがあります。

・半角英数字記号のみを使用する。 ・使用できる記号は「-」(ハイフン)または「_」(アンダースコア)のみ ・機種依存文字を使用してはいけない😥 ・スペースは使用しない ・1文字目はアルファベットから開始する

また、idについては追加で下記のようなルールがあります。

・id名は1つのページに1つだけ ・複数のid名を同じ要素につけることはできない

修正例
1 <a id="btn">OK</a>
2 <input type="hidden"
3 id="first">
4 <script>
5 $(document).on(
6 'click', '#btn',
7 function(){
8 $('#1st').attr(
9 'value','ok'
10 );
11 }
12 );
13 </script>
SPADE 10
PHP
1 $sql = "
2 select * from sample1
3 ";
4 $stmt = $db->query($sql);
5 $re = $stmt->fetchAll(
6 PDO::FETCH_ASSOC
7 );
8 $ans = $re['n1']+$re['n2'];
9 echo($ans);

PDOによりデータベースからデータを取り出す方法(フェッチパターン)に注意

このコードでは「sample1」テーブルの「n1」、「n2」カラムに格納されている数値を足し合わせるのが目的です。

しかしながら5行目で「fetchAll」とあるように、条件にあるデータをすべて取り出してカラム名をキーとする連想配列として構成する命令が記述されています。 すなわち配列「$re['n1']」「$re['n2']」の中身はどちらも配列となっているため、8行目では配列同士を加算処理することになり、結果はエラーではなく「0」と表示されてしまいます。

以下の修正コードは、条件に合致した「最初の1行」だけを取り出すことにより配列「$re['n1']」「$re['n2']」の中身を単一の値として構成しました。 もし「fetchAll」を使用して該当するデータすべてに加算処理を行いたい場合は「foerach」を使用して全要素に繰り返し同じ処理を行うか、結果として取得した配列の中身がすべて数値と分かっている場合に限り「array_sum」関数を使用するのも良いでしょう。

修正例
1 $sql = "
2 select * from sample1
3 ";
4 $stmt = $db->query($sql);
5 $re = $stmt->fetch(
6 PDO::FETCH_ASSOC
7 );
8 $ans = $re['n1']+$re['n2'];
9 echo($ans);
CLUB A
PHP
1 $a = null;
2 $b = 0;
3 if(
4 is_null($a) && is_null($b)
5 ){
6 echo 'success';
7 }

「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
修正例
1 $a = null;
2 $b = 0;
3 if(
4 is_null($a) && empty($b)
5 ){
6 echo 'success';
7 }
CLUB 2
PHP
1 $a = 'success';
2
3 function sample() {
4 echo $a;
5 }
6
7 sample();

変数のスコープミス

PHPの変数にはローカル変数とグローバル変数という2種類の変数があり、特にPHPではどちらの変数も対象の範囲外からアクセスすることはできません。

このコードでは関数外で宣言されたグローバル変数「$a」を関数内のローカル変数としても使用しようとしており、このままでは関数「sample」をいくら実行しても何も表示することはできません。

関数の中で関数外の値を使うには引数として値を渡してあげるか、関数内にて関数外の変数を参照するグローバル宣言をすることがあります。 下記の修正されたコードでは後者のパターンを採用しています。

修正例
1 $a = 'success';
2
3 function sample() {
+ global $a;
4 echo $a;
5 }
6
7 sample();
CLUB 3
Python
1 import math
2
3 a = 0.1
4 b = 0.7
5 ans = math.floor((a+b)*10)
6
7 print(ans)

電算機特有の誤差が生じる恐れあり

このコードは正しく動作しているように見えますが、計算上の誤差の影響を受ける可能性があります。 実際、これを実行すると期待している「8」ではなく「7」が表示されてしまいます。

浮動小数点演算では、0.1や0.7という値を正確に表現できないため、「a + b」の計算結果が「0.7999999999999999」となり、それを10倍して「7.999999999999999」となり、「math.floor」関数によって「7」に切り捨てられてしまうためです。

小数どうしの演算は極力避けなければなりません。

CLUB 4
PHP
1 // PHP7.0
2 $host = 'sample.com';
3 $user = 'admin';
4 $pass = 'pAssw0rD$aMp1e';
5
6 $link = mysql_connect(
7 $host,
8 $user,
9 $pass
10 );

mysql_connectはセキュリティの観点からPHP7.0.0で削除され、使用できなくなりました。

レガシーなシステムの保守をする際にmysql_connectが使用されていることが多々ありますが、mysql_connectはPHP5.5.0で非推薦となり、PHP7.0.0で削除されました。

代替としてmysqli_connectやPDOを使用することになりますが、PDOであればデータベースがMySQLでなくても使用できるので、特に理由が無ければPDOで実装します。

近年ではフレームワークを使用してアプリケーションを制作することがほとんどのため、このようにマニュアルでデータベースを扱う機会はほとんど無いかもしれませんが、プロジェクトのビルド時には使用する言語のバージョンやデータベースへのアクセス方法のほか、セキュリティ規約にも気を遣う必要があります。

修正例
1 // PHP7.0
2 $host = 'mysql:dbname=sample1;host=sample.com;charset=utfmb4';
3 $user = 'admin';
4 $pass = 'pAssw0rD$aMp1e';
5
6 $link = new PDO(
7 $host,
8 $user,
9 $pass
10 );
CLUB 5
HTML
1 /* css */
2 .sec {
3 height: 100px;
4 background: #f6e;
5 }
6 <!-- html -->
7 <h1>This is
8 <span class="sec">2nd</span>
9 anniversary
10 </h1>

インライン要素はサイズの指定ができない

HTMLの要素にはインライン要素とブロックレベル要素があり、インライン要素にはwidthやheightなどのサイズの指定ができません。 本コードのようにspanで作成されたインライン要素をボタンのように表示したい場合は、ブロック要素にしたうえでサイズ指定を行う必要があります。

なおインライン要素にはmarginは左右のみ設定可能で、paddingは上下左右の設定が可能です。

修正例
1 /* css */
2 .sec {
+ display: block;
3 height: 100px;
4 background: #f6e;
5 }
6 <!-- html -->
7 <h1>This is
8 <span class="sec">2nd</span>
9 anniversary
10 </h1>
CLUB 6
MySQL
1 /*
2 entrytime: DATETIME
3 */
4 SELECT * FROM users
5 WHERE
6 entrytime
7 BETWEEN
8 '2023-08-05'
9 AND
10 '2023-08-06'

データを取りこぼす可能性あり

このコードでは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関数を使用して日付部分のみを取得してから比較します。

日付の比較はアプリケーションの制作では頻繁に使用する項目ですが、本コードのようにエラーとしてプログラムがストップしないことも多々あり、お客様の指摘があるまでミスに気が付かずご迷惑をお掛けしてしまうことになるため気を付けましょう。

修正例
1 /*
2 entrytime: DATETIME
3 */
4 SELECT * FROM users
5 WHERE
6 DATE(entrytime)
7 BETWEEN
8 '2023-08-05'
9 AND
10 '2023-08-06'
CLUB 7
JavaScript
1 const a = ['a','b','c'];
2 const b = ['d'];
3
4 function box(src, add){
5 const total = src;
6 for(const val of add){
7 total.push(val);
8 }
9 return [src,total];
10 }
11 let ans = box(a,b);
12 alert('src:'+ans[0]);
13 alert('total:'+ans[1]);

配列の参照渡し

const total = src;でtotalにsrcの参照を渡しています。そのため、totalを変更するとsrcも変更されてしまいます。

これを防ぐには、srcのコピーを作成してtotalに代入する必要があります。

修正例
1 const a = ['a','b','c'];
2 const b = ['d'];
3
4 function box(src, add){
5 const total = [...src]; // 配列のコピーを作成
6 for(const val of add){
7 total.push(val);
8 }
9 return [src,total];
10 }
11 let ans = box(a,b);
12 alert('src:'+ans[0]);
13 alert('total:'+ans[1]);
CLUB 8
JavaScript
1 first = [
2 'Hi-web','2nd-anniv'
3 ];
4 second = ['thankYou'];
5 function countStr(a,b){
6 var a_len = a.length;
7 var b_len = b.length;
8 return a_len+b_len;
9 }
10 console.log(
11 countStr(first,second)
12 );
お詫び

大変申し訳ありませんが、このコードに誤りはありませんでした。

CLUB 9
JavaScript
1 const num = 0.1 + 0.2;
2 if(num === 0.3){
3 console.log('ok');
4 }

電算機特有の誤差が生じる恐れあり

このコードでは2行目でnumの値が0.3になることを期待していますが、実際にはそうなりません。 これは電算機特有の問題で、0.1 + 0.2の結果が正確に0.3にならず、条件文がfalseとなるためです。

正確に比較するためには、ある程度の誤差を許容する方法をとることができます。

修正例
1 const num = 0.1 + 0.2;
2 if (Math.abs(num - 0.3) < Number.EPSILON){
3 console.log('ok');
4 }
CLUB 10
JavaScript
1 const obj1 = {a: 1};
2 const obj2 = {a: 1};
3 if(obj1 === obj2){
4 console.log('ok');
5 }

オブジェクトの比較は内容の比較ではない

このJavaScriptコードは、obj1とobj2が等しいかどうかを比較しようとしていますが、期待通りに動作しません。 JavaScriptでは、オブジェクトの比較は参照の比較になり、内容の比較ではありません。そのため、異なるオブジェクトであるobj1とobj2は常に等しくないと判断されます。

修正するには、オブジェクトの内容を比較する方法を使う必要があります。例えば、JSON文字列に変換して比較する方法があります

修正例
1 const obj1 = {a: 1};
2 const obj2 = {a: 1};
3 if (JSON.stringify(obj1) === JSON.stringify(obj2)) {
4 console.log('ok');
5 }
DIAMOND A
JavaScript
1 const a = NaN;
2 if(a === NaN){
3 console.log('ok');
4 }

NaNを正しく比較するには、isNaN関数を使用する

このJavaScriptコードは、aがNaN(Not-a-Number)であるかどうかを比較しようとしていますが、NaNは自身と等しくないという特性があります。 つまり、NaN === NaNは常にfalseとなります。そのため、このコードは'ok'を出力しません。

NaNを正しく比較するには、isNaN関数を使用する必要があります。

修正例
1 const a = NaN;
2 if(isNaN(a)){
3 console.log('ok');
4 }
DIAMOND 2
PHP
1 $a = [
2 'Happy',
3 '2ndAnniv',
4 'Hi-web'
5 ];
6 function concat($arr){
7 $msg = null;
8 foreach($arr as $value){
9 $msg += $value;
10 }
11 return $msg;
12 }
13 echo concat($a);

文字列の結合には「.」(ピリオド)を使用する

エラー表示例
Uncaught TypeError: Unsupported operand types: null + string

このコードでは配列「$a」に格納されている文字列をすべて結合したものを表示しようとしています。

しかし、9行目で文字列の結合を行うのに「+」を用いているため、正しく文字列を結合できていません。 文字列の結合には「.」(ピリオド)を使用します。

PHPは変数の宣言時に型に対して自由度が非常に高い言語ですが、この自由度の高さが思わぬバグを生じさせる原因となりかねません。 少なくとも一連の処理の中で変数の中身の型を変えないようにするなど、型管理はしっかりと行いましょう。

修正例
1 $a = [
2 'Happy',
3 '2ndAnniv',
4 'Hi-web'
5 ];
6 function concat($arr){
7 $msg = null;
8 foreach($arr as $value){
9 $msg .= $value;
10 }
11 return $msg;
12 }
13 echo concat($a);
DIAMOND 3
JavaScript
1 var list;
2 list.count = 0;
3 console.log(list);

未定義のオブジェクト(配列)にプロパティ(要素)を追加しようとしている

このJavaScriptコードの問題点は、listが未定義のままプロパティを追加しようとしている点です。list変数はundefinedのままであるため、list.count = 0;を実行するとエラーが発生します。

listを配列またはオブジェクトとして初期化する必要があります。

修正例
1 var list = {}; // または var list = [];
2 list.count = 0;
3 console.log(list);
DIAMOND 4
Python
1 class Hiweb:
2 def msg():
3 print('Hello!')
4
5 disp = Hiweb()
6 disp.msg()

メソッド引数にselfが無い

msgメソッドはインスタンスメソッドとして定義されていますが、self引数がありません。そのため、インスタンスから呼び出すとエラーが発生します。

このselfは、インスタンス自身を示すものです。

selfはpythonの設計仕様で欠かすことはできません。なお、名前を”self”以外のキーワード(例えば、myselfなど)にすることは可能ですが、慣例としてselfを使用しています。

pythonでは作成されたコードを後から読む人が、どのように実装されているかが一目でわかるように書く事が重要である為、基本的には”self”とするのが良いでしょう。以降では、selfがどのように使われるかを確認します。

修正例
1 class Hiweb:
2 def msg(self):
3 print('Hello!')
4
5 disp = Hiweb()
6 disp.msg()
DIAMOND 5
Python
1 a = 'This is global'
2
3 def exe():
4 print(f"1st: {a}")
5 a = 'update this'
6 print(f"2nd: {a}")
7
8 exe()

先に関数内でaに再代入されてしまう

エラー表示例
UnboundLocalError: local variable 'a' referenced before assignment

このPythonコードは、関数exe内で変数aを参照しようとしたときにエラーが発生します。

これは、関数内で同じ名前の変数aが再代入されるため、Pythonはaをローカル変数として認識し、再代入前に参照することができないためです。

修正するには、関数内でグローバル変数aを使用することを明示する必要があります。

修正例
1 a = 'This is global'
2
3 def exe():
+ global a
4 print(f"1st: {a}")
5 a = 'update this'
6 print(f"2nd: {a}")
7
8 exe()
DIAMOND 6
PHP
1 $a = '2nd';
2 $b = 'Hi-web';
3
4 $msg = '
5 {$a} Anniv.
6 {$b}!
7 ';
8 print $msg;

シングルクォーテーションの中で変数展開はできない

このコードでは変数「$msg」の中で変数「$a」「$b」の中身を展開して表示しようとしています。

しかしながら、シングルクォーテーションで記述されているため変数は展開されず、そのまま「{$a}」「{$b}」などと表示されてしまいます。 文中で変数を展開したい場合は「'」(シングルクォーテーション)ではなく「"」(ダブルクォーテーション)で囲むようにしましょう。

なお動作処理速度はシングルクォーテーションの方が速く、ダブルクォーテーションに比べて約1.2倍~約2倍ほど違うそうです。

修正例
1 $a = '2nd';
2 $b = 'Hi-web';
3
4 $msg = "
5 {$a} Anniv.
6 {$b}!
7 ";
8 print $msg;
DIAMOND 7
Python
1 a = (1,2,3,4,5)
2 b = (6)
3 ans = a + b
4
5 print(ans)

タプル定義エラー

bをタプルとして定義する際に、単一の要素を持つタプルを作成するには、カンマを追加する必要があります。(6,)のように書くと、bはタプルとして認識されます。本コードのままでは、bは整数6として扱われます。

なおaとbを結合する際に、両方がタプルであることを確認する必要があります。

修正例
1 a = (1,2,3,4,5)
2 b = (6,)
3 ans = a + b
4
5 print(ans)
DIAMOND 8
Python
1 list = [1,2]
2 update = list.append(3)
3
4 print(update)

appendメソッドの戻り値はNone

list.append(3)メソッドはリストlistを更新しますが、戻り値はNoneです。そのため、updateにはNoneが代入され、print(update)はNoneを出力します。

修正例
1 list = [1,2]
2 list.append(3)
3
4 print(list)
DIAMOND 9
MySQL
1 SELECT
2 *
3 FROM users
4 GROUP BY
5 column

SQL構文エラー

エラー表示例
Expression #1 of SELECT list is not in GROUP BY clause

このエラーは、SELECT文のリストに含まれるフィールドがGROUP BY句に含まれていない場合に発生します。SELECTリストに含まれるすべてのフィールドをGROUP BY句に追加することで解消します。

修正例
1 SELECT
2 u.column, u.*
3 FROM users as u
4 GROUP BY
5 column
DIAMOND 10
MySQL
1 /*
2 mail: VARCHAR NULL
3 */
4 SELECT
5 *
6 FROM users
7 WHERE
8 mail = ''

NULLが許可されているカラムで何もデータが無いレコードを抽出する場合、「''」(空文字)を検索するだけでは取りこぼしが発生する

このSQLでは「users」テーブルの「mail」カラムにデータが入っていないユーザー情報を検索しようとしています。

一見このSQLクエリによって所望の結果が得られそうですが、mailカラムには「NULL」が許可されており、このNULLが格納されているレコードは「''」(空文字)判定では抽出できません。 NULLの検索には「IS NULL」を使用します。

修正例
1 /*
2 mail: VARCHAR NULL
3 */
4 SELECT
5 *
6 FROM users
7 WHERE
8 mail = '' OR mail IS NULL
HEART A
MySQL
1 SELECT
2 *
3 FROM
4 users
5 WHERE
6 phone = NULL

NULL値の比較に「=」演算子を使用できない

SQLでNULL値の比較を行う際、「=」演算子を使用することができません。

NULL値がある場合は「IS NULL」を、NULL値が無い場合は「IS NOT NULL」を使用します。

修正例
1 SELECT
2 *
3 FROM
4 users
5 WHERE
6 phone IS NULL
HEART 2
PHP
1 class hiweb{
2 function set(){
3 return 'Hi-web';
4 }
5 function print(){
6 echo 'Go '.set();
7 }
8 }
9 $msg = new hiweb;
10 $msg -> print();

メソッドのスコープミス

エラー表示例
Uncaught Error: Call to undefined function set()

このコードでは「hiweb」クラス内に「set」および「print」の2つのメソッドがあり、printメソッド内でsetメソッドを呼び出そうとしています。

しかし、setメソッドをスコープ外から呼び出しているためエラーとなります。 正しくは「$this」を使って呼び出す必要があります。

修正例
1 class hiweb{
2 function set(){
3 return 'Hi-web';
4 }
5 function print(){
6 echo 'Go '.$this->set();
7 }
8 }
9 $msg = new hiweb;
10 $msg -> print();
HEART 3
JavaScript
1 $.ajax({
2 url: 'app/show.php',
3 type: 'POST',
4 timeout: 10000,
5 success: function(data){
6 $('#id').html(data);
7 }
8 });

非同期処理が完了する前にデータを利用しようとしている

ajax非同期処理が完了する前にデータを使用しようとすると、問題が発生する可能性があります。

データが利用可能になるまで待つか、コールバック関数内で処理を行いましょう。

修正例
1 $.ajax({
2 url: 'app/show.php',
3 type: 'POST',
4 timeout: 10000,
- success: function(data){
- $('#id').html(data);
- }
+ })
+ .then(
+ function(data){
+ $('#id').html(data);
+ },
+ function(){
+ alert('失敗');
+ }
+ );
- });
HEART 4
Python
1 def calc(n,inp):
2 try:
3 ans = n / inp
4 return ans
5 except ZeroDivisionError:
6 print("zero err")
7
8 try:
9 print("start")
10 calc(5,0)
11 print("end")
12 except:
13 print("err on func")

関数内で発生した例外を、関数外で認識できない

calc関数内でZeroDivisionErrorをキャッチしてメッセージを表示しますが、例外を再度スローしていないため、外側のtryブロックでキャッチされません。その結果、print("end")が実行されてしまいます。

calc関数がZeroDivisionErrorをキャッチした場合、Noneを返すことになりますが、これは呼び出し元で処理されません。

修正するには、calc関数内で例外を再度スローするか、または外側のtryブロックで直接例外をキャッチする必要があります。

修正例
1 def calc(n,inp):
2 try:
3 ans = n / inp
4 return ans
5 except ZeroDivisionError:
6 print("zero err")
+ raise # 例外を再度スローする
7
8 try:
9 print("start")
10 calc(5,0)
11 print("end")
12 except:
13 print("err on func")
HEART 5
Python
1 def func():
2 print(x)
3 x += 1
4 print(x)
5
6 func()

関数内部での変数xが未定義

エラー表示例
UnboundLocalError: local variable 'x' referenced before assignment

関数func内で変数xを使用していますが、xは関数の内部で定義されておらず、関数が呼び出されたときにエラーが発生します。

関数内で変数xを使用したい場合は、xをグローバル変数として宣言するか、関数内で初期化する必要があります。

修正例
+ x = 0 # グローバル変数として初期化
1 def func():
+ global x # グローバル変数を参照
2 print(x)
3 x += 1
4 print(x)
5
6 func()
HEART 6
PHP
1 <input
2 type="checkbox"
3 name="c" value="1">
4 <input
5 type="checkbox"
6 name="c" value="2">
7
8 <?php
9 $c = $_POST['c'];
10 foreach($c as $val){
11 print $val;
12 }
13 ?>

formからの配列としてのデータ送信

このコードではフォーム内に設置されたチェックボックスから複数の値を送信しようとしています。

しかしながらチェックボックスの「name」が「c[]」ではなく単に「c」となっているため、これでは配列形式でデータを送信することができません。 チェックボックスは選択肢の中からユーザーに複数の回答を送信してもらうために設置することがほとんどですので、nameには「[]」を付けて配列形式でデータを送信できるようにします。

修正例
1 <input
2 type="checkbox"
3 name="c[]" value="1">
4 <input
5 type="checkbox"
6 name="c[]" value="2">
7
8 <?php
9 $c = $_POST['c'];
10 foreach($c as $val){
11 print $val;
12 }
13 ?>
HEART 7
PHP
1 <input
2 type="date"
3 name="start_date">
4
5 <?php
6 $d = $_POST['start_date'];
7 $disp = date('Y-m-d', $d);
8 print $disp;
9 ?>

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クラスを使用する方が安全と言えます。

修正例
1 <input
2 type="date"
3 name="start_date">
4
5 <?php
6 $d = $_POST['start_date'];
7 $disp = new DateTime($d);
8 print $disp->format('Y-m-d');
9 ?>
HEART 8
PHP
1 <html>
2 <body>
3 <?php
4 $p = $_GET['p'];
5 if(!empty($page)){
6 print 'OK';
7 }else{
8 header(
9 "Location: {$p}"
10 );
11 }
12 ?>
13 </body>
14 </html>

header関数の前にHTMLを出力してはいけない

header関数はHTTPヘッダを送信する機能があり、Locationを使用することで指定したURLに遷移させることができます。 このheader関数はすべての出力の前に呼び出す必要があり、本コードのようにHTMLが出力された後では機能しません。

またheader関数の前に不要なスペースや改行があってもHTMLを出力したとみなされ、動作しなくなります。

修正例
- <html>
- <body>
3 <?php
4 $p = $_GET['p'];
5 if(!empty($page)){
6 print 'OK';
7 }else{
8 header(
9 "Location: {$p}"
10 );
11 }
12 ?>
+ <html>
+ <body>
13 </body>
14 </html>
HEART 9
Python
1 try:
2 file = open('ex.txt', 'r')
3 content = file.read()
4 ###
5 finally:
6 file.close()

try文とfinally文の間にexcept文が抜けている

このコードではファイルを開いて内容を読み取った後に必ずファイルを閉じるという機能を記述してあります。tryブロック内でファイル操作を行い、finallyブロックでファイルを閉じることにより、例外が発生した場合でも確実にリソースが解放されます。

ただしこのままではファイルが存在しない場合にエラーが発生する可能性があるため、try文の中にファイルの存在を確認するコードを追加する必要があります。

修正例
1 try:
2 file = open('ex.txt', 'r')
3 content = file.read()
4 ###
+ except FileNotFoundError:
+ print("ファイルが見つかりません")
5 finally:
6 file.close()
HEART 10
Python
1 func = []
2 for i in range(5):
3 func.append(lambda: i)
4
5 result = [f() for f in func]
6 print(result)

lambda関数は実行されるまで変数iの値が変わらない

このコードでは、lambda関数がループ変数iの最終的な値を参照しているため、すべてのlambda関数が同じ値を返してしまいます。 つまり、lambda関数は実行されるまで変数iの値が変わらないということです。 この場合、リストfunc内のすべてのlambda関数がiの最終値(4)を返します。そのため、resultリストは[4, 4, 4, 4, 4]になります。

この問題を解決するには、lambda関数がループ変数の現在の値をキャプチャするようにする必要があります。

修正例
1 func = []
2 for i in range(5):
3 func.append(lambda i=i: i)
4
5 result = [f() for f in func]
6 print(result)

※ 全40枚(A〜10 × 4スート)の解答を掲載しています。

CONTACT

ITのこと、まずはお気軽にご相談ください。

「何から始めればいいか分からない」も大歓迎。
お話を伺い、最適な進め方を一緒に考えます。