可能性のある複製:
マジックメソッドは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が不明瞭になり、オートコンプリートが不可能になり、最も重要なことに、APIが遅くなります。彼らのユースケースは、PHPをハックして、望まないことをすることでした。そしてそれは働いた。しかし、悪いことが起こりました。
しかし、私はこれについてもっと意見を聞きたいです。
私は過去にまさにあなたの場合でした。そして、私は魔法の方法に行きました。
これは間違いでした、あなたの質問の最後の部分はそれをすべて言っています:
@property
phpdocアノテーションで処理できますが、それらを維持する必要があります:非常に苦痛です)getXXX()
はプライベートプロパティを返すだけでなく、実際のロジックを実行します。同じ名前が付いています。たとえば、$user->getName()
(プライベートプロパティを返す)と$user->getToken($key)
(計算済み)があります。ゲッターがゲッター以上のものを取得し、何らかのロジックを実行する必要がある日でも、すべてが一貫しています。最後に、これがIMOの最大の問題です。これは魔法です。そして、魔法は非常に非常に悪いものです。それは、魔法を適切に使用するには、魔法がどのように機能するかを知る必要があるからです。それは私がチームで出会った問題です。あなただけではなく、誰もが魔法を理解しなければなりません。
ゲッターとセッターは書くのが苦手です(私はそれらを嫌います)が、彼らはそれだけの価値があります。
オブジェクトが実際に「魔法」である場合にのみ、魔法を使用する必要があります。プロパティが固定されたクラシックオブジェクトがある場合、setterとgetterを使用すると、それらは正常に機能します。
オブジェクトに動的プロパティがあり、たとえばデータベース抽象化レイヤーの一部であり、そのパラメーターが実行時に設定される場合、実際には便利なマジックメソッドが必要です。
コードを読みやすくするため、__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
が本当にdo何かをすべきか、単に値を返すかは、呼び出し先の責任です。呼び出し元の場合、$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#を実行しているふりをしないでください。
私は、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");
}
}
}
}
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);
}
}
この方法では、自動化されたゲッターがあり(セッターも作成できます)、メンバー変数に特別なケースがある場合にのみ新しいメソッドを作成する必要があります。