126

重複の可能性:
MagicメソッドはPHPのベストプラクティスですか?

これらは単純な例ですが、クラスに2つより多くのプロパティがあると想像してください。

ベストプラクティスは何でしょうか?

a)__getおよび__setを使用する

class MyClass {
    private $firstField;
    private $secondField;

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

    public function __set($property, $value) {
        if (property_exists($this, $property)) {
            $this->$property = $value;
        }
    }
}

$myClass = new MyClass();

$myClass->firstField = "This is a foo line";
$myClass->secondField = "This is a bar line";

echo $myClass->firstField;
echo $myClass->secondField;

/* Output:
    This is a foo line
    This is a bar line
 */

b)従来のセッターとゲッターを使用する

class MyClass {

    private $firstField;
    private $secondField;

    public function getFirstField() {
        return $this->firstField;
    }

    public function setFirstField($firstField) {
        $this->firstField = $firstField;
    }

    public function getSecondField() {
        return $this->secondField;
    }

    public function setSecondField($secondField) {
        $this->secondField = $secondField;
    }

}

$myClass = new MyClass();

$myClass->setFirstField("This is a foo line");
$myClass->setSecondField("This is a bar line");

echo $myClass->getFirstField();
echo $myClass->getSecondField();

/* Output:
    This is a foo line
    This is a bar line
 */

この記事の内容:http ://blog.webspecies.co.uk/2011-05-23/the-new-era-of-php-frameworks.html

著者は、魔法の方法を使用することは良い考えではないと主張しています。

まず第一に、当時はPHPの魔法の関数(__get、__ callなど)を使用することが非常に人気がありました。一見したところ何も問題はありませんが、実際には本当に危険です。それらはAPIを不明瞭にし、オートコンプリートを不可能にし、そして最も重要なことにそれらは遅いです。彼らのユースケースは、PHPをハックして、やりたくないことをすることでした。そしてそれはうまくいった。しかし、悪いことが起こりました。

しかし、これについてもっと意見を聞きたいと思います。

4

9 に答える 9

168

私は過去にあなたのケースに正確にいました。そして、私は魔法の方法に行きました。

これは間違いでした、あなたの質問の最後の部分はそれをすべて言います:

  • これは遅いです(ゲッター/セッターより)
  • オートコンプリートはなく(これは実際には大きな問題です)、リファクタリングとコードブラウジングのためのIDEによるタイプ管理@property(Zend Studio / PhpStormではこれはphpdocアノテーションで処理できますが、維持する必要があります:かなり痛み)
  • ドキュメント(phpdoc)は、コードの使用方法と一致していません。また、クラスを見ても、多くの答えは得られません。これは紛らわしいです。
  • 編集後に追加:プロパティのゲッターを持つことは、プライベートプロパティを返すだけでなく、実際のロジックを実行する「実際の」メソッドとより一貫性があります。getXXX()あなたは同じ名前を持っています。たとえば、$user->getName()(私有財産を返す)と$user->getToken($key)(計算された)があります。ゲッターがゲッター以上のものを取得し、何らかのロジックを実行する必要がある日でも、すべてが一貫しています。

最後に、これがIMOの最大の問題です。これは魔法です。そして、魔法は非常に悪いです。なぜなら、魔法を適切に使用するには、魔法がどのように機能するかを知る必要があるからです。それは私がチームで遭遇した問題です。あなただけでなく、誰もが魔法を理解する必要があります。

ゲッターとセッターは書くのが面倒ですが(私はそれらが嫌いです)、それだけの価値があります。

于 2011-05-31T08:30:57.733 に答える
131

オブジェクトが実際に「魔法」である場合にのみ、魔法を使用する必要があります。プロパティが固定されたクラシックオブジェクトがある場合は、セッターとゲッターを使用すると、正常に機能します。

オブジェクトに動的プロパティがある場合、たとえば、オブジェクトがデータベース抽象化レイヤーの一部であり、そのパラメーターが実行時に設定される場合は、便利な魔法のメソッドが必要です。

于 2011-05-31T08:42:36.560 に答える
91

__getコードをはるかに読みやすくするため、可能な限り(およびパブリックプロパティを)使用します。比較:

このコードは、私が何をしているのかを明確に示しています。

echo $user->name;

このコードは私を愚かに感じさせますが、私はそれを楽しんでいません:

function getName() { return $this->_name; }
....

echo $user->getName();

一度に複数のプロパティにアクセスする場合、この2つの違いは特に明白です。

echo "
    Dear $user->firstName $user->lastName!
    Your purchase:
        $product->name  $product->count x $product->price
"

