2008-08-28 3 views
8

Ich werde etwas mit dem Format dieser Frage versuchen und ich bin sehr offen für Vorschläge über einen besseren Weg, damit umzugehen.Ist dies ein vernünftiger Weg, um Getter/Setter in einer PHP-Klasse zu behandeln?

Ich wollte nicht einfach einen Haufen Code in der Frage ablegen, also habe ich den Code für die Klasse auf refactormycode gepostet.

base class for easy class property handling

Mein Gedanke war, dass die Menschen entweder Post Code-Schnipsel hier oder Änderungen auf refactormycode und Post-Links zurück zu ihren Refactorings. Ich werde Upvotes machen und darauf basierend eine Antwort akzeptieren (vorausgesetzt es gibt einen klaren "Gewinner").

Auf jeden Fall auf die Klasse selbst:

ich eine Menge Debatte über Getter/Setter-Klasse Methoden sehen und ist es besser, nur einfache Eigenschaftsvariablen direkt zugreifen oder sollte jede Klasse haben explizite get/set Methoden definiert, blah blah blah. Ich mag die Idee, explizite Methoden zu haben, falls Sie später mehr Logik hinzufügen müssen. Dann müssen Sie keinen Code ändern, der die Klasse verwendet. Aber ich hasse es, eine Million Funktionen haben, die wie folgt aussehen:

public function getFirstName() 
{ 
    return $this->firstName; 
} 
public function setFirstName($firstName) 
{ 
    return $this->firstName; 
} 

Jetzt bin ich sicher, ich bin nicht die erste Person, dies zu tun (ich hoffe, dass es einen besseren Weg, es zu tun, dass jemand kann schlage mir vor).

Grundsätzlich hat die PropertyHandler-Klasse eine __call magische Methode. Alle Methoden, die durch __call kommen und mit "get" oder "set" beginnen, werden dann an Funktionen weitergeleitet, die Werte in einem assoziativen Array setzen oder abrufen. Der Schlüssel in das Array ist der Name der aufrufenden Methode nach get oder set. Wenn also die Methode, die in __call eingeht, "getFirstName" lautet, lautet der Array-Schlüssel "FirstName".

Ich mochte __call verwenden, weil es automatisch den Fall behandelt, in dem die Unterklasse bereits eine "getFirstName" -Methode definiert hat. Mein Eindruck (und ich kann falsch liegen) ist, dass die __get & __set magischen Methoden das nicht tun.

Also hier ist ein Beispiel dafür, wie es funktionieren würde:

class PropTest extends PropertyHandler 
{ 
    public function __construct() 
    { 
     parent::__construct(); 
    } 
} 

$props = new PropTest(); 

$props->setFirstName("Mark"); 
echo $props->getFirstName(); 

Beachten Sie, dass PropTest nicht wirklich „setFirstName“ oder „getFirstName“ Methoden und auch nicht Property. Es werden nur Array-Werte manipuliert.

Der andere Fall wäre, wo Ihre Unterklasse bereits etwas anderes erweitert. Da Sie in PHP keine echte Mehrfachvererbung haben können, können Sie Ihre Unterklasse dazu veranlassen, eine PropertyHandler-Instanz als private Variable zu verwenden. Sie müssen eine weitere Funktion hinzufügen, aber dann verhalten sich die Dinge genau so.

class PropTest2 
{ 
    private $props; 

    public function __construct() 
    { 
     $this->props = new PropertyHandler(); 
    } 

    public function __call($method, $arguments) 
    { 
     return $this->props->__call($method, $arguments); 
    } 
} 

$props2 = new PropTest2(); 

$props2->setFirstName('Mark'); 
echo $props2->getFirstName(); 

Beachten Sie, wie die Unterklasse eine __call Methode verfügt, die einfach alles verläuft entlang zum Property __call Methode.

Ein weiteres gutes Argument gegen die Handhabung von Getter und Setter ist, dass es wirklich schwer zu dokumentieren ist.

In der Tat ist es im Grunde unmöglich, irgendeine Art von Dokumentgenerierungswerkzeug zu verwenden, da die expliziten Methoden, die nicht dokumentiert werden sollen, nicht existieren.

Ich habe diesen Ansatz für jetzt so gut wie aufgegeben. Es war eine interessante Lernübung, aber ich denke, es opfert zu viel Klarheit.

+0

Aus Neugier, welche Methode verwenden Sie jetzt? – SeanJA

