Getter und Setter?

203

Ich bin kein PHP-Entwickler, daher frage ich mich, ob es in PHP beliebter ist, explizite Getter / Setter in einem reinen OOP-Stil mit privaten Feldern zu verwenden (wie ich es mag):

class MyClass {
    private $firstField;
    private $secondField;

    public function getFirstField() {
        return $this->firstField;
    }
    public function setFirstField($x) {
        $this->firstField = $x;
    }
    public function getSecondField() {
        return $this->secondField;
    }
    public function setSecondField($x) {
        $this->secondField = $x;
    }
}

oder nur öffentliche Felder:

class MyClass {
    public $firstField;
    public $secondField;
}

Vielen Dank

Kennzeichen
quelle
7
Nachdem ich Code aus den Antworten ausprobiert hatte, verwendete ich den Code, den Sie in der Frage verwenden. Wie traurig :-(
sumid
9
PHPstorm ... generieren> Getter und Setter. == win
DevDonkey
@ DevDonkey Überhaupt kein Gewinn. Verwenden Sie stattdessen Arrays, um strukturierte Daten zu speichern. @: Mark Dies ist nicht das, wofür Objekte sind oder sind. Getter und Setter sind böse: yegor256.com/2014/09/16/getters-and-setters-are-evil.html
Kubo2

Antworten:

222

Sie können PHP Magic Methoden __get und verwenden __set.

<?php
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;
    }

    return $this;
  }
}
?>
Davis Peixoto
quelle
15
Ich denke du meinst __getund __set. Es gibt zwei Unterstriche, keinen. Hier ist der direkte Link zum rechten Teil der Seite: php.net/manual/en/… (+1 für eine korrekte Antwort)
Computerish
28
Was ist der Vorteil gegenüber publicImmobilien, wenn es keine Validierung / Hygiene gibt?
KingCrunch
7
@KingCrunch, dies ist nur ein Beispiel. Ein sehr sehr dummes Beispiel für eine mächtige Ressource.
Davis Peixoto
10
Das ist nicht wirklich Setter und Getter. Normalerweise brauche ich für jede Eigenschaft eine andere Implementierung von Getter!
Summe
79
Bitte nicht: Mit magischen Methoden verlieren Sie fast alle qualitätsbezogenen Funktionen in vielen IDE (sogar vim): automatische Vervollständigung, explizite PHP-Vererbung, schnelle PHP-Interpretation und nützliche PHPDoc-Generierung und -Ausgabe. vgl. stackoverflow.com/a/6184893/490589
Ronan
113

Warum Getter und Setter verwenden?

  1. Skalierbarkeit : Es ist einfacher, einen Getter umzugestalten, als alle var-Zuweisungen in einem Projektcode zu durchsuchen.
  2. Debugging : Sie können Haltepunkte an Setter und Getter setzen.
  3. Reiniger : Magische Funktionen sind keine gute Lösung, um weniger zu schreiben. Ihre IDE schlägt den Code nicht vor. Verwenden Sie besser Vorlagen für schnell schreibende Getter.

direkte Zuordnung und Getter / Setter

Wiliam
quelle
7
Wenn Sie @property verwenden, schlägt Ihre IDE den Code vor (getestet mit PhpStorm 7)
Alex2php
41

Google hat bereits einen Leitfaden zur Optimierung von PHP veröffentlicht. Die Schlussfolgerung lautete:

Kein Getter und Setter PHP optimieren

Und nein, Sie dürfen keine magischen Methoden anwenden . Für PHP sind Magic Method böse. Warum?

  1. Sie sind schwer zu debuggen.
  2. Es gibt negative Auswirkungen auf die Leistung.
  3. Sie erfordern das Schreiben von mehr Code.

PHP ist nicht Java, C ++ oder C #. PHP ist anders und spielt mit verschiedenen Rollen.

