この記事は、Deji AkalaとMarco Pivettaによって査読されました。 SitePointで最高のコンテンツを取得するためにSetePointのすべてのピアレビューアに感謝します!
お互いのコードとその使用方法を理解する方法は、中程度のチームと大規模なチームが同じコードベースで協力すると困難になる場合があります。この目的のために、複数のソリューションがあります。たとえば、コードの読みやすさを改善するために一連のコーディング標準に従って、すべてのチームメンバーに馴染みのあるフレームワークを使用することに同意することができます(優れたLaravelエントリレベルのコースはこちらから入手できます)。
これらのエラーは品質保証で見られる可能性がありますが、本当に無視される場合があります。発見されたとしても、コードを返送して修正するには多くの時間がかかる場合があります。
「ポカヨーク」
です。キーポイント
Poka Yokeを欠いているハードウェアの例は、キーボードコネクタとマウスコネクタとまったく同じ形状を持つPS/2ポートです。それらはカラーコードを使用することによってのみ区別することができるため、コネクタは誤って交換して間違ったポートに挿入するのが簡単です。
ハードウェアで使用されていることに加えて、Poka Yokeの概念もプログラミングに適用できます。アイデアは、コードのパブリックインターフェースをできるだけ理解しやすくすることを行い、コードが誤って使用されている場合にすぐにエラーをスローすることです。これは明白なように思えますが、実際には、この点で欠陥があるコードに遭遇することがよくあります。
ただし、Poka Yokeは意図的な虐待を防ぐことを意図していないことに注意してください。その目標は、コードを悪意のある使用から保護するのではなく、予期しないエラーを防ぐことです。誰かがあなたのコードにアクセスできる限り、彼らは本当に望むなら常にあなたのセキュリティ対策を迂回することができます。
コードをよりエラー防止にするためにどのような具体的な測定値を取ることができるかを議論する前に、ポカヨークメカニズムを通常2つのカテゴリに分割できることを知ることが重要です。
一方、エラー検出メカニズムはコードの外側に存在します。彼らは潜在的なエラーについて私たちのアプリケーションを監視し、私たちに警告します。例は、PS/2ポートに接続されたデバイスが正しいタイプのソフトウェアであるかどうかを検出することであり、そうでない場合、それが機能しない理由について警告がユーザーに表示されます。この特定のソフトウェアは、接続するとコネクタが交換可能であるためエラーを防ぐことはできませんが、エラーを検出して警告することができ、エラーを修正できるようにします。
この記事の残りの部分では、アプリケーションでエラー予防とエラー検出を実装するために使用できるいくつかの方法を検討します。ただし、このリストはほんの出発点にすぎないことを忘れないでください。特定のアプリケーションに応じて、コードをよりエラー防止にする可能性のある追加の測定値がある場合があります。また、ポカヨークの前払いコストを覚えておいて、特定のプロジェクトに価値があることを確認することが重要です。アプリケーションの複雑さとサイズに応じて、潜在的なエラーコストに比べていくつかのメジャーは高すぎる場合があります。したがって、あなたとあなたのチームは、あなたがとるのに最適な措置を決定する必要があります。エラー防止例
(スカラー)タイプ宣言
以前にPHP 5でタイプのヒントと呼ばれていたタイプのヒントは、PHP 7のエラー防止機能とメソッド署名を開始する簡単な方法です。
ユーザーに送信したいこの通知を見てみましょう。
<?php class Notification { private $userId; private $subject; private $message; public function __construct( $userId, $subject, $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() { return $this->userId; } public function getSubject() { return $this->subject; } public function getMessage() { return $this->message; } }
間違ったタイプを挿入すると、アプリケーションが実際に通知を処理しようとするまでエラーが検出されない場合があります。その時までに、予期しないタイプに関する理解できないエラーメッセージをいくつか取得するかもしれませんが、整数の代わりに文字列を挿入するコードをすぐに指すものはありません。
したがって、通常、開発中にそのようなエラーをできるだけ早く検出できるように、できるだけ早くアプリケーションを強制的にクラッシュさせることが通常興味深いです。
この場合、単にタイプの宣言を追加できます。パラメータータイプを難読化すると、PHPはすぐに停止し、致命的なエラーを警告します。
しかし、デフォルトでは、PHPは予想されるタイプに誤ったパラメーターをキャストしようとすることに注意してください。これを防ぐために、strict_typesを有効にして、エラーが発生したときに実際に致命的なエラーを受信することが重要です。したがって、スカラー型宣言は理想的なポカヨークフォームではありませんが、エラーを減らすための良いスタートです。 strict_typesが無効になっていても、パラメーターの予想されるタイプの指標として機能することができます。
さらに、メソッドの返品タイプを宣言します。これらにより、特定の関数を呼び出すときに期待できる値を簡単に判断できます。<?php declare(strict_types=1); class Notification { private $userId; private $subject; private $message; public function __construct( int $userId, string $subject, string $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() : int { return $this->userId; } public function getSubject() : string { return $this->subject; } public function getMessage() : string { return $this->message; } }
操作的に定義されたリターンタイプは、戻り値を処理するときに多数のスイッチステートメントを使用することを避けるのにも役立ちます。これは、メソッドが明示的に宣言されたリターンタイプなしでさまざまなタイプを返すことができるためです。したがって、私たちの方法を使用する人は、特定のケースで実際にどのタイプが返されるかを確認する必要があります。これらのスイッチステートメントは明らかに忘れられており、検出不能なエラーにつながります。返品タイプを使用すると、このようなエラーは大幅に削減されます。
値オブジェクト
スカラータイプのヒントを簡単に解決できないという問題の1つは、複数の関数パラメーターを使用すると、上記のパラメーターの順序を混乱させることができることです。すべてのパラメーターが異なるスカラータイプを持っている場合、パラメーターの順序を難読化すると、
PHPが警告できますが、ほとんどの場合、同じタイプのいくつかのパラメーターがある場合があります。
この問題を解決するために、このような値オブジェクトにパラメーターを包むことができます。
パラメーターには非常に具体的なタイプがあるため、それらを混乱させることはほとんど不可能です。
値オブジェクトを使用する場合、オブジェクト自体のデータ検証ロジックをカプセル化できます。そうすることで、無効な状態で値オブジェクトの作成を防ぐことができます。これにより、アプリケーションの他のレイヤーに問題が発生する可能性があります。
たとえば、特定のユーザーIDは常に肯定的でなければならないというルールがあるかもしれません。
ユーザーIDを入力として取得するたびにこのルールを明らかに検証できますが、一方では、ある場所で簡単に忘れられることもあります。
このエラーがアプリケーションの別のレイヤーで実際のエラーが発生した場合でも、エラーメッセージは、実際に発生したエラーを明確に示していない場合があり、デバッグが困難です。
このようなエラーを防ぐために、ユーザーIDコンストラクターに検証を追加できます。
この方法では、userIDオブジェクトを使用するときに有効な状態があることを常に保証できます。これにより、アプリケーションのあらゆるレベルでデータを常に敬意なくすることができなくなります。<?php class Notification { private $userId; private $subject; private $message; public function __construct( $userId, $subject, $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() { return $this->userId; } public function getSubject() { return $this->subject; } public function getMessage() { return $this->message; } }
IS_INTを使用する代わりにスカラー型宣言を追加できることに注意してくださいが、これにより、ユーザーIDを使用するすべての場所でstrict_typesを有効にすることができます。
strict_typesを有効にしない場合、PHPはユーザーIDに渡すときに、他のタイプをintに自動的にキャストしようとします。これには問題が発生する可能性があります。たとえば、フローティングポイント数を挿入する可能性があります。これは、ユーザーIDが浮動小数点数ではないため、実際には誤った変数になる可能性があります。
PHPがフローティングポイント変数をintに自動的に変換するため、価格値オブジェクトを使用する場合など、他の場合など、strict_typesを無効にすると、丸めエラーが発生する場合があります。不変
デフォルトでは、オブジェクトはPHPで参照によって渡されます。これは、オブジェクトを変更すると、アプリケーション全体ですぐに変更されることを意味します。
この方法には利点がありますが、いくつかの欠点もあります。テキストメッセージと電子メールを介してユーザーに通知を送信する例を見てみましょう。
この問題を解決するために、通知オブジェクトを不変にすることができます。それを変更するために設定されたメソッドを提供する代わりに、元の通知のコピーを作成して変更を適用して、次の方法でいくつかを追加できます。
このように、たとえば、メッセージの長さを短縮することで通知クラスを変更するたびに、アプリケーション全体で変化はもはや伝播しないため、予期しない副作用が防止されます。<?php declare(strict_types=1); class Notification { private $userId; private $subject; private $message; public function __construct( int $userId, string $subject, string $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() : int { return $this->userId; } public function getSubject() : string { return $this->subject; } public function getMessage() : string { return $this->message; } }
しかし、PHPでオブジェクトを真に不変にすることは難しい(不可能ではないにしても)注意してください。しかし、コードをよりエラー防止にするために、セットメソッドの代わりにメソッドを「不変」に追加すると、クラスのユーザーが変更を行う前にオブジェクト自体をクローンすることを忘れないでください。
特定の値またはnullを返すことができる関数または方法がある場合があります。これらの無効な返品値は、それらを使用する前に空であるかどうかをほとんど常に確認する必要があるため、問題を引き起こす可能性があります。繰り返しますが、これは私たちが簡単に忘れることができるものです。常に返品値を確認する必要がないようにするために、代わりに空のオブジェクトを返すことができます。
割引が適用されたり、割引が適用されないショッピングカートを使用できます。
<?php class Notification { private $userId; private $subject; private $message; public function __construct( $userId, $subject, $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() { return $this->userId; } public function getSubject() { return $this->subject; } public function getMessage() { return $this->message; } }
一方、割引が設定されていないときに空のオブジェクトを返す場合、これらのチェックを完全に削除できます。
<?php declare(strict_types=1); class Notification { private $userId; private $subject; private $message; public function __construct( int $userId, string $subject, string $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() : int { return $this->userId; } public function getSubject() : string { return $this->subject; } public function getMessage() : string { return $this->message; } }
オプション依存関係
class UserId { private $userId; public function __construct(int $userId) { $this->userId = $userId; } public function getValue() : int { return $this->userId; } } class Subject { private $subject; public function __construct(string $subject) { $this->subject = $subject; } public function getValue() : string { return $this->subject; } } class Message { private $message; public function __construct(string $message) { $this->message = $message; } public function getMessage() : string { return $this->message; } } class Notification { /* ... */ public function __construct( UserId $userId, Subject $subject, Message $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() : UserId { /* ... */ } public function getSubject() : Subject { /* ... */ } public function getMessage() : Message { /* ... */ } }
この方法には2つの問題があります
class UserId { private $userId; public function __construct($userId) { if (!is_int($userId) || $userId <= 0) { throw new \InvalidArgumentException( 'UserId should be a positive integer.' ); } $this->userId = $userId; } public function getValue() : int { return $this->userId; } }
サービスコンテナにSomeServiceクラスをセットアップする場合、誰かが実際にロガーをセットアップするのを忘れたり、クラスにロガーをセットアップするオプションがあることさえ知らない場合があります。
この問題を必要な依存関係にすることで、この問題を簡素化できます。
interface NotificationSenderInterface { public function send(Notification $notification); } class SMSNotificationSender implements NotificationSenderInterface { public function send(Notification $notification) { $this->cutNotificationLength($notification); // 发送短信... } /** * 确保通知不超过短信长度。 */ private function cutNotificationLength(Notification $notification) { $message = $notification->getMessage(); $messageString = substr($message->getValue(), 0, 160); // 修正截取长度 $notification->setMessage(new Message($messageString)); } } class EmailNotificationSender implements NotificationSenderInterface { public function send(Notification $notification) { // 发送电子邮件... } } $smsNotificationSender = new SMSNotificationSender(); $emailNotificationSender = new EmailNotificationSender(); $notification = new Notification( new UserId(17466), new Subject('Demo notification'), new Message('Very long message ... over 160 characters.') ); $smsNotificationSender->send($notification); $emailNotificationSender->send($notification);
さらに、IFステートメントを省略して、ロガーが注入されているかどうかを確認しました。
<?php class Notification { private $userId; private $subject; private $message; public function __construct( $userId, $subject, $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() { return $this->userId; } public function getSubject() { return $this->subject; } public function getMessage() { return $this->message; } }
最終的に、これはオプションのsetlogger()メソッドを使用するのと同じ効果をもたらしますが、コードが容易になり、依存関係噴射コンテナのエラーの可能性を減らします。
コードを使いやすくするには、クラスのパブリックメソッドの数を最小限に抑えることが最善です。これにより、コードの使用量が紛らわしくなくなり、コードを維持し、リファクタリングする際に逆方向の互換性を突破する可能性が低くなります。
パブリック方法を最小限に抑えるために、それはトランザクションとしてパブリック方法と見なすことができます。
たとえば、2つの銀行口座間でお金を譲渡します
基礎となるデータベースは、デポジットを作成できない場合、資金が撤回されないことを保証するためのトランザクションを提供できますが、その逆も同様ですが、データベースは$ Account1-&gt()または$ Account2を呼び出すことを忘れられないようにします。 - &gt; dopit()。これにより、残高が間違っています。<?php declare(strict_types=1); class Notification { private $userId; private $subject; private $message; public function __construct( int $userId, string $subject, string $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() : int { return $this->userId; } public function getSubject() : string { return $this->subject; } public function getMessage() : string { return $this->message; } }
幸いなことに、2つの別々の方法を単一のトランザクション方法に置き換えることで、この問題を簡単に解決できます。
結果として、トランザクションを部分的に完了するだけでエラーが発生しやすいため、コードはより堅牢になります。
class UserId { private $userId; public function __construct(int $userId) { $this->userId = $userId; } public function getValue() : int { return $this->userId; } } class Subject { private $subject; public function __construct(string $subject) { $this->subject = $subject; } public function getValue() : string { return $this->subject; } } class Message { private $message; public function __construct(string $message) { $this->message = $message; } public function getMessage() : string { return $this->message; } } class Notification { /* ... */ public function __construct( UserId $userId, Subject $subject, Message $message ) { $this->userId = $userId; $this->subject = $subject; $this->message = $message; } public function getUserId() : UserId { /* ... */ } public function getSubject() : Subject { /* ... */ } public function getMessage() : Message { /* ... */ } }
エラー防止メカニズムとは対照的に、エラー検出メカニズムはエラーを防ぐことを意図していません。代わりに、問題を検出したときに警告するように設計されています。
単位テスト
単体テストでは、新しいコードが適切に機能することを保証できますが、誰かがシステムの一部をリファクタリングするときに既存のコードが予想どおりに機能することを確認することもできます。
エラー検出とは別に、単体テストは、コードの特定の部分がどのように機能するかを示す例を提供する素晴らしい方法でもあり、これにより、他の人がコードを使用するときに間違いを犯すことができなくなります。
コードカバレッジレポートと突然変異テスト
常に十分なテストを書くのを忘れる可能性があるため、Coverallsなどのサービスを使用して、単体テストを実行するときにコードカバレッジレポートを自動的に生成することが有益かもしれません。コードカバレッジがダウンするたびに、Coverallsは通知を送信するため、いくつかの単体テストを追加でき、コードカバレッジが時間の経過とともにどのように変化するかも確認できます。
コードに十分な単体テストがあることを確認するもう1つのより良い方法は、Humbugの使用など、いくつかの突然変異テストを設定することです。名前が示すように、これらのテストは、ソースコードをわずかに変更し、単体テストを実行して、変異により関連するテストが失敗し始めることにより、十分なコードカバレッジがあることを確認するように設計されています。
コードカバレッジの報告と突然変異テストを使用して、単体テストが予期しないエラーやエラーを防ぐのに十分なコードをカバーすることを確認できます。
コードアナライザーは、開発プロセスの早い段階でアプリケーションのエラーを検出できます。たとえば、phpstormなどのIDEはコードアナライザーを使用して、エラーを警告し、コードを書くときに提案をします。これらは、単純な構文エラーから繰り返しコードの検出にまで及びます。
ほとんどのIDEの組み込みアナライザーに加えて、サードパーティまたはカスタムアナライザーをアプリケーションのビルドプロセスに組み込むために、特定の問題を発見することができます。 PHPプロジェクトに適したアナライザーの網羅的ではないリストは、標準的な分析装置からセキュリティの脆弱性をチェックする分析装置まで、Exakat/PHP-Static-Analysis-Toolsにあります。
Sensiolabs Insightsなどのオンラインソリューションが存在します。ログメッセージ
ほとんどのアプリケーションは少なくともいくつかのメッセージを記録しますが、KibanaやNagiosなどのツールを使用してそれらを積極的に分析および監視している場合、それらが提供する情報は本当に興味深いものになります。このようなツールは、ユーザーが内部でテストするときではなく、アプリケーションでアプリケーションで発生するエラーと警告に関する洞察を与えてくれます。このELKスタックを使用したPHPアプリケーションの監視に関する優れた記事があります。
エラーを抑制しない
一般的に、PHPのERROR_REPORTINGレベルをE_ALLに設定して、わずかな警告でさえ報告されるようにすることをお勧めします。ただし、これらのメッセージがどこかに記録され、ユーザーの前に隠されていることを確認して、アプリケーションアーキテクチャまたは潜在的なセキュリティの脆弱性に関する機密情報がエンドユーザーにさらされないようにしてください。
ERROR_REPORTING Configurationに加えて、PHPがあるタイプから別のタイプに変換するとき(例えば、INTに変換するときのフロート丸めエラーからのFLUNCEパラメーターを自動的にキャストしようとしないように、常にstrict_typesを常に有効にしてください。 )通常、エラーの検出が困難になります。
Poka Yokeは特定のテクノロジーというよりも概念であるため、PHP以外の領域(ただし、PHPに関連する)にも適用できます。
インフラストラクチャレベルでは、Vagrantなどのツールを使用して、生産環境と同じ開発設定を共有して、多くのエラーを防ぐことができます。
JenkinsやGOCDなどのサーバー構築を使用すると、アプリケーションの変更を展開するときにエラーを防ぐのにも役立ちます。REST API
たとえば、
APIには色パラメーターがあるかもしれませんが、APIを使用している人は誤って色パラメーターを使用する場合があります。警告がなければ、このエラーは、エンドユーザーが予期しない動作のために気付くまで簡単に生産に入ることができます。後であなたを失望させることのないAPIを構築する方法を理解するために、良い本が役立つかもしれません。アプリケーション構成
ただし、上記の色や色の例と同様に、構成パラメーターを誤って入力するのは簡単であるため、アプリケーションは誤ってデフォルト値に戻ります。アプリケーションがエラーをスローしない場合、このようなエラーをトレースすることは困難です。また、誤った構成のエラーをスローする最良の方法は、デフォルト値をまったく提供せず、構成パラメーターが欠落しているときにすぐにエラーをスローすることです。
ユーザーエラーを防止
Poka Yoke Conceptを適用して、ユーザーエラーを防止または検出することもできます。たとえば、支払いソフトウェアでは、チェックビットアルゴリズムを使用して、ユーザーが入力したアカウントを確認できます。これにより、ユーザーはタイプミスで誤ってアカウントを入力することを防ぎます。
Poka Yokeは特定のツールセットというよりも概念ですが、コードと開発プロセスにさまざまな原則を適用して、エラーができるだけ早く予防または検出されるようにすることができます。多くの場合、これらのメカニズムはアプリケーション自体とそのビジネスロジックに固有のものになりますが、いくつかの簡単な手法とツールを使用して、コードをよりエラー防止することができます。
最も重要なことは、明らかに生産のエラーを回避したいのに、開発に非常に役立ち、エラーをより簡単に追跡できるようにできるだけ早くそれらを上げることを恐れないでください。これらのエラーは、コード自体によって、またはアプリケーションとは別に実行され、外部から監視する個別のプロセスによって発生することができます。
エラーをさらに削減するには、コードのパブリックインターフェースを可能な限り単純かつ明確にするよう努力する必要があります。
PHP開発または一般的なプログラミングにPoka Yokeを適用する方法に関する他のヒントがある場合は、コメントでお気軽に共有してください!
ポカヨークは従来のプログラミング方法とどう違うのですか?
はい、Poka-Yokeは、あらゆるプログラミング言語に適用できる概念です。特定のツールやテクノロジーではなく、考え方やプログラミング方法です。どの言語を使用していても、Poka-Yokeの原則を実装して、コードをよりエラー抵抗性にすることができます。
Poka-Yokeは、ソフトウェア製品の全体的な品質をどのように促進しますか?
ポカヨークは時間のかかるプロセスですか?
製造におけるポカヨークの利点は何ですか?
プログラミングの練習でPoka-Yokeの実装を開始するにはどうすればよいですか?
ポカヨークはアジャイル開発に使用できますか?
ポカヨークは大規模なプロジェクトにのみ役立ちますか?
以上がPoka Yoke-高防御プログラミングでプロジェクトを保存しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。