私のお気に入りのプログラミングミス

プログラマヌずしおのキャリアの䞭で、私はいく぀かの異なる蚀語で膚倧な数の間違いを犯したした。 実際、最初に機胜するコヌドを10行以䞊蚘述するず、疑わしくなり、構文の゚ラヌ、配列ぞの誀った参照、たたは誀っお蚘述された倉数を芋぀けるこずを想定しお、通垞よりも慎重にテストを開始したす。他の䜕か。



これらの゚ラヌを、障害、゚ラヌ、欠点の3぀の倧きなグルヌプに分けたいず思いたす。 倱敗は、画面を芋お愚かに座っお静かに「ああ」ず蚀うずきです。 デヌタベヌスやサむト党䜓を削陀したり、3日間の仕事の結果の䞊に䜕かを曞いたり、誀っお2䞇人に手玙を送ったりずいったこずです。



゚ラヌは異なる堎合がありたす単玔な構文゚ラヌたずえば、忘れる}から重倧な゚ラヌや蚈算の゚ラヌたで。



間違いがあたりにも明癜でわかりにくいためにほが完璧な堎合、私はそれを欠陥ず呌びたす。 これは、コヌドの䞀郚が完党に予枬䞍可胜で非垞にたれな状況に盎面したずきに発生したす。 あなたは埌ろに寄りかかっお、「すごい」ず考えたす。たるで明るい虹や流れ星を芋おいるかのようです。



デバッグモヌドがオフになっおいない


この蚘事で蚀及する最初の2぀の゚ラヌは、完党な倱敗でした。



フリヌランスを始めたずき、デヌタベヌスク゚リ、フォヌム、およびペヌゞテンプレヌトを凊理するためのPHPラむブラリをいく぀か䜜成したした。 デバッグモヌドは、グロヌバル倉数$ DEBUGに䟝存するかなり深いレベルでラむブラリに組み蟌たれたした。



たた、開発、デバッグ、およびテストのために䜜業したすべおの䞻芁なサむトのロヌカルコピヌを保持したした。 したがっお、問題が発生したずきは、垞に$ DEBUG = 1に蚭定できたした。 たずえば、すべおの実行可胜なデヌタベヌスステヌトメントなど、さたざたなこずを教えおくれたした。 私は、オンラむンサむトでこの゚ラヌ怜出方法を䜿甚するこずはほずんどなく、ロヌカルでの䜿甚を目的ずしおいたす。



しかしある日、私は倜遅くたで働いお、ある人気のあるeコマヌスサむトの小さな問題を修正したした。 $ DEBUG = 1に蚭定したした。 いく぀かのペヌゞの䞊郚で、それらの間で切り替えたした。 私の頭の疲れから、すべおが混ざり合い、最終的にはサむトの最も重芁なペヌゞにデバッグ倉数を远加したした。ナヌザヌは「今すぐ支払う」ボタンをクリックするずすぐに衚瀺され、このフォヌムでサむトの䜜業バヌゞョンにアップロヌドされたした。



翌朝、私は早めに家を出お、午埌9時に戻っおきたしたが、留守番電話に12通のメッセヌゞがあり、もう1通は迷惑で、さらに倚くの電子メヌルがありたした。 箄20時間、「今すぐ支払う」をクリックしたナヌザヌは次のようなものを芋たした。

画像

゚ラヌを修正するのにたった10秒しかかかりたせんでしたが、泚文を逃した1日䞭、クラむアントに謝眪するのにより倚くの時間がかかりたした。



孊んだ教蚓


私はこのケヌスを熟考し、次のこずがわかった。

1.深倜に仕事をしない

2.泚文凊理に少しでも倉曎を加えるたびに完党なテストを実斜する

3.デバッグレポヌトがラむブサむトに衚瀺されないようにしたす

4.緊急時の連絡先情報を顧客に提䟛したす。



泚意深いデバッグ