+0

Getters ** und ** Setter in der gleichen Klasse ist ein Zeichen, dass die Klasse ihren Code fehlt. Untersuchen Sie den Code, der die Getter und Setter der Klasse verwendet, und verschieben Sie ihn innerhalb der Klasse, da er dort hingehört. – axiac

Antwort

5

So wie ich es tun, ist die folgende:

class test { 
    protected $x=''; 
    protected $y=''; 

    function set_y ($y) { 
     print "specific function set_y\n"; 
     $this->y = $y; 
    } 

    function __call($function , $args) { 
     print "generic function $function\n"; 
     list ($name , $var) = split ('_' , $function); 
     if ($name == 'get' && isset($this->$var)) { 
      return $this->$var; 
     } 
     if ($name == 'set' && isset($this->$var)) { 
      $this->$var= $args[0]; 
      return; 
     } 
     trigger_error ("Fatal error: Call to undefined method test::$function()"); 
    } 
} 

$p = new test(); 
$p->set_x(20); 
$p->set_y(30); 
print $p->get_x(); 
print $p->get_y(); 

$p->set_z(40); 

Welche Ausgang (Umbrüche Zeile hinzugefügt für Klarheit) wird

generic function set_x 
specific function set_y 

generic function get_x 
20 
generic function get_y 
30 

generic function set_z 
Notice: Fatal error: Call to undefined method set_z() in [...] on line 16 
+0

(bewegender Kommentar von OP): @Pat Das ist interessant. Sicherlich viel weniger Code. Aber Sie müssen immer noch jede zugrunde liegende Eigenschaftsvariable deklarieren, richtig? –

+0

"Split" ist seit PHP 5.3.0 veraltet. –

2

Ja, das stimmt die Variablen manuell deklariert werden müssen, aber ich finde dass eine bessere befürchten, da ich einen Tippfehler in der Setter

$props2->setFristName('Mark'); 

wird eine neue Eigenschaft automatisch generieren (FristName anstelle von FirstName), wodurch das Debugging schwieriger wird.

1

Ich mag Methoden anstatt nur öffentliche Felder zu verwenden, aber mein Problem mit der PHP-Standardimplementierung (mit __get() und __set()) oder Ihrer benutzerdefinierten Implementierung ist, dass Sie Getter und Setter nicht einrichten eine pro-Eigenschaft Basis. Mein Problem dabei ist, dass das Hinzufügen von "mehr Logik später" erfordert, dass Sie pauschale Logik hinzufügen, die für alle Eigenschaften gilt, auf die mit dem Getter/Setter zugegriffen wird oder die Sie verwenden, wenn Sie Anweisungen ändern, um zu ermitteln, auf welche Eigenschaft Sie zugreifen spezifische Logik.

Ich mag Ihre Lösung, und ich applaudiere Ihnen dafür - ich bin einfach nicht zufrieden mit den Einschränkungen, die PHP hat, wenn es um implizite Zugriffsmethoden geht.

+0

(bewegender Kommentar von OP) @Brian Das ist ein interessanter Punkt. Ich werde das etwas übertreiben müssen. –

3

@Brian

Mein Problem dabei ist, dass „mehr Logik später“ Hinzufügen erfordert, dass Sie Decke Logik hinzufügen, die mit dem Getter/Setter auf alle Eigenschaften zugegriffen gilt, oder dass Sie verwenden if oder switch-Anweisungen, um zu ermitteln, auf welche Eigenschaft Sie zugreifen, damit Sie eine bestimmte Logik anwenden können.

Das ist nicht ganz richtig ist. Nehmen Sie mein erstes Beispiel:

class PropTest extends PropertyHandler 
{ 
    public function __construct() 
    { 
     parent::__construct(); 
    } 
} 

$props = new PropTest(); 

$props->setFirstName("Mark"); 
echo $props->getFirstName(); 

Sagen wir, dass ich einige Logik für die Validierung von FirstNames hinzufügen muss. Alles, was ich tun muss, ist eine Methode setFirstName zu meiner Unterklasse hinzuzufügen und diese Methode wird stattdessen automatisch verwendet.

class PropTest extends PropertyHandler 
{ 
    public function __construct() 
    { 
     parent::__construct(); 
    } 

    public function setFirstName($name) 
    { 
     if($name == 'Mark') 
     { 
      echo "I love you, Mark!"; 
     } 
    } 
} 

