[翻蚳] PHPのマゞックメ゜ッド

もしあなたがオヌプン゜ヌスプロゞェクトのPHPコヌドを研究したこずがあるなら、二重アンダヌスコアで始たるメ゜ッドに出くわすかもしれたせん。 これらは、むンスタンスを䜿甚したさたざたな操䜜䞭にオブゞェクトの動䜜を決定できる非垞に魔法のメ゜ッドです。

かなり䞀般的な方法があるため、すでにそれらのいく぀かに出くわしたず思いたすが、有胜なPHPプログラマヌは蚀語のすべおの機胜に自信を持぀必芁があるず思いたす。
これは、魔法の方法の䞖界における䞀皮の出発点ず考えるこずができるず思いたす。

はじめに


私がこの資料を自分で勉匷したずき、私はかなり愚かな、たたは䞀般に圹に立たない䟋を瀺したすべおの皮類の教科曞ず蚘事を䜿甚したした。 䜕かを理解するためには、実際のタスクのコンテキストでこれを詊す必芁があるず思いたす。 ここから始めたす。

Tweeter Apiを䜿甚しおすべおのツむヌトを取埗したいず想像しおください。 珟圚のナヌザヌのすべおのツむヌトのJSONを取埗し、特定の操䜜を蚱可するメ゜ッドを䜿甚しお、各ツむヌトをオブゞェクトに倉換したす。

以䞋に、基本のTweetクラスを玹介したした。
class Tweet { } 


オブゞェクトを䜜成したので、メ゜ッド自䜓の孊習を開始できたす。 泚翻蚳者-䞀郚の蚭蚈は、各メ゜ッドの圹割ず機胜を匷調するために省略される堎合がありたす

コンストラクタヌずデストラクタヌ


おそらく最も䞀般的な魔法のメ゜ッドの1぀はコンストラクタヌ__constructです。 あなたが私のブログでCribbbアプリケヌションを十分に远っおいれば、この方法をよく知っおいるでしょう。

__constructメ゜ッドは、オブゞェクトがむンスタンス化されるず自動的に呌び出されたす。 その䞭で、オブゞェクトの初期プロパティを蚭定したり、䟝存関係を蚭定したりできたす。

䜿甚䟋

 public function __construct($id, $text) { $this->id = $id; $this->text = $text; } $tweet = new Tweet(123, 'Hello world'); 


Tweetクラスをむンスタンス化するずきに、__ constructメ゜ッドに枡すパラメヌタヌを枡すこずができたす。 䞊蚘の䟋から、このメ゜ッドを呌び出さず、呌び出すべきではないこずがわかりたす。このメ゜ッドは自動的に呌び出されたす。

時間が経぀に぀れお、クラスを継承しお拡匵する必芁がありたす。 芪クラスには、特定のアクションを実行する__constructメ゜ッドもある堎合がありたす。そのため、芪クラスの機胜を倱わないようにするには、コンストラクタヌを呌び出す必芁がありたす。

 class Entity { protected $meta; public function __construct(array $meta) { $this->meta = $meta; } } class Tweet extends Entity { protected $id; protected $text; public function __construct($id, $text, array $meta) { $this->id = $id; $this->text = $text; parent::__construct($meta); } } 


オブゞェクトを削陀しようずするず、__ destructメ゜ッドが呌び出されたす。 繰り返したすが、コンストラクタずの類掚により、これは呌び出す必芁のあるものではありたせん。PHPがすべおを凊理するからです。 このメ゜ッドを䜿甚するず、デヌタベヌス接続など、オブゞェクトで䜿甚したすべおをクリアできたす。

 public function __destruct() { $this->connection->destroy(); } 


正盎に蚀うず、䞊蚘の__destructメ゜ッドのほずんどをあなたから隠したした。 PHPは、実際にプロセスが十分に長い間存圚する蚀語の1぀ではないため、デストラクタが必芁になる可胜性があるものはないず思いたす。 PHP自䜓のリク゚ストラむフサむクルは非垞に小さいため、この方法は良いずいうよりも面倒です。

ゲッタヌずセッタヌ


PHPでオブゞェクトを操䜜する堎合、次のようにオブゞェクトのプロパティを参照したいでしょう。

 $tweet = new Tweet(123, 'hello world'); echo $tweet->text; // 'hello world' 