3番目のポむントに関連しお、デバッグメッセヌゞの衚瀺が自分だけに衚瀺されるように、いく぀かの関数を䜜成したした。



 function CanDebug() { global $DEBUG; $allowed = array ('127.0.0.1', '81.1.1.1'); if (in_array ($_SERVER['REMOTE_ADDR'], $allowed)) return $DEBUG; else return 0; } function Debug ($message) { if (!CanDebug()) return; echo '<div style="background:yellow; color:black; border: 1px solid black;'; echo 'padding: 5px; margin: 5px; white-space: pre;">'; if (is_string ($message)) echo $message; else var_dump ($message); echo '</div>'; }
      
      





$蚱可された配列には、ロヌカルテスト甚のIPアドレス127.0.0.1ず倖郚IPが含たれおいたす。

次のように出力できたす



 $DEBUG = 1; Debug ("The total is now $total"); //about a debugging message Debug ($somevariable); //output a variable Debug ("About to run: $query"); //before running any database query mysql_query ($query);
      
      





そしお、私以倖の誰にもデバッグメッセヌゞが衚瀺されないようにしたす。 䞊蚘の倉数が指定されおいる堎合、コヌドは次のようになりたす。

画像

セキュリティを匷化するために、HTMLコメント内に゚ラヌメッセヌゞを転送するこずもできたすが、適切な郚分を芋぀けるには長い間コヌドを掘り䞋げる必芁がありたす。

すべおのPHP通知、譊告、゚ラヌが自分だけに衚瀺されるように、ペヌゞたたは構成ファむルの䞊郚に配眮できる別の䟿利なコヌドがありたす。 ゚ラヌず譊告はログに蚘録されたすが、画面には衚瀺されたせん。



 if (CanDebug()) {ini_set ('display_errors', 1); error_reporting (E_ALL);} else {ini_set ('display_errors', 0); error_reporting (E_ALL & ~E_NOTICE);}
      
      





デバッガヌ


このような方法は、厳密に定矩されたコヌドフラグメントの゚ラヌをすばやく芋぀けるのに䟿利です。 FirePHPやXdebugなど、コヌドに関する豊富な情報を提䟛できるさたざたなデバッグツヌルもありたす。 たた、ナヌザヌに衚瀺せずに、すべおの関数呌び出しをログファむルに䞀芧衚瀺しお、芋えないように動䜜させるこずもできたす。 Xdebugは次のように䜿甚できたす。



 ini_set ('xdebug.collect_params', 1); xdebug_start_trace ('/tmp/mytrace'); echo substr ("This will be traced", 0, 10); xdebug_stop_trace();
      
      





このコヌドは、すべおの関数呌び出しずそのパラメヌタヌを/tmp/mytrace.xtファむルに登録したす。これは次のようになりたす。

画像

Xdebugは、PHPの譊告たたぱラヌに関するより倚くの情報も衚瀺したす。 ただし、サヌバヌにむンストヌルする必芁があるため、ほずんどのホスティング䌚瀟ではこれを実行できない可胜性がありたす。

䞀方、FirePHPは、Firebugアドオンず盞互䜜甚するPHPラむブラリヌずしお機胜したす。 デバッグ情報をPHPからFirebugコン゜ヌルに盎接出力できたす。これもナヌザヌには衚瀺されたせん。

どちらの方法でも、䞊蚘のCanDebugのような関数は、Firebugのすべおの所有者がスタックトレヌスずログファむルの䜜成にアクセスできないようにするのに圹立ちたす。



デバッグモヌドをオフにする


電子メヌルスクリプトのデバッグはより耇雑です。 スクリプトが正しい方法で電子メヌルを送信しおいるかどうかを確認するこずは、実際に電子メヌルを送信するこずなく間違いなく困難です。 それは私がか぀お誀っおやったこずです。



数幎前、私は毎日2䞇人以䞊の加入者に電子メヌルを送信するための倧芏暡な電子メヌルスクリプトを䜜成するように求められたした。 開発䞭に、CanDebug関数に䌌たものを䜿甚しお、電子メヌルを送信せずにスクリプトをテストできたした。 電子メヌル送信機胜は次のようになりたした。



 function SendEmail ($to, $from, $subject, $message) { if (CanDebug() >= 10) Debug ("Would have emailed $to:\n$message"); else { if (CanDebug()) {$subject = "Test to $to: $subject"; $to = "test@test.com";} mail ($to, $subject, $message, "From: $from"); } }
      
      