Ich bin einfach nicht zufrieden mit den Einschränkungen, die PHP hat, wenn es um implizite Accessormethoden kommt.

stimme ich vollständig. Ich mag die Python-Art, dies zu handhaben (meine Implementierung ist nur eine ungeschickte Abzocke davon).

1

@ Mark

Aber selbst erfordert Ihre Methode eine neue Erklärung des Verfahrens, und es dauert etwas den Vorteil, legt es in einem Verfahren entfernt, so dass Sie mehr Logik hinzufügen können, weil mehr Logik hinzuzufügen, erfordert die altmodische Erklärung der Methode sowieso. In ihrem Standardzustand (wo sie beeindruckend ist, was sie erkennt/tut), bietet Ihre Technik keinen Vorteil (in PHP) gegenüber öffentlichen Feldern. Sie beschränken den Zugriff auf das Feld, aber geben Carte Blanche durch Zugriffsmethoden, die keine eigenen Einschränkungen haben. Ich bin mir nicht bewusst, dass unkontrollierte explizite Accessoren einen Vorteil gegenüber öffentlichen Feldern in jeder Sprache bieten, aber Menschen können und sollten sich frei fühlen, mich zu korrigieren, wenn ich falsch liege.

+0

Alle Eigenschaften, die mit einem grundlegenden Getter/Setter in Ordnung sind, werden die magischen Aufrufe verwenden. Wenn er zusätzlichen Code hinzufügen muss, kann er Getter/Setter nur für diese Eigenschaften erstellen. Auch dies kann Verwirrung stiften, aber es ist eine Option. – Galen

0

Ich habe dieses Problem immer in einer ähnlichen mit einem __call behandelt, die in vielen meiner Klassen so ziemlich wie Kesselplatte Code endet. Es ist jedoch kompakt und verwendet die Reflektionsklassen, um nur Getter/Setter für Eigenschaften hinzuzufügen, die Sie bereits festgelegt haben (es werden keine neuen hinzugefügt). Wenn Sie den Getter/Setter explizit hinzufügen, werden komplexere Funktionen hinzugefügt. Es erwartet

-Code sieht wie folgt zu sein:

/** 
* Handles default set and get calls 
*/ 
public function __call($method, $params) { 

    //did you call get or set 
    if (preg_match("|^[gs]et([A-Z][\w]+)|", $method, $matches)) { 

     //which var? 
     $var = strtolower($matches[1]); 

     $r = new ReflectionClass($this); 
     $properties = $r->getdefaultProperties(); 

     //if it exists 
     if (array_key_exists($var,$properties)) { 
      //set 
      if ('s' == $method[0]) { 
       $this->$var = $params[0]; 
      } 
      //get 
      elseif ('g' == $method[0]) { 
       return $this->$var; 
      } 
     } 
    } 
} 

dies zu einer Klasse hinzufügen, wo Sie Standardeigenschaften wie erklärt haben:

class MyClass { 
    public $myvar = null; 
} 

$test = new MyClass; 
$test->setMyvar = "arapaho"; 

echo $test->getMyvar; //echos arapaho  

Die Reflexionsklasse kann etwas Nutzungs hinzuzufügen, was du hast vorgeschlagen. Ordentliche Lösung @Mark.

0

Erst kürzlich habe ich darüber nachgedacht, Getter und Setter so zu behandeln, wie du es vorgeschlagen hast (der zweite Ansatz war mein Favorit, das private $ Requisiten-Array), aber ich habe es verworfen, weil es in meiner App nicht geklappt hätte .

Ich arbeite an einer ziemlich großen SoapServer-basierten Anwendung und die Soap-Schnittstelle von PHP 5 injiziert die Werte, die über Soap direkt in die zugehörige Klasse übertragen werden, ohne über vorhandene oder nicht vorhandene Eigenschaften in der Klasse zu stören.

0

ich nicht in meinem 2 Cent setzen helfen ...

ich mit __get und __set in diesem Herrn http://gist.github.com/351387 (ähnlich die Art und Weise, die Lehre es tut) genommen habe, dann immer nur die Eigenschaften zugreifen über die $obj->var in einem außerhalb der Klasse. Auf diese Weise können Sie die Funktionalität nach Bedarf überschreiben, anstatt eine große __get oder __set Funktion zu erstellen oder __get und __set in den untergeordneten Klassen zu überschreiben.

Verwandte Themen