echo "
    Dear " . $user->getFirstName() . " " . $user->getLastName() . "
    Your purchase: 
        " . $product->getName() . " " . $product->getCount() . "  x " . $product->getPrice() . " ";

本当に何か$a->bをするべきか、単に値を返すべきかは、呼び出し先の責任です。発信者にとっては、同じように見えるはずですが、後者は複雑な計算を伴う場合があります。私のデータクラスでは、次の小さなメソッドを使用します。$user->name$user->accountBalance

 function __get($p) { 
      $m = "get_$p";
      if(method_exists($this, $m)) return $this->$m();
      user_error("undefined property $p");
 }

誰かが呼び出し$obj->xxxてクラスがget_xxx定義されると、このメソッドが暗黙的に呼び出されます。したがって、必要に応じて、インターフェイスを均一で透過的に保ちながら、ゲッターを定義できます。追加のボーナスとして、これは計算を記憶するためのエレガントな方法を提供します。

  function get_accountBalance() {
      $result = <...complex stuff...>
      // since we cache the result in a public property, the getter will be called only once
      $this->accountBalance = $result;
  }

  ....


   echo $user->accountBalance; // calculate the value
   ....
   echo $user->accountBalance; // use the cached value

結論:phpは動的スクリプト言語です。そのように使用してください。JavaやC#を使用しているふりをしないでください。

于 2011-05-31T09:25:57.333 に答える
2

私は3番目の解決策に投票します。私は自分のプロジェクトでこれを使用しており、Symfonyも次のようなものを使用しています。

public function __call($val, $x) {
    if(substr($val, 0, 3) == 'get') {
        $varname = strtolower(substr($val, 3));
    }
    else {
        throw new Exception('Bad method.', 500);
    }
    if(property_exists('Yourclass', $varname)) {
        return $this->$varname;
    } else {
        throw new Exception('Property does not exist: '.$varname, 500);
    }
}

このようにして、自動ゲッター(セッターを作成することもできます)があり、メンバー変数に特別な場合がある場合にのみ、新しいメソッドを作成する必要があります。

于 2011-05-31T07:44:19.553 に答える
2

私はedemの答えとあなたの2番目のコードを組み合わせて行います。このように、一般的なゲッター/セッター(IDEでのコード補完)、必要に応じたコーディングの容易さ、存在しないプロパティによる例外(タイプミスの発見に最適:$foo->naemではなく$foo->name)、読み取り専用プロパティ、および複合プロパティの利点があります。

class Foo
{
    private $_bar;
    private $_baz;

    public function getBar()
    {
        return $this->_bar;
    }

    public function setBar($value)
    {
        $this->_bar = $value;
    }

    public function getBaz()
    {
        return $this->_baz;
    }

    public function getBarBaz()
    {
        return $this->_bar . ' ' . $this->_baz;
    }

    public function __get($var)
    {
        $func = 'get'.$var;
        if (method_exists($this, $func))
        {
            return $this->$func();
        } else {
            throw new InexistentPropertyException("Inexistent property: $var");
        }
    }

    public function __set($var, $value)
    {
        $func = 'set'.$var;
        if (method_exists($this, $func))
        {
            $this->$func($value);
        } else {
            if (method_exists($this, 'get'.$var))
            {
                throw new ReadOnlyException("property $var is read-only");
            } else {
                throw new InexistentPropertyException("Inexistent property: $var");
            }
        }
    }
}
于 2011-05-31T08:09:10.490 に答える
-2

2番目のコード例は、に与えられたデータを完全に制御しているため、これを行うためのはるかに適切な方法ですclass__set__getが役立つ場合がありますが、この場合はそうではありません。

于 2011-05-31T07:38:10.727 に答える
-3

魔法のメンバーが必要な場合、クラスを作成する場合はstdClassを使用する必要があります-クラスに含まれるものを定義します。

于 2011-05-31T07:38:50.193 に答える
-3

ベストプラクティスは、内省または反省のために、従来のゲッターとセッターを使用することです。PHPには(Javaとまったく同じように)メソッドまたはすべてのメソッドの名前を取得する方法があります。このようなものは、最初のケースでは「__get」を返し、2番目のケースでは「getFirstField」、「getSecondField」(およびセッター)を返します。

詳細: http: //php.net/manual/en/book.reflection.php

于 2011-05-31T07:43:24.170 に答える
-4

私は今、セッターとゲッターに戻っていますが、ゲッターとセッターを魔法の方法__getと__setに入れています。このように、これを行うとデフォルトの動作になります

$ class-> var;

これは、__getで設定したゲッターを呼び出すだけです。通常はゲッターを直接使用しますが、これが単純な場合もあります。

于 2011-05-31T09:18:18.733 に答える