$ DEBUG = 1に蚭定するず、スクリプトはテストアドレスに電子メヌルすべお2䞇件を送信し、確認できたした。 $ DEBUG = 10に蚭定した堎合、圌は電子メヌルを送信しようずしおいるず通知したしたが、実際には䜕も送信したせんでした。 スクリプトを開始しお間もなく、問題が始たりたした。 圌は、䜎パフォヌマンスの情報凊理が20,000回連続しお実行されたため、メモリ䞍足になったず思いたす。 ある時点で、ある皮の゚ラヌを修正しようずし、$ DEBUG倉数たたは間違った時間に倖郚IPが倉曎されたを忘れお、誀っお2䞇人に手玙を送りたした。



私が働いおいた代理店に謝眪したしたが、幞いなこずに特に圱響はありたせんでした。 おそらく、倚くの文字がスパムフィルタヌによっおブロックされたした。 たたは、受信者は、手玙が空であるこずに単玔に満足しおいたかもしれたせん。



孊んだ教蚓


手玙の䞻題ず内容ずしお単に「テスト」ずいう蚀葉を残したこずは非垞にうれしく、発生したバグに察する䞍満を反映した声明ではありたせん。 必芁なものを孊びたした

1.バルクメヌルスクリプトをテストする堎合は特に泚意しおください-デバッグモヌドが機胜しおいるかどうかを確認しおください。

2.できるだけ少ない人にテストメヌルを送信したす。

3.手玙の本文には、「このテストメッセヌゞを無芖しおください」など、垞に䞁寧なものを曞いおください。 「私のクラむアントはバカです」のようなものを曞くのは望たしくありたせん-疑いを持たない2䞇人の投資家がそれを読むかどうかはわかりたせん。



PHPの空癜ペヌゞ


今、私たちは倱敗の王囜から埮劙な間違いの領域に到達したした。 取り陀くのが難しい゚ラヌを芋たい堎合は、コヌドの深さのどこかに以䞋を埋めおください。



 function TestMe() {TestMe();} TestMe();
      
      





ブラりザずサヌバヌ䞊のApacheずPHPのバヌゞョンによっおは、空癜ペヌゞ、「このWebペヌゞは利甚できたせん」、メモリ䞍足による重倧な゚ラヌ、たたは「保存」たたは「開く」ペヌゞ文が衚瀺される堎合がありたす。

画像

これは本質的に無限再垰を匕き起こし、メモリ䞍足やサヌバヌスレッドクラッシュを匕き起こす可胜性がありたす。 動䜜を停止するず、゚ラヌログに小さなトレヌスが残る堎合がありたす。

[2011幎6月6日18:24:10] [通知]子pid 7192

終了信号セグメンテヌション障害11

これにより、゚ラヌが発生した堎所ず理由がわかりたす。 たた、問題のあるコヌドが実行される限りペヌゞ党䜓が機胜しないため、さたざたな堎所に出力行を远加するすべおのクむックデバッグメ゜ッドは、特別な結果をもたらしたせん。 基本的に、これはPHPが生成されたHTMLを定期的にのみブラりザに送信するためです。 したがっお、倚くの匏を远加するず、flush; 少なくずも、再垰゚ラヌの盎前にスクリプトが䜕をしたかを瀺したす。

もちろん、この゚ラヌの原因ずなるコヌドは、䞊蚘のコヌドよりもはるかに掗緎されおいる堎合がありたす。 元のクラスを参照する他のクラスのクラスを呌び出すメ゜ッドが含たれる堎合がありたす。 そしお、この゚ラヌは、再珟が困難な状況でのみ発生する可胜性がありたす。



孊んだ教蚓


1.䜕かがそこに曞き蟌たれた堎合に備えお、゚ラヌログの堎所を把握したす。

2.このような状況では、Xdebugのようなスタックトレヌスデバッガヌは非垞に䟿利です。

3.それ以倖の堎合は、すべおのコヌドを1行ず぀調べお、機胜するたで䞍芁な郚分をコメントアりトするための時間を倧幅に節玄したす。



無効な倉数タむプ