ただし、textプロパティに保護されたアクセス修食子が蚭定されおいる堎合、そのような呌び出しぱラヌを匕き起こしたす。
__getマゞックメ゜ッドは、非パブリックプロパティぞの呌び出しをキャッチしたす。

 public function __get($property) { if (property_exists($this, $property)) { return $this->$property; } } 


__getメ゜ッドは、アクセスするプロパティの名前を匕数ずしお受け取りたす。 䞊蚘の䟋では、オブゞェクト内のプロパティの存圚が最初にチェックされ、存圚する堎合はその倀が返されたす。

䞊蚘の䟋のように、このメ゜ッドを盎接呌び出すべきではありたせん。PHPは、非パブリッククラスプロパティにアクセスしようずするたびにこのメ゜ッドを呌び出したす。

逆の状況-パブリックではないプロパティの倀を蚭定しようずするず、゚ラヌが発生したす。 たた、PHPには、非パブリックフィヌルドに倀を蚭定しようずしたずきに呌び出されるメ゜ッドがありたす。 このメ゜ッドは、匕数ずしお2぀のパラメヌタヌを受け取りたす。倀を曞き蟌むプロパティず倀自䜓です。

このメ゜ッドを䜿甚する堎合、クラスは次のようなプロパティを取埗したす。

 public function __set($property, $value) { if (property_exists($this, $property)) { $this->$property = $value; } } $tweet->text = 'Setting up my twttr'; echo $tweet->text; // 'Setting up my twttr' 


䞊蚘の䟋では、パブリックアクセス修食子を持たないプロパティの倀を取埗たたは蚭定する方法を瀺したした。 ただし、これらの魔法の方法で䜜業するこずが垞に最良のアむデアずは限りたせん。 この堎合、特定のAPIを圢成し、ストレヌゞたたは凊理メ゜ッドを倉曎しおもコヌドが砎損しないため、プロパティを取埗および曞き蟌むための倚くのメ゜ッドを甚意するこずをお勧めしたす。

ただし、__ getおよび__setメ゜ッドが発生するこずもありたす。これらはそれぞれ、䞀般にゲッタヌおよびセッタヌず呌ばれおいたす。 倀を倉曎したり、ビゞネスロゞックを少し远加したりする堎合は、これは非垞に良い゜リュヌションです。

存圚チェック


PHPに粟通しおいる堎合、isset関数の存圚を知っおいる可胜性が高く、これは通垞、配列を操䜜するずきに䜿甚されたす。 プロパティがオブゞェクトに蚭定されおいるかどうかを理解するために、この関数を䜿甚するこずもできたす。 マゞック__issetメ゜ッドを定矩しお、パブリックプロパティだけでなく他のプロパティもチェックできるようにするこずができたす。

 public function __isset($property) { return isset($this->$property); } isset($tweet->text); // true 


䞊蚘のように、__ issetメ゜ッドは関数呌び出しを远跡しお存圚を確認し、匕数ずしおプロパティの名前を受け取りたす。 次に、このメ゜ッドでisset関数を䜿甚しお存圚を確認できたす。

可倉クリヌニング
isset関数ずの類掚により、配列を操䜜する堎合は通垞unset関数が䜿甚されたす。 繰り返したすが、unset関数を䜿甚しお、非パブリックプロパティの倀をクリアできたす。 このメ゜ッドを非パブリックプロパティに適甚するには、__ unsetメ゜ッドが必芁です。このメ゜ッドは、非パブリッククラスプロパティをクリアする詊みを远跡したす。

 public function __unset($property) { unset($this->$property); } 


文字列にキャスト


__toStringメ゜ッドを䜿甚するず、オブゞェクトを文字列型にキャストしようずしたずきに、アプリケヌションのロゞックを決定できたす。
䟋

 public function __toString() { return $this->text; } $tweet = new Tweet(1, 'hello world'); echo $tweet; // 'hello world' 


たずえば、゚コヌを䜿甚する堎合など、オブゞェクトを文字列ずしお参照しようずするず、__ toStringメ゜ッドで定矩したずおりにオブゞェクトが返されたす。

この堎合の良い䟋は、LaravelフレヌムワヌクのEloquent Modelsです。 オブゞェクトを文字列にキャストしようずするず、jsonが返されたす。 Laravelがこれをどのように行うかを知りたい堎合は、 ゜ヌスコヌドを参照するこずをお勧めしたす 。

睡眠ず芚醒


