PHPで記述する...静的に

PHPは、動的な暗黙の弱い型付けを備えた言語です。 多くの場合、動的型付けは非常に便利です。また、値に集中し、型が何であるかを考えないため、実際に人生が大幅に簡素化されます。



多くのPHPプログラマと同様に、静的型付けは「複雑」だと思いました。 それは柔軟性を制限し、一般的に:人々はそれをどのように扱うのでしょうか? そして、多くの経験豊富なプログラマーが静的型付けと厳密な型チェックを備えた言語を好む理由を心から理解していませんでした。



議論を入力する



私は型についてほとんど知らない人の右半分に属していましたが、同時にこれは便利ではないと心から信じています。 そのため、強く型付けされた言語(c#)の1つに密接に出会うまではそうでした。 それ以来、私のphpへの態度、そして実際にはプログラミング全般に対する態度が変わりました。



この記事では、プログラミングの「タイタン」に興味を持つことはほとんどありません。なぜなら、ここでは革新的で新しいものは何も見つからないからです。







まず、静的な型定義を定義しましょう:



静的型付けは、変数と関数の最終型がコンパイル段階で設定されるという事実によって決定されます。 つまり コンパイラはすでにどの型がどこにあるかを100%確信しています。



phpにはコンパイラがありません。いずれの場合も実行時にすべての型が計算されますが、これにより特定のルールに従って厳密に自分で監視することを妨げることはありません。



PHPでこれはすべて何ですか?



c#を勉強し、同時に、十分な経験とドキュメントなしで(より正確にはドキュメントがまったくありませんでした)タスクの設定に応じて製品を習得しました。与えられたタイプ。 何をどこに転送するかを理解するためにドキュメントは必要ありませんでした。何か間違ったことはできませんでした。



これにより、このメソッドがそのようなパラメーターのみを受け入れることがすぐにわかり、これらのパラメーターのオブジェクトを作成するときに、構築などで欠落しているリンクが見つかるため、時間が大幅に短縮されました。



「まあ、それは本当に便利です」と言います。「しかし、新しいことは何も言わなかったのです。PHPには、最新のIDEを理解し、その場でコードを書くのに役立つPHPDocがあります!」



すべてが真実ですが、これを静的に型付けされた言語と同じように機能させるには、各メソッドとクラスプロパティにコメントを書く必要があるだけでなく、アーキテクチャ全体が既知のオブジェクトで動作するように調整されていることが重要です。 このようなアーキテクチャを構築するには、特定のルールに従う必要があります。 さらに話したいのは、彼らについてです。



配列の代わりにオブジェクトを使用する



はい、正確に。 一般に、この真実は新しいものではなく、多くのことが書かれています。オブジェクトは簡単に展開でき、メソッドはオブジェクトに簡単に追加でき、一般的にはOOPのパスです。



しかし、効果的なプログラミングに適したアーキテクチャを構築するという観点から、この問題を反対側から検討したいと思います。



簡単な例を考えてみましょう。

システムでイベントが発生し、特定のパラメーターをハンドラーに渡す必要があるとします。

これには配列を使用します。



$eventData = array( 'sender' => $controller, //   'name' => 'onDelete', 'group' => 'global', 'arguments' => array( 'id' => 15 ), ); $eventDispatcher->triggerEvent($eventData); ... /** *    * @param array $eventData */ protected function onDelete($eventData) { //    $eventData   , //       var_dump() //        $model = $eventData['sender']->model->deleteChilds(); }
      
      





最も単純で最も一般的なケースは、複雑なことではなく、データで配列を作成し、関数に送信します。



ここで、このイベントの挑戦が、あなたによって(またはあなたによってではなく、昨日ではなく)書かれたプログラムのどこか深いところにあると想像してください。



提示? また、この配列の形式を説明するドキュメントがあると便利です。したがって、配列の構造を調べるためにコードの呼び出しセクションを探す必要はありません。 または、var_dump()を使用して、とにかくそこで何が起こっているのかを理解するために時間を費やします。



配列の代わりに、クラスを記述してそのオブジェクトを作成した場合にどうなるか考えてみましょう。 コードを変更します。



 class EventData { /** *  ,   * @var BaseController */ public $sender; /** *   * @var string */ public $name; /** *      () * @var string */ public $group; /** *     * @var array */ public $arguments; } $eventData = new EventData(); $eventData->sender = $controller; $eventData->name = 'onDelete'; $eventData->group = 'global'; $eventData->arguments = array('id' => 15); $eventDispatcher->triggerEvent($eventData); ... /** *    * @param EventData $eventData */ protected function onDelete($eventData) { $eventData->sender->model->deleteChilds(); }
      
      





その結果、何が得られましたか? これで、お気に入りのIDEが結果のオブジェクトに含まれるものを教えてくれます。







さらに、送信されたオブジェクトの各属性のタイプが詳細になっているため、すぐにそれらのヒントを得ることができます。 つまり $ eventData-> controller->と入力すると、controllerという名前の属性の内部にあるものを確認できます。



これで、このオブジェクトで送信されるものとそのタイプを理解するために、コードのどこかに行く必要がなくなりました。 あなたのコードを扱う他のプログラマーがあなたに感謝することを想像してください!



アプリケーションがActiveRecordパターンを積極的に使用している場合は、すでに完全な入力に近づいていますが、配列の形でデータベースから要素を取得し、そこから要素を追跡する場合は、関数ごとに何度も変更することを検討する必要がありますアーキテクチャ。



これは単純でかなり合成的な例でした。 おそらく同様の状況のプロジェクトでは、配列ではなくオブジェクトが使用されます。 しかし、注意深く見てください。これには似ていない状況があります。たとえば、3階建ての配列が関数に転送されたり、関数が理解できない型の引数を12個持っています。



例の改善を続けて、1つのクラスの作成にとどまりません。 通常、現代のWebアプリケーションでは多くの異なるイベントが発生し、それらに異なる引数が渡され、それらをグループ化することができ、イベントの各グループに対して独自の-適切な引数のクラスを作成します。



クラスを作成することを恐れないでください。 相互接続されている場合は、それぞれに個別のファイルを作成する必要はありません。

たとえば、EventArguments.phpファイルを作成し、その中にイベント引数のすべての可能なオプションを記述します。



UPD:文字通りすべての配列をオブジェクトに置き換えるという私の呼び出しを理解することをお勧めしません。これは正しくありません。



ウィキからの引用:

配列は、同じタイプのデータを格納するための順序付けられたデータのセットであり、1つ以上のインデックスによって識別されます。


配列を使用する必要があるのは、この方法です。 つまり 特定の順序付けられた同種データを保存します。



配列をオブジェクトに無意識に変更すると、良いことよりも害になることがあります。 したがって、オブジェクトの使いやすさとシステムパフォーマンスの間の境界線を自分自身(およびプロジェクト)で決定する必要があります。



正しい型で変数を初期化します。



例えばすぐに:



  /** *   * @return array */ public function getChildren(){ if(!$this->isLeaf) { return $this->find('where id = :id', array(':id', $this->id)); } } ... foreach($model->getChildren() as $child) { //     }
      
      





それはおなじみですか? 全体の問題は、条件!$ This-> isLeafが満たされない場合、関数の結果がまったく配列されないという事実にあります。不正確な引数のためにforeachが落ちないように、大量の定型コードを記述する必要があります。



あるべき姿:



  /** *   * @return array */ public function getChildren(){ //     ,     -   . //         ,     ,   . $children = array(); if(!$this->isLeaf) { $children = $this->find('where id = :id', array(':id', $this->id)); } return $children; }
      
      





今、条件!$ This-> isLeafが満たされない場合、関数の結果は最初に定義した空の配列になり、この関数からの値が入るforeachは何かがうまくいかなくても落ちません。



考慮された例は、氷山の一角にすぎません。 既知の目的の型で変数を常にすぐに決定する場合、多くの時間を節約できます。 また、コードの認識を悪化させる追加のチェックなしで、より美しいコードを作成します。



ところで、これはphpだけに当てはまりません。 たとえば、V8エンジンのJavaScriptでは、作成の段階で変数のタイプを決定すると、エンジンに不必要な変換を強制しないため、スクリプトの実行が加速されます(この記事で詳しく説明します )。



強力なタイピングツール。



PHPには弱い型付けがあります。つまり、型を処理するときに型をチェックしません。 つまり 文字列に数字を追加したり、スカラーオブジェクトの等価性を確認したりすると、インタープリターは何も通知しません。 PHPに厳密に型指定された動作を強制することはできません。 ただし、PHPにはいくつかの便利な(常に論理的ではありませんが)型制御ツールが付属しています。



たとえば、関数を定義するときに、パラメーターのタイプを明示的に指定できます。



  public function triggerEvent(EventData $event) { //          EventData    } ... $dispatcher->triggerEvent(new EventData); //  $dispatcher->triggerEvent(new EventDataChild); //EventDataChild -  EventData,   $dispatcher->triggerEvent(new AnotherClass()); //Catchable fatal error: Argument 1 passed to EventDispatcher::triggerEvent() must be an instance of EventData, instance of new AnotherClass given
      
      





したがって、追加のチェックを行わずに、誤ったタイプのパラメーターを誤って関数に渡さないように保護しました。 PHP自体は、渡されたパラメーターのタイプをチェックし、一致しない場合はエラーをスローします。



php.net/oop5.typehinting型制御マニュアルのページから引用:



現時点では、関数はパラメーターをオブジェクト(関数プロトタイプでクラス名を指定することにより)にするか、インターフェース、配列(PHP 5.1以降)、または呼び出し可能型で呼び出し可能(PHP 5.4以降)にすることができます。



ただし、デフォルトのパラメータ値としてNULLが使用された場合、これは後の呼び出しの引数としても有効です。



型制御にクラスまたはインターフェースが指定されている場合、その子孫または実装もすべて有効です。

型制御は、intやstringなどのスカラー型では使用できません。 特性も受け入れられません。


上記から結論を導き出し、スカラー型を除くほとんどすべてを指定できますが、PHPにもこれに対する解決策があります。 標準SPLライブラリには、この問題を解決するために設計されたいくつかの面白いクラスが含まれています。



これらの型はクラスであるため(オイルはバターですが、intが型であるがクラスではないphpのコンテキストでは-許可されます)、関数定義で指定できます。これにより、たとえばブール値が必要な文字列の関数への転送を防止できます。 したがって、標準のスカラーではなく、コード全体でこれらのクラスを使用する必要があります。



正直なところ、私はこれを使いませんでした。私の意見ではこれが多すぎるからです。 さらに、これらのクラスは標準のPHPパッケージには含まれておらず、PECLパッケージによって配布されているため、使用が困難です。



可能な限りPHPDocを使用する



IDEでのPHPDocとそのサポートは、PHPプログラマの時間を節約するための優れたツールです。



言語の構文とそのイデオロギーでは、変数の作成時に変数のタイプを設定できないため、IDEがどの変数が何であるかを理解することは非常に困難です。 だから彼女を助けてください-コメントでタイプを示してください、そしてあなたのIDEは作業中のヒントであなたに感謝します。



これは、アプリケーションを完全に入力するためのもう1つのステップです。 何を取得し、目的のメソッドが何を返すかがわかっていれば、それを使用するのに問題はありません。



私は次の規則を順守します。

クラスのすべてのパブリック属性には、タイプの説明を含むコメントが必要です。

すべてのパブリックメソッドには、実行内容の説明と、返されるものを含む各パラメーターの説明も含める必要があります。



PHPDocに十分な知識がない人のための小さなヒント(これはすべてNetBeans環境に当てはまります)



  //     , //         @return //  IDE             /** *      EventData * @return EventData */ public function getEventData(){ return new EventData(); } //   ,    ,     : //@return EventHandler[] //  -      . //   ,    ,      // //: /**    EventHandler * @return EventHandler[] */ public function getEventHandlers(){ return $this->handlers; } public function triggerEvent(){ foreach ($this->getEventHandlers() as $handler) { $handler; //   IDE  ,  $handler    EventHandler,     } } public function getModel(){ //      //(    .  ,      ) //   ,         .    : $model = NodeFactory::byId($id); /* @var $model Folder */ $model; //  IDE         Folder }
      
      







おわりに



誰が言っても、PHPはかなり強力なツールに成長しました。 動的型付けは、可能な限り柔軟かつ単純にすることを可能にします。 しかし、それに伴い困難が伴います。







静的型付けは、生産性を向上させ、コード内のエラーの数を減らすことができます。



habrに関するコメントから、類型化に関するLikbezの記事まで:

少なくとも何かを自動的にチェックする可能性を非常に複雑にしている複雑なタスクでは、強力な静的型付けの言語は他のすべてよりも優れています。 サイト構築中に解決されるタスクの複雑さは、徐々にこのレベルに近づいています。


PHPの関係を型に変更することはできません。これは必要ありません。 また、一晩かけてすべてのプロジェクトを他の言語に翻訳することもできません。時間も機会もありません。

しかし、よく構築されたアーキテクチャ、関数のプロトタイプでの型制御の使用、およびPHPDocを使用したドキュメントのおかげで、不必要な作業やプロジェクトのエラーを発見することができます。



この記事で概説したアドバイスに従って、両方の長所を組み合わせて、複雑なプロジェクトを開発するための強力で柔軟なツールを手に入れることができます。



便利なリンク:



プログラミング言語でのタイピングに関するLikbez

PHPでの型制御

PHP SPLタイプ

静的コード分析(Wikipedia)



All Articles