この゚ラヌは、倚くの堎合デヌタベヌスで発生したす。 これらのSQLステヌトメントが䞎えられた堎合...



 CREATE TABLE products ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(60), category VARCHAR(10), price DECIMAL(6,2) ); INSERT INTO products VALUES (1, 'Great Expectations', 'book', 12.99); INSERT INTO products VALUES (2, 'Meagre Expectations', 'cd', 2.50); INSERT INTO products VALUES (3, 'Flared corduroys', 'retro clothing', 25);
      
      





...以䞋を実行するず䜕が返されるず思いたすか



 SELECT * FROM products WHERE category='retro clothing';
      
      





カテゎリ列の長さは10文字しかないため、答えは䜕もありたせん。そのため、最埌の補品のカテゎリはレトロクロットになりたす。 最近倉曎された補品や新しいメニュヌ項目の突然の消倱は、非垞に混乱する可胜性がありたす。 しかし、通垞これはかなり簡単に修正できたす。



 ALTER TABLE products MODIFY category VARCHAR(30); UPDATE products SET category='retro clothing' WHERE category='retro clot';
      
      





画像

最初の䞻芁なeコマヌスサむトで䜜業しおいるずきに、より深刻なミスを犯したした。 泚文プロセスの最埌に、サむトはクラむアントにクレゞットカヌドの詳现を入力するように芁求し、バヌクレむズePDQシステムに支払い芁求を送信するJavaプログラムを呌び出したした。 金額は幎金で蚈算されたした。 私はJavaに粟通しおいなかったので、基瀎ずしお芋぀かったコヌドサンプルを取りたした。

短い合蚈;

Javaプログラムはコマンドラむンから呌び出されたした。 圌女が䜕も返さなかった堎合、トランザクションは完了したずみなされ、クラむアントは電子メヌルを受信し、泚文は完了したした。 クレゞットカヌドの確認䞭に゚ラヌが発生した堎合、プログラムは「カヌドは認蚌されおいたせん」たたは「カヌドは認蚌に倱敗したした」などのメッセヌゞを返したした。

短敎数には、-32768〜+32767の倀を栌玍できたす。 これらの数字は巚倧に芋えたした。 しかし、私は金額がポンドではなくペンスで蚈算されたずいう事実に泚意を払いたせんでした。぀たり、可胜な最倧額は327.67ポンドでした。 そしお最悪なのは、泚文金額がこれを超えるず、Javaプログラムが機胜しなくなり、䜕も返されないこずです。 泚文が正垞に完了したように芋え、その埌、通垞どおり賌入プロセスが進行したした。 この゚ラヌは、数か月埌の倧量の未払い泚文の埌、経理郚、たたは甚心深く誠実なバむダヌによっお気づかれたした。



孊んだ教蚓


1.デヌタベヌスたたは倉数の列に型を割り圓おる堎合、慎重に行う必芁がありたす。

2.プログラムの正垞な完了が、動䜜を停止するプログラムず異なるこずを確認する必芁がありたす。



「ペニヌの゚ラヌ」


私のお気に入りの間違いは、わずか1ペニヌセント、時代、たたは他のコむンの䞍䞀臎を匕き起こすものです。 远跡が非垞に難しく、しばしば䞞め誀差が生じるため、私はそれらが奜きです。

数幎前、私はお金を衚瀺する1぀のサむト甚の簡単なJavaScript関数を䜜成する必芁がありたした。 私はこれを䜿甚したした



 <script type="text/javascript"> function GetMoney (amount) {return Math.round (amount * 100) / 100;} </script>
      
      





しかし、1.20のような量が1.2の圢で画面に衚瀺されるこずがすぐに明らかになりたした。 だから私はこのようにコヌドを倉曎したした

 <script type="text/javascript"> function GetMoney (amount) { var pounds = Math.floor (amount); var pence = Math.round (amount * 100) % 100; return pounds + '.' + (pence < 10 ? '0' : '') + pence; } </script>
      
      





䞻な違いは、最埌の行の䜙分な0です。 しかし、ペニヌは個別に蚈算されるため、数量を100で割ったずきに残りを取埗するには挔算子が必芁です。このコヌドで゚ラヌが発生する可胜性の䜎い状況を芋぀けおください。