Magallane
quelle
10
Ich stimme dieser Idee eher zu. das $dog->name = 'fido'ist besser als $dog->setName('fido'). Wenn eine Eigenschaft tatsächlich mutiert wird (Beispiel: $dog->increaseAge(1)Ich kann die Methode ausbauen, die die erforderliche Validierung durchführt und diese Eigenschaft mutiert. Aber nicht alle Aktionen erfordern wirklich eine Mutation in diesem Sinne.
Charlie Schliesser
11
Der Artikel sagt nicht " nicht ", sondern "naive Setter und Getter".
Brett Santore
13
Man kann davon ausgehen, dass ein Artikel von Google mit der Überschrift "PHP-Leistungstipps" NICHT einen guten Codierungsstil, sondern eine schnelle Codeausführung suggerieren soll. Das Kapitel, das sich mit Setzern und Gettern befasst, trägt die Bezeichnung "Vermeiden Sie es, naive Setter und Getter zu schreiben", und das Codebeispiel lautet genau das: Naiv. Es setzt und erhält nur eine Variable ohne Validierung. Diese Art von Setter / Getter ist nutzlos. Wenn Sie die Validierung in einem Setter durchführen (verwenden Sie einfach einen Typhinweis für das Methodenargument), sind Setter / Getter nützlich, da Ihr Code jetzt weiß, womit er es zu tun hat.
Sven
3
Dies ist wie zu sagen, dass Inline-Styling besser ist. Natürlich ist die Leistung besser, aber ist es besserer Code? Ich wusste sowieso nicht, dass Google-Ingenieure PHP verwenden
Claudiu Creanga
13

Kapselung ist in jeder OO-Sprache wichtig, Popularität hat nichts damit zu tun. In dynamisch typisierten Sprachen wie PHP ist dies besonders nützlich, da es kaum Möglichkeiten gibt, sicherzustellen, dass eine Eigenschaft von einem bestimmten Typ ist, ohne Setter zu verwenden.

In PHP funktioniert dies:

class Foo {
   public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";

In Java ist dies nicht der Fall:

class Foo {
   public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error

Die Verwendung magischer Methoden ( __getund __set) funktioniert ebenfalls, jedoch nur, wenn auf eine Eigenschaft zugegriffen wird, die eine geringere Sichtbarkeit als der aktuelle Bereich aufweist. Es kann leicht Kopfschmerzen beim Debuggen verursachen, wenn es nicht richtig verwendet wird.

Netcoder
quelle
7
Getter und Setter bringen keine Kapselung. Encapsulation == Objekte tun etwas mit ihren eigenen Daten, anstatt sie außerhalb zu geben. Getter und Setter sind kein Werkzeug zum Erzwingen von Typ in dynamisch typisierten Sprachen wie PHP.
Smentek
14
@smentek: Du vermisst eindeutig mindestens die Hälfte dessen, was Kapselung wirklich ist.
Netcoder
2
Als Update für alle, die es suchen, wird PHP 7.4 mit Unterstützung für typisierte Eigenschaften geliefert. So könnten Sie $barals intim ersten Beispiel deklarieren
Kevin
7

Wenn Sie die Funktion __call bevorzugen, können Sie diese Methode verwenden. Es funktioniert mit

  • GET => $this->property()
  • SET => $this->property($value)
  • GET => $this->getProperty()
  • SET => $this->setProperty($value)

kalsdas

public function __call($name, $arguments) {

    //Getting and setting with $this->property($optional);

    if (property_exists(get_class($this), $name)) {


        //Always set the value if a parameter is passed
        if (count($arguments) == 1) {
            /* set */
            $this->$name = $arguments[0];
        } else if (count($arguments) > 1) {
            throw new \Exception("Setter for $name only accepts one parameter.");
        }

        //Always return the value (Even on the set)
        return $this->$name;
    }

    //If it doesn't chech if its a normal old type setter ot getter
    //Getting and setting with $this->getProperty($optional);
    //Getting and setting with $this->setProperty($optional);
    $prefix = substr($name, 0, 3);
    $property = strtolower($name[3]) . substr($name, 4);
    switch ($prefix) {
        case 'get':
            return $this->$property;
            break;
        case 'set':
            //Always set the value if a parameter is passed
            if (count($arguments) != 1) {
                throw new \Exception("Setter for $name requires exactly one parameter.");
            }
            $this->$property = $arguments[0];
            //Always return the value (Even on the set)
            return $this->$name;
        default:
            throw new \Exception("Property $name doesn't exist.");
            break;
    }
}
J-Rou
quelle
2
@ krzysztof-przygoda: Diese "Magic Methods" sind immer mit einem Preis verbunden. Sie müssen Rekursion verwenden property_exists(get_class($this), $name)und die Rekursion ist langsam. Es gibt eine Möglichkeit, dies durch Cacheing zu verringern, aber es wird immer noch langsamer sein, als die Getter und Setter von Hand zu erstellen. Ich habe das nur als Alternative geschrieben. Ich empfehle eigentlich nicht, "Magic Methods" zu verwenden. Die zusätzliche Zeit zum Erstellen der Getter und Setter ist normalerweise unbedeutend.
J-Rou
7

Zusätzlich zu den bereits großartigen und angesehenen Antworten hier möchte ich PHP ohne Setter / Getter erweitern.

PHP hat keine Getter- und Setter-Syntax . Es bietet untergeordnete oder magische Methoden, um das "Verknüpfen" und Überschreiben des Eigenschaftssuchprozesses zu ermöglichen, wie von Dave hervorgehoben .

Mit Magic können wir faulen Programmierer mit weniger Code mehr erreichen, wenn wir aktiv an einem Projekt beteiligt sind und es genau kennen, aber normalerweise auf Kosten der Lesbarkeit.

Leistung Jede unnötige Funktion, die sich aus dem Erzwingen einer Getter / Setter-ähnlichen Code-Architektur in PHP ergibt, beinhaltet beim Aufruf einen eigenen Speicher-Stack-Frame und verschwendet CPU-Zyklen.

Lesbarkeit: Die Codebasis führt zu aufgeblähten Codezeilen, was sich auf die Code-Navigation auswirkt, da mehr LOC mehr Scrollen bedeutet.

Präferenz: Persönlich nehme ich als Faustregel das Versagen der statischen Code-Analyse als Zeichen, um zu vermeiden, den magischen Weg zu gehen, solange mir zu diesem Zeitpunkt offensichtliche langfristige Vorteile entgehen.

Irrtümer:

Ein häufiges Argument ist die Lesbarkeit. Zum Beispiel $someobject->widthist das leichter zu lesen als $someobject->width(). Im Gegensatz zu der Instanz eines Planeten circumferenceoder width, wie angenommen werden kann static, nimmt die Instanz eines Objekts, wie beispielsweise $someobjecteine Breitenfunktion, wahrscheinlich eine Messung der Instanzbreite des Objekts vor.
Daher erhöht sich die Lesbarkeit hauptsächlich aufgrund durchsetzungsfähiger Benennungsschemata und nicht durch Ausblenden der Funktion, die einen bestimmten Eigenschaftswert ausgibt.

__get / __set verwendet:

  • Vorvalidierung und Hygiene von Immobilienwerten

  • Saiten zB

    "
    some {mathsobj1->generatelatex} multi
    line text {mathsobj1->latexoutput}
    with lots of variables for {mathsobj1->generatelatex}
     some reason
    "

    In diesem Fall generatelatexwürde ein Namensschema von Aktionsname + Methodenname eingehalten

  • besondere, offensichtliche Fälle

    $dnastringobj->homeobox($one_rememberable_parameter)->gattaca->findrelated()
    $dnastringobj->homeobox($one_rememberable_parameter)->gttccaatttga->findrelated()

Hinweis: PHP hat sich dafür entschieden, die Getter / Setter-Syntax nicht zu implementieren. Ich behaupte nicht, dass Getter / Setter im Allgemeinen schlecht sind.

Lorenz Lo Sauer
quelle
6
class MyClass {
    private $firstField;
    private $secondField;
    private $thirdField;

    public function __get( $name ) {
        if( method_exists( $this , $method = ( 'get' . ucfirst( $name  ) ) ) )
            return $this->$method();
        else
            throw new Exception( 'Can\'t get property ' . $name );
    }

    public function __set( $name , $value ) {
        if( method_exists( $this , $method = ( 'set' . ucfirst( $name  ) ) ) )
            return $this->$method( $value );
        else
            throw new Exception( 'Can\'t set property ' . $name );
    }

    public function __isset( $name )
    {
        return method_exists( $this , 'get' . ucfirst( $name  ) ) 
            || method_exists( $this , 'set' . ucfirst( $name  ) );
    }

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

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

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

$obj = new MyClass();

echo $obj->firstField; // works
$obj->firstField = 'value'; // works

echo $obj->getFirstField(); // works
$obj->setFirstField( 'value' ); // not works, method is protected

echo $obj->secondField; // works
echo $obj->getSecondField(); // not works, method is private

$obj->secondField = 'value'; // not works, setter not exists

echo $obj->thirdField; // not works, property not exists

isset( $obj->firstField ); // returns true
isset( $obj->secondField ); // returns true
isset( $obj->thirdField ); // returns false

Bereit!

Joas
quelle
Zu viel Boilerplate. Stellen Sie sich vor, dieses Zeug ist in jeder Klasse. Vermeiden Sie IMO
DarkNeuron
PHP unterstützt Getter und Setter aus den gleichen Gründen, die Sie erwähnen, nicht. Jede Implementierung dieses Typs beeinträchtigt die Leistung von serverseitigen Skripten erheblich.
Joas
Ich denke, das widerspricht "privaten" Immobilien. Sie kapseln sie, erlauben aber auch den direkten Zugriff.
Koray Küpe
@ KorayKüpe nur wenn der Getter definiert ist. Ich benutze diese Kapselung oft (mit vielen Verbesserungen) und sie funktioniert perfekt. Sie können die Klasse auch erweitern und problemlos im gesamten Code verwenden.
Joas
5

Nun, PHP zaubern Methoden __get, __set, __isset& __unset, das ist immer ein Anfang. Leider sind OO-Eigenschaften mehr als magische Methoden. Das Hauptproblem bei der Implementierung von PHP besteht darin, dass für alle unzugänglichen Eigenschaften magische Methoden aufgerufen werden. Das heißt, Sie müssen sich in den magischen Methoden wiederholen (z. B. durch Aufrufen von property_exists ()), um festzustellen, ob der Name tatsächlich eine Eigenschaft Ihres Objekts ist. Und Sie können dieses allgemeine Problem nicht wirklich mit einer Basisklasse lösen, es sei denn, alle Ihre Klassen erben von dh. ClassWithProperties, da PHP keine Mehrfachvererbung aufweist.

Im Gegensatz dazu erhalten Sie neue Python- Stilklassen property(), mit denen Sie alle Ihre Eigenschaften explizit definieren können. C # hat eine spezielle Syntax.

http://en.wikipedia.org/wiki/Property_(programming)

Emanuel Landeholm
quelle
1
Das Aufrufen von property_exists, class_vars oder array_key_exists (dh das Überprüfen, ob die Eigenschaft tatsächlich vorhanden ist) ist nur ein Schritt, um einen schwerwiegenden Laufzeitfehler zu vermeiden. Ich bin mir nicht sicher, ob es gleichbedeutend ist, sich beim Codieren zu wiederholen.
Davis Peixoto
1
Meinetwegen. In Python und C # ist diese Wiederholung jedoch nicht erforderlich. Ich denke das ist eine Stärke.
Emanuel Landeholm
4

Ich habe ein Experiment mit der magischen Methode __call gemacht. Ich bin mir nicht sicher, ob ich es posten soll (wegen all der Warnungen "KEINE MAGISCHEN METHODEN VERWENDEN" in den anderen Antworten und Kommentaren), aber ich werde es hier lassen ... nur für den Fall, dass jemand es nützlich findet.


public function __call($_name, $_arguments){
    $action  = substr($_name, 0, 4);
    $varName = substr($_name, 4);

    if (isset($this->{$varName})){
        if ($action === "get_") return $this->{$varName};
        if ($action === "set_") $this->{$varName} = $_arguments[0];
    }
}

Fügen Sie diese Methode einfach oben in Ihre Klasse ein. Jetzt können Sie Folgendes eingeben:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_foo(); // return "bar"
$C->get_bom(); // return "bim"

// as setter
$C->set_foo("abc"); // set "abc" as new value of foo
$C->set_bom("zam"); // set "zam" as new value of bom


Auf diese Weise können Sie alles in Ihrer Klasse abrufen / festlegen, sofern es vorhanden ist. Wenn Sie es also nur für einige bestimmte Elemente benötigen, können Sie eine "Whitelist" als Filter verwenden.

Beispiel:

private $callWhiteList = array(
    "foo" => "foo",
    "fee" => "fee",
    // ...
);

public function __call($_name, $_arguments){
    $action  = substr($_name, 0, 4);
    $varName = $this->callWhiteList[substr($_name, 4)];

    if (!is_null($varName) && isset($this->{$varName})){
        if ($action === "get_") return $this->{$varName};
        if ($action === "set_") $this->{$varName} = $_arguments[0];
    }
}

Jetzt können Sie nur noch "foo" und "fee" erhalten / einstellen.
Sie können diese "Whitelist" auch verwenden, um benutzerdefinierte Namen für den Zugriff auf Ihre Variablen zuzuweisen.
Beispielsweise,

private $callWhiteList = array(
    "myfoo" => "foo",
    "zim" => "bom",
    // ...
);

Mit dieser Liste können Sie nun Folgendes eingeben:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // private $callWhiteList = array( ... )
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_myfoo(); // return "bar"
$C->get_zim(); // return "bim"

// as setter
$C->set_myfoo("abc"); // set "abc" as new value of foo
$C->set_zim("zam"); // set "zam" as new value of bom

.
.
.
Das ist alles.


Doc: __call () wird ausgelöst, wenn unzugängliche Methoden in einem Objektkontext aufgerufen werden .

Sabaz
quelle
Das Problem bei all diesen "magischen" Lösungen ist, dass sie einfach diese magischen Methoden verwenden, weil sie da sind, und sie sind nützlich, weil das gestellte Problem ein einfaches, allgemeines ist. Sobald Sie diese Ebene des allgemeinen Problems verlassen, werden Sie auf bestimmte Anforderungen stoßen, die nicht mit einer einfachen magischen Methode gelöst werden können, sondern eine sehr komplexe magische Methode erfordern würden - oder einzelne, codierte Setter und Getter.
Sven
2

Nachdem ich die anderen Ratschläge gelesen habe, neige ich dazu zu sagen:

Als GENERISCHE Regel definieren Sie nicht immer Setter für ALLE Eigenschaften, insbesondere für "interne" (Semaphoren, interne Flags ...). Schreibgeschützte Eigenschaften haben offensichtlich keine Setter, daher haben einige Eigenschaften nur Getter. Hier kommt __get (), um den Code zu verkleinern:

  • Definieren Sie ein __get () (magische globale Getter) für alle Eigenschaften, die gleich sind.
  • gruppieren Sie sie in Arrays so:
    • Sie haben gemeinsame Merkmale: Geldwerte werden / können ordnungsgemäß formatiert angezeigt werden, Daten in einem bestimmten Layout (ISO, US, Intl.) usw.
    • Der Code selbst kann mit dieser magischen Methode überprüfen, ob nur vorhandene und zulässige Eigenschaften gelesen werden.
    • Wenn Sie eine neue ähnliche Eigenschaft erstellen müssen, deklarieren Sie sie einfach und fügen Sie ihren Namen dem richtigen Array hinzu. Fertig. Das ist viel SCHNELLER als das Definieren eines neuen Getters, vielleicht mit einigen Codezeilen, die im gesamten Klassencode immer wieder wiederholt werden.

Ja! Wir könnten auch eine private Methode schreiben, um dies zu tun, aber andererseits werden VIELE Methoden deklariert (++ Speicher), die am Ende eine andere, immer dieselbe Methode aufrufen. Warum nicht einfach eine EINZELNE Methode schreiben , um sie alle zu regieren ...? [ja! Wortspiel absolut beabsichtigt! :)]

Magic Setter können auch NUR auf bestimmte Eigenschaften reagieren, sodass alle Datumstyp-Eigenschaften nur in einer Methode auf ungültige Werte überprüft werden können. Wenn Datumstyp-Eigenschaften in einem Array aufgelistet wurden, können deren Setter einfach definiert werden. Nur ein Beispiel natürlich. Es gibt viel zu viele Situationen.

Über Lesbarkeit ... Nun ... Das ist eine weitere Debatte: Ich bin nicht gerne an die Verwendung einer IDE gebunden (tatsächlich verwende ich sie nicht, sie neigen dazu, mir zu sagen (und mich zu zwingen ), wie es geht schreibe ... und ich habe meine Vorlieben für das Codieren von "Schönheit"). Ich neige dazu, konsequent zu benennen, daher reicht es mir aus, ctags und ein paar andere Hilfsmittel zu verwenden ... Wie auch immer: Sobald all diese magischen Setter und Getter fertig sind, schreibe ich die anderen Setter, die zu spezifisch oder "speziell" sind in einer __set () -Methode verallgemeinert werden. Und das deckt alles ab, was ich zum Abrufen und Festlegen von Eigenschaften benötige. Natürlich: Es gibt nicht immer eine gemeinsame Basis, oder es gibt so einige Eigenschaften, die die Mühe, eine magische Methode zu codieren, nicht wert sind, und dann gibt es immer noch das alte gute traditionelle Setter / Getter-Paar.

Programmiersprachen sind genau das: menschliche künstliche Sprachen. Jeder von ihnen hat seine eigene Intonation oder seinen eigenen Akzent, seine eigene Syntax und seinen eigenen Geschmack. Ich werde also nicht so tun, als würde ich einen Ruby- oder Python-Code mit demselben "Akzent" wie Java oder C # schreiben, noch würde ich ein JavaScript oder PHP schreiben, das ähnelt Perl oder SQL ... Verwenden Sie sie so, wie sie verwendet werden sollen.

Manuel
quelle
1

Im Allgemeinen ist der erste Weg insgesamt beliebter, da Personen mit Programmierkenntnissen problemlos auf PHP umsteigen und ihre Arbeit objektorientiert erledigen können. Der erste Weg ist universeller. Mein Rat wäre, sich an das zu halten, was in vielen Sprachen bewährt ist. Wenn Sie dann eine andere Sprache verwenden, sind Sie bereit, etwas zu erreichen ( anstatt Zeit damit zu verbringen, das Rad neu zu erfinden ).

Anthony Rutledge
quelle
0

Es gibt viele Möglichkeiten, Quellcode in einer Netbeans-Konvention zu erstellen. Das ist nett. Es macht das Denken so einfacher === FALSE. Verwenden Sie einfach die Tradition, insbesondere wenn Sie nicht sicher sind, welche der Eigenschaften gekapselt werden soll und welche nicht. Ich weiß, es ist ein Boi ... Pla ... Code, aber für Debugging-Arbeiten und viele andere hält es für den besseren, klaren Weg. Verbringen Sie nicht zu viel Zeit mit Tausenden von Künsten, um einfache Getter und Setter herzustellen. Sie können nicht zu viele Entwurfsmuster wie die Demeter-Regel usw. implementieren, wenn Sie Magie verwenden. In bestimmten Situationen können Sie magic_calls oder kleine, schnelle und klare Lösungen verwenden. Natürlich könnten Sie auch auf diese Weise Lösungen für Designmuster finden, aber warum sollten Sie das Leben schwieriger machen?

Dennis Komnenovic
quelle
0

Validieren + Formatieren / Ableiten von Werten

Mit Setzern können Sie Daten validieren und mit Gettern können Sie Daten formatieren oder ableiten. Mit Objekten können Sie Daten und ihren Validierungs- und Formatierungscode in einem übersichtlichen Paket zusammenfassen, das DRY fördert.

Betrachten Sie beispielsweise die folgende einfache Klasse, die ein Geburtsdatum enthält.

class BirthDate {

    private $birth_date;

    public function getBirthDate($format='Y-m-d') {
        //format $birth_date ...
        //$birth_date = ...
        return $birth_date;
    }

    public function setBirthDate($birth_date) {                   
        //if($birth_date is not valid) throw an exception ...          
        $this->birth_date = $birth_date;
    }

    public function getAge() {
        //calculate age ...
        return $age;
    }

    public function getDaysUntilBirthday() {
        //calculate days until birth days
        return $days;
    }
}

Sie möchten überprüfen, ob der festgelegte Wert lautet

  • Ein gültiges Datum
  • Nicht in der Zukunft

Und Sie möchten diese Validierung nicht für Ihre gesamte Anwendung (oder für mehrere Anwendungen) durchführen. Stattdessen ist es einfacher, die Mitgliedsvariable geschützt oder privat zu machen (um den Setter zum einzigen Zugriffspunkt zu machen) und im Setter zu validieren, da Sie dann wissen, dass das Objekt ein gültiges Geburtsdatum enthält, unabhängig davon, welcher Teil des Anwendung, aus der das Objekt stammt, und wenn Sie weitere Validierungen hinzufügen möchten, können Sie es an einer einzigen Stelle hinzufügen.

Möglicherweise möchten Sie mehrere Formatierer hinzufügen, die mit derselben Mitgliedsvariablen arbeiten, z. B. getAge()und je nach Gebietsschema getDaysUntilBirthday()ein konfigurierbares Format erzwingen getBirthDate(). Daher bevorzuge ich den konsequenten Zugriff auf Werte über Getter im Gegensatz zum Mischen $date->getAge()mit $date->birth_date.

Getter und Setter sind auch nützlich, wenn Sie Objekte erweitern. Angenommen, Ihre Bewerbung muss an einigen Orten Geburtsdaten von mehr als 150 Jahren zulassen, an anderen jedoch nicht. Eine Möglichkeit, das Problem zu lösen, ohne Code zu wiederholen, besteht darin, das BirthDateObjekt zu erweitern und die zusätzliche Validierung in den Setter zu stellen.

class LivingBirthDate extends BirthDate {

    public function setBirthDate($birth_date) {
        //if $birth_date is greater than 150 years throw an exception
        //else pass to parent's setter
        return parent::setBirthDate($birth_date);
    }
}
FuzzyTree
quelle
Oft müssen Sie jedoch Eigenschaften zusammen (und nicht nur einzeln) validieren. Ich denke, "Setter" zuzulassen bedeutet, dass Sie auch keinen Kontext erfassen. Die Validierung sollte kontextbezogen erfolgen. Außerdem müssen Sie bei jeder Methode das Flag "isValid" überprüfen (und eine Validierung durchführen, wenn es falsch ist). Davon abgesehen bieten Setter eine nützliche Möglichkeit, Hinweise zu tippen, z. B. wenn Sie eine Eigenschaft haben, die Sie als Wertobjekt (dh Geld) verwenden möchten.
Programmierer
Ich verstehe nicht, was Sie damit meinen, dass Sie gezwungen sind, ein "isValid" -Flag zu überprüfen? Wenn die einzige Möglichkeit zum Festlegen von Werten über Setter besteht, die eine Validierung durchführen, wissen Sie, dass die Daten gültig sind, da sie erfolgreich festgelegt wurden. Wenn Sie Eigenschaften gemeinsam validieren müssen, können Sie eine allgemeine Validierungsmethode schreiben, die die Setter dieser Eigenschaften aufrufen.
FuzzyTree
Angenommen, Sie haben eine Mitarbeiterklasse mit setHiredund setHireDate. Es ist nicht gültig, jemanden ein Einstellungsdatum festlegen zu lassen, ohne auch den Mitarbeiter als eingestellt festzulegen. Aber Sie haben dies auf keinen Fall durchgesetzt. Wenn Sie es in einem dieser Setter erzwingen, erzwingen Sie die Reihenfolge der "Einstellung", und dies erfordert mehr Code-Lesen von einem Entwickler, um dies zu wissen. Wenn Sie dann eine Methode wie diese $employee->promote($newPosition);ausführen, müssen Sie ein Flag überprüfen, um festzustellen, ob die Validierung durchgeführt wurde, oder annehmen, dass sie nicht durchgeführt wurde, und sie erneut ausführen (redundant).
Programmierer
Erfassen Sie stattdessen die Interaktionen. Vielleicht $employee->updateWorkStatus($hired, $hireDate);oder wenn fortgeschrittener $employee->adminUpdate(\Employee\AdminUpdateDTO $dto);. Jetzt können Sie im gewünschten Kontext validieren und entscheiden, ob überhaupt eine zusätzliche Validierung erforderlich ist.
Programmierer
updateWorkStatus ist im Wesentlichen eine Setter-Funktion, die 2 anstelle von 1 Wert setzt, aber das Konzept ist das gleiche. Dies ist eine Möglichkeit, aber Sie können die kontextbezogene Validierung auch in eine allgemeine Methode einfügen, die nur ausgeführt wird, wenn alle Eigenschaften festgelegt sind, die zusammen validiert werden müssen. Der kontextbezogene Teil der Validierung wird sowohl von setHiredDate als auch von setHired aufgerufen, jedoch nur ausgeführt Wenn sowohl das gemietete als auch das gemietete Datum wahr sind.
FuzzyTree
0

In diesem Beitrag geht es nicht speziell um __getund __setsondern um __calldie gleiche Idee, außer für den Methodenaufruf. In der Regel halte ich mich von jeglichen magischen Methoden fern, die aus den in den Kommentaren und Beiträgen genannten Gründen eine Überladung ermöglichen. JEDOCH bin ich kürzlich auf eine Drittanbieter-API gestoßen , die ich verwende und die beispielsweise einen SERVICE und einen SUB-SERVICE verwendet ::

http://3rdparty.api.com?service=APIService.doActionOne&apikey=12341234

Der wichtige Teil davon ist, dass diese API bis auf die Unteraktion in diesem Fall alles gleich hat doActionOne. Die Idee ist, dass der Entwickler (ich und andere, die diese Klasse verwenden) den Subdienst beim Namen nennen könnte, im Gegensatz zu etwas wie:

$myClass->doAction(array('service'=>'doActionOne','args'=>$args));

Ich könnte stattdessen tun:

 $myClass->doActionOne($args);

Das Hardcodieren wäre nur eine Menge Duplizierung (dieses Beispiel ähnelt dem Code sehr stark):

public function doActionOne($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionTwo($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionThree($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

protected function executeCoreCall($service)
    {
        $cURL = new \cURL();
        return $cURL->('http://3rdparty.api.com?service='.$service.'&apikey='.$this->api.'&'.http_build_query($this->args))
                    ->getResponse();
    }

Aber mit der magischen Methode von kann __call()ich mit dynamischen Methoden auf alle Dienste zugreifen:

public function __call($name, $arguments)
    {
        $this->args     =   $arguments;
        $this->response =   $this->executeCoreCall("APIService.{$name}");   
        return $this;
    }

Der Vorteil dieser dynamischen Aufforderung zur Rückgabe von Daten besteht darin, dass ich, wenn der Anbieter einen weiteren Unterdienst hinzufügt, der Klasse keine weitere Methode hinzufügen oder eine erweiterte Klasse usw. erstellen muss. Ich bin mir nicht sicher, ob dies nützlich ist jemand, aber ich dachte , ich würde ein Beispiel zeigen , wo __set, __get, __calletc. eine Option zur Prüfung sein kann , da die primäre Funktion die Rückgabe von Daten ist.


BEARBEITEN:

Zufälligerweise habe ich dies einige Tage nach der Veröffentlichung gesehen, was genau mein Szenario beschreibt. Es ist nicht die API, auf die ich mich bezog, aber die Anwendung der Methoden ist identisch:

Benutze ich API richtig?

Rasclatt
quelle
-2

Update: Verwenden Sie diese Antwort nicht, da dies sehr dummer Code ist, den ich beim Lernen gefunden habe. Verwenden Sie einfach Getter und Setter, es ist viel besser.


Normalerweise verwende ich diesen Variablennamen als Funktionsnamen und füge dieser Funktion einen optionalen Parameter hinzu. Wenn dieser optionale Parameter vom Aufrufer ausgefüllt wird, setze ich ihn auf die Eigenschaft und gebe $ this object (Verkettung) zurück, und wenn dieser optionale Parameter nicht von angegeben wird Anrufer, ich gebe nur die Eigenschaft an den Anrufer zurück.

Mein Beispiel:

class Model
{
     private $propOne;
     private $propTwo;

     public function propOne($propVal = '')
     {
          if ($propVal === '') {
              return $this->propOne;
          } else {
              $this->propOne = $propVal;
              return $this;
          }
     }

     public function propTwo($propVal = '')
     {
          if ($propVal === '') {
              return $this->propTwo;
          } else {
              $this->propTwo = $propVal;
              return $this;
          }
     }
}
Ajiyakin
quelle
Nun bleibt die Frage: Wie setzen Sie eine Eigenschaft auf die leere Zeichenfolge? Und wie erkennt man, dass das Setzen einer Eigenschaft auf die leere Zeichenfolge tatsächlich fehlgeschlagen ist und als Getter funktioniert hat? Denken Sie nur an HTML-Formulare, die leere Felder als Zeichenfolgen senden ... Und nein: Die Verwendung eines anderen Werts wie NULL als Standardwert löst das Problem nicht.
Sven