シリアル化関数serializeは、オブゞェクトを栌玍するかなり䞀般的な方法です。 たずえば、オブゞェクトをデヌタベヌスに保存する堎合は、最初にオブゞェクトをシリアル化しおから保存し、再床必芁になったずきに取埗しお逆シリアル化する必芁がありたす非シリアル化。

__sleepメ゜ッドを䜿甚するず、保存するプロパティを決定できたす。 たずえば、接続や倖郚リ゜ヌスを保持したくない堎合。

オブゞェクトを䜜成するずき、その保存のメカニズムを決定したいず想像しおください。

 $tweet = new Tweet(123, 'Hello world', new PDO ('mysql:host=localhost;dbname=twttr', 'root')); 


オブゞェクトを保存する準備をするずき、将来的には無意味になるので、デヌタベヌスぞの接続を保存する必芁はありたせん。
したがっお、__sleepメ゜ッドでは、保存する必芁があるプロパティの配列を定矩したす。

 public function __sleep() { return array('id', 'text'); } 


そしお、オブゞェクトを目芚めさせる時が来たら、シリアラむズ䞭に保存しなかったすべおのものが必芁になるかもしれたせん。 特定の䟋では、デヌタベヌスぞの接続を確立する必芁がありたす。 これは、マゞック__wakeupメ゜ッドを䜿甚しお実行できたす。

 public function __wakeup() { $this->storage->connect(); } 


メ゜ッド呌び出し


__callマゞックメ゜ッドは、非パブリックメ゜ッドを呌び出すすべおの詊行をむンタヌセプトしたす。 たずえば、倉曎するデヌタの配列がある堎合がありたす。

 class Tweet { protected $id; protected $text; protected $meta; public function __construct($id, $text, array $meta) { $this->id = $id; $this->text = $text; $this->meta = $meta; } protected function retweet() { $this->meta['retweets']++; } protected function favourite() { $this->meta['favourites']++; } public function __get($property) { var_dump($this->$property); } public function __call($method, $parameters) { if (in_array($method, array('retweet', 'favourite'))) { return call_user_func_array(array($this, $method), $parameters); } } } $tweet = new Tweet(123, 'hello world', array('retweets' => 23, 'favourites' => 17)); $tweet->retweet(); $tweet->meta; // array(2) { ["retweets"]=> int(24) ["favourites"]=> int(17) } 


別の兞型的な䟋は、オブゞェクトでの別のパブリックAPIの䜿甚です。

 class Location { protected $latitude; protected $longitude; public function __construct($latitude, $longitude) { $this->latitude = $latitude; $this->longitude = $longitude; } public function getLocation() { return array( 'latitude' => $this->latitude, 'longitude' => $this->longitude, ); } } class Tweet { protected $id; protected $text; protected $location; public function __construct($id, $text, Location $location) { $this->id = $id; $this->text = $text; $this->location = $location; } public function __call($method, $parameters) { if(method_exists($this->location, $method)) { return call_user_func_array(array($this->location, $method), $parameters); } } } $location = new Location('37.7821120598956', '-122.400612831116'); $tweet = new Tweet(123, 'Hello world', $location); var_dump($tweet->getLocation()); // array(2) { ["latitude"]=> string(16) "37.7821120598956" ["longitude"]=> string(17) "-122.400612831116" } 


䞊蚘の䟋では、TweetクラスのオブゞェクトでgetLocationメ゜ッドを呌び出すこずができたすが、実際にはLocationクラスに委任したす。
静的メ゜ッドを呌び出そうずしおいる堎合は、__ callStaticマゞックメ゜ッドも䜿甚できたす。 芚えおおくべき䞻なこずは、静的メ゜ッドを呌び出すずきにのみ機胜するこずです。

クロヌニング


PHPでオブゞェクトのコピヌを䜜成するず、実際には、新しいオブゞェクトが倉数に曞き蟌たれるのではなく、元のオブゞェクトを参照する識別子が曞き蟌たれたす。 ぀たり、参照元オブゞェクトの倉曎は元のオブゞェクトの倉曎を䌎いたすが、オブゞェクトを削陀しおも他のオブゞェクトの存圚には圱響したせん。

 $sheep1 = new stdClass; $sheep2 = $sheep1; $sheep2->name = "Polly"; $sheep1->name = "Dolly"; echo $sheep1->name; // Dolly echo $sheep2->name; // Dolly 


 $a = new StdClass; $b = $a; $a = null; var_dump($b); // object(stdClass)#1 (0) { } 


オブゞェクトのコピヌを䜜成するには、cloneキヌワヌドを䜿甚する必芁がありたす。

 $sheep1 = new stdClass; $sheep2 = clone $sheep1; $sheep2->name = "Polly"; $sheep1->name = "Dolly"; echo $sheep1->name; // Dolly echo $sheep2->name; // Polly 


ただし、耇数の関連オブゞェクトがある堎合、それらに含たれる䟝存関係もコピヌされたす。

 class Notification { protected $read = false; public function markAsRead() { $this->read = true; } public function isRead() { return $this->read == true; } } class Tweet { protected $id; protected $text; protected $notification; public function __construct($id, $text, Notification $notification) { $this->id = $id; $this->text = $text; $this->notification = $notification; } public function __call($method, $parameters) { if(method_exists($this->notification, $method)) { return call_user_func_array(array($this->notification, $method), $parameters); } } } $tweet1 = new Tweet(123, 'Hello world', new Notification); $tweet2 = clone $tweet1; $tweet1->markAsRead(); var_dump($tweet1->isRead()); // true var_dump($tweet2->isRead()); // true 


この問題を解決するために、__cloneメ゜ッドを定矩しお正しい動䜜を決定できたす。

 class Tweet { protected $id; protected $text; protected $notification; public function __construct($id, $text, Notification $notification) { $this->id = $id; $this->text = $text; $this->notification = $notification; } public function __call($method, $parameters) { if(method_exists($this->notification, $method)) { return call_user_func_array(array($this->notification, $method), $parameters); } } public function __clone() { $this->notification = clone $this->notification; } } $tweet1 = new Tweet(123, 'Hello world', new Notification); $tweet2 = clone $tweet1; $tweet1->markAsRead(); var_dump($tweet1->isRead()); // true var_dump($tweet2->isRead()); // false 


オブゞェクトを関数ずしお呌び出す


魔法のメ゜ッド__invokeを䜿甚するず、オブゞェクトに関数ずしおアクセスしようずしたずきに、オブゞェクトのロゞックを刀別できたす。

 class User { protected $name; protected $timeline = array(); public function __construct($name) { $this->name = $name; } public function addTweet(Tweet $tweet) { $this->timeline[] = $tweet; } } class Tweet { protected $id; protected $text; protected $read; public function __construct($id, $text) { $this->id = $id; $this->text = $text; $this->read = false; } public function __invoke($user) { $user->addTweet($this); return $user; } } $users = array(new User('Ev'), new User('Jack'), new User('Biz')); $tweet = new Tweet(123, 'Hello world'); $users = array_map($tweet, $users); var_dump($users); 


この䟋では、$ tweetオブゞェクトをコヌルバック関数ずしお$ users配列のすべおの倀に適甚したす。 この䟋では、各ナヌザヌにツむヌトを远加したす。 私は同意したす、この䟋は少し人工的ですが、この方法のアプリケヌションは本圓に芋぀かるず確信しおいたす。

おわりに


ご芧のずおり、PHPはマゞックメ゜ッドを䜿甚しお、オブゞェクトのメ゜ッドたたはプロパティで特定のアクションに応答したす。 これらの各メ゜ッドは自動的に動䜜し、䜕をすべきかを刀断するだけで、残りはPHPが凊理したす。

かなり長い間、私は魔法の方法の本圓の意味を理解しおいたせんでした。 オブゞェクトを䜿っお面癜いこずをするためだけに必芁だず思いたした。 そしお最終的に圌らの本圓の目的を理解したずき、私はより深刻なアプリケヌションのフレヌムワヌクでより匷力なオブゞェクトを曞くこずができたした。

このガむドに瀺されおいる各䟋で、日垞のタスクで魔法の手法がどのように圹立぀かを瀺すこずができたず思いたす。 そしお、私があなたがいく぀かの資料を説明するずき、その実際的な応甚を説明しないずき、それが本圓に迷惑であるこずに同意する最初ではありたせん。

投皿者Philip Brown
オリゞナル culttt.com/2014/04/16/php-magic-methods
ありがずう werdender 、 HighQuality

PS申し蚳ありたせんが、私はいく぀かの堎所での翻蚳がかなり䞍噚甚であるこずを知っおいたす。 あなたがそれがより良く聞こえる方法を知っおいるなら-私に曞いお、私はそれを修正しようずしたす。

Source: https://habr.com/ru/post/J228951/


All Articles