これは、ビヌズを販売したサむトで発生したした。 その埌、ビヌズは、分数の倀を含むカスタムブレンドを含め、さたざたな圢や数量で販売できるこずがわかりたした。 顧客が4.95ポンド盞圓の1.01品目を賌入し、4.00ポンドのみを支払った堎合。 金額は4.9995ず定矩されおいるため、プログラムはペンスを100に䞞め、100は0ペンスを残したした。 したがっお、支払額は4ポンドに枛少したした。

画像

䞞み付けの単玔な欠陥。100個あたり4.95ポンドで販売された101個のビヌズに察しお、5ポンドではなく4ポンドを支払いたした。

私はすぐにコヌドを修正したした



 <script type="text/javascript"> function GetMoney (amount) { var pounds = Math.floor (amount); var pence = Math.floor (amount * 100) % 100; return pounds + '.' + (pence < 10 ? '0' : '') + pence; } </script>
      
      





ただし、これは4.9995ポンドから4.99ポンドに切り䞊げられたため、サヌバヌからの察応する蚈算ずの同期が劚げられたため、適切な修正ではありたせんでした。 さらに悪いこずに、£1.00盞圓の0.7を泚文した堎合、金額は70ペンスから69ペンスになりたした これは、0.7のような浮動小数点数が0.6999999999999999ではなく2進数で衚瀺され、70に䞞める代わりに69ペンスに削枛されるためです。

これは本圓の「䞀ペニヌの間違い」です。 それを修正するために、最初に別の䞞めを远加したした。



 <script type="text/javascript"> function GetMoney (amount) { var pence = Math.round (100 * amount); var pounds = Math.floor (pence / 100); pence %= 100; return pound + '.' + (pence < 10 ? '0' : '') + pence; } </script>
      
      





これで、非垞に単玔な1぀のこずを行うために、非垞に耇雑な4行のコヌドができたした。 今日、私はこの蚘事を曞いおいるずきに、これらすべおを凊理できるJavascriptに組み蟌たれた関数を芋぀けたした。



 <script type="text/javascript"> function GetMoney (amount) {return amount.toFixed (2);} alert (GetMoney (4.9995) + ' ' + GetMoney (0.1 * 0.7)); </script>
      
      







PayPalで割匕を提䟛する


PayPalは「䞀ペニヌの間違い」であり、翌の䞭で埅っおいたす。 倚くのサむトでは、泚文金額の特定の割合の割匕を提䟛するコヌドを提䟛しおいたす。 最埌に蚈算されたす。 95ペンスで2぀のアむテムを泚文した堎合、合蚈金額は£1.90になり、19ペンスの割匕を受けるため、£1.71を支払いたす。

ただし、PayPalはこのタむプの割匕をサポヌトしおいたせん。 PayPalで買い物かごに商品を衚瀺する堎合は、それぞれの商品の䟡栌ず数量を個別に蚈算する必芁がありたす。



 <input name="item_name_1" type="hidden" value="My Difficult Product" /> <input name="amount_1" type="hidden" value="0.99" /> <input name="quantity_1" type="hidden" value="1" />
      
      





したがっお、アむテムごずに個別に割匕を受ける必芁がありたす。 95ペンスから10割匕するず、85.5ペンスになりたす。 PayPalは小数では動䜜しないため、86ペンスに䞞める必芁がありたす。これにより、PayPalの合蚈金額は£1.72になりたす。 85pに切り䞊げた堎合、合蚈金額は£1.70になりたす。

この問題を解決するには、各アむテムの割匕を個別に蚈算する必芁もありたした。 通垞の10×£1.90の蚈算の代わりに、コヌドは、ペンスの党額を䜿甚するたびに、アむテムからアむテムぞの割匕を环積したす。 $アむテムが泚文アむテムのPHP配列であるず仮定したす



 $discount = 0; $discountpercent = 10; foreach ($items as $item) { $mydiscount = floor ($item->price * $discountpercent) / 100; $item->priceforpaypal = $item->price - $mydiscount; $discount += $mydiscount * $item->quantity; }
      
      





孊んだ教蚓


1.単玔な倖芳の非垞に小さな車茪であっおも、車茪を再発明しないでください。

2. 1ペニヌの差がある堎合は、数字がどこでどのように䞞められおいるかを確認したす。

3.可胜であれば、フロヌト倉数で䟡栌を提瀺しないでください。 代わりに、ペンスずセントに敎数を䜿甚し、デヌタベヌスでは固定小数点倉数のタむプDECIMALを䜿甚したす。

時蚈の倉曎


この゚ラヌを「゚ラヌ」ずは呌びたせん。 特別なたれな状況䞋で衚瀺されるため、プログラマヌの芳点からは、これは「欠陥」である可胜性が高くなりたす。 プログラマヌが提䟛できるすべおを超えおいたす。

䞀週間以䞊前に完了した泚文を匷調するこの䞀芋無害なコヌド行の䜕が問題なのか掚枬できたすか



 mysql_query ("SELECT * FROM orders WHERE completeddate < '" . date ('Ymd H:i:s', (time() - 7 * 86400 + 600)) . "'")
      
      





定期的な週次泚文には、システムで同様の行を䜿甚したした。 圌女は先週行われた泚文を遞択し、それらを耇補しお、今週に泚文を出したした。 86,400は1日の秒数であるため、時間-7 * 86400はちょうど1週間前であり、+ 600は10分の空き時間を远加したす。

これは、繰り返し泚文を実装する䜎コストの方法でした。 時間があれば、別のテヌブルや買い物かごを䜜成しお、繰り返される泚文ず繰り返されない泚文を区別したす。 このコヌドは数か月間は問題なく機胜し、3月末には謎の理由で倱敗したこずが刀明したした。

欠陥の解消ずその結果には倚くの時間がかかり、泚文を手動で実行する必芁がありたした。 原因を特定するのにさらに時間がかかりたした。特に、敷地党䜓を別の日だず考えるようにサむト党䜓に匷制する必芁があったためです。

䞀般に、セクションのタむトルでその理由に぀いおすでに説明したした。1週間が7 * 86400秒未満の堎合、時間の倏時間ぞの倉換を考慮するのを忘れおいたした。

次の3぀の方法を比范しお、ちょうど1週間前に取埗したす。 埌者が最も゚レガントです。 私は最近それを発芋したした



 $time = strtotime ('28 March 2011 00:01'); echo date ('Ymd H:i:s', ($time - 7 * 86400)) . '<br/>'; echo date ('Ymd H:i:s', mktime (date ('H', $time), date ('i', $time), 0, date ('n', $time), date ('j', $time) - 7, date ('Y', $time))); echo date ('Ymd H:i:s', (strtotime ('-1 week', $time))) . '<br/>';
      
      





孊んだ教蚓


このような゚ラヌから䞀般的な結論を匕き出すこずは困難ですが、特定の教蚓が埗られたした。

1.繰り返しの倚いサむトでは、タむムゟヌンずクロックを考慮するこずを忘れないでください。

2.繰り返したすが、車茪を再発明しないでください。



おわりに


プログラミングの゚ラヌにはさたざたな圢ずサむズがありたす。 この蚘事では、それらは非垞に明癜な倱敗から信じられないほど埮劙な誀算にたで及びたす。 そしお、圌ら党員がマヌフィヌの法則を確認しおいるようです。トラブルが起こる可胜性がある堎合、それは起こりたす。



ただし、怜出、報告、修正される゚ラヌごずに、コヌドにいく぀かの゚ラヌが残る堎合がありたす。 それらが芋぀からない可胜性がありたす原因ずなる異垞な状態が再珟されおいないため、それらは報告されない可胜性がありたすほずんどのナヌザヌが゚ラヌレポヌトを送信するこずに煩わされないため、たたは䞍必芁なお金や時間のために修正されないコスト。



さらに、゚ラヌは人気のあるサむトでより䞀般的です-䞻に倚くの人々によっお䜜られおいるためですが、1぀の゚ラヌを修正するず他の堎所に新しい゚ラヌが珟れる可胜性があるためです。 したがっお、先を芋越しお、゚ラヌを泚意深く修正する必芁がありたす。



All Articles