Getter and Setter?

203

Je ne suis pas un développeur PHP, donc je me demande si en PHP est plus populaire d'utiliser des getter / setters explicites, dans un pur style OOP, avec des champs privés (comme j'aime):

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

ou simplement des domaines publics:

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

Merci

marque
la source
7
Après avoir essayé du code à partir des réponses, j'ai utilisé le code que vous utilisez dans la question. Quelle tristesse :-(
somptueux
9
PHPstorm ... génère> getters et setters. == victoire
DevDonkey
@DevDonkey Pas une victoire du tout. Pour conserver des données structurées, utilisez plutôt des tableaux. @: Mark Ce n'est pas ce que sont ou sont les objets. Les getters et setters sont mauvais: yegor256.com/2014/09/16/getters-and-setters-are-evil.html
Kubo2

Réponses:

222

Vous pouvez utiliser les méthodes magiques php __get et __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
la source
15
Je pense que tu veux dire __getet __set. Il y a deux soulignements, pas un. Voici le lien direct vers la partie droite de la page: php.net/manual/en/… (+1 pour une réponse correcte)
Computerish
28
Quel est l'avantage par rapport aux publicpropriétés, s'il n'y a pas de validation / assainissement?
KingCrunch
7
@KingCrunch, ce n'est qu'un exemple. Un exemple très très factice pour une ressource puissante.
Davis Peixoto
10
Ce n'est pas vraiment setter et getter. En règle générale, j'ai besoin pour chaque propriété d'une implémentation différente de getter!
somptueux
79
Veuillez ne pas le faire: avec des méthodes magiques, vous perdrez presque toutes les fonctionnalités liées à la qualité, dans de nombreux IDE (même vim): auto-complétion, héritage PHP explicite, interprétation PHP rapide et génération et sortie PHPDoc utiles. cf. stackoverflow.com/a/6184893/490589
Ronan
113

Pourquoi utiliser des getters et setters?

  1. Évolutivité : il est plus facile de refactoriser un getter que de rechercher toutes les affectations var dans un code de projet.
  2. Débogage : vous pouvez placer des points d'arrêt sur les setters et les getters.
  3. Cleaner : les fonctions Magic ne sont pas une bonne solution pour écrire moins, votre IDE ne suggérera pas le code. Mieux utiliser des modèles pour les getters à écriture rapide.

affectation directe et getters / setters

Wiliam
la source
7
Si vous utilisez @property, votre IDE vous suggérera le code (testé avec PhpStorm 7)
Alex2php
41

Google a déjà publié un guide sur l'optimisation de PHP et la conclusion était:

Aucun getter et setter Optimisation de PHP

Et non, vous ne devez pas utiliser de méthodes magiques . Pour PHP, la méthode magique est mauvaise. Pourquoi?

  1. Ils sont difficiles à déboguer.
  2. Il y a un impact négatif sur les performances.
  3. Ils nécessitent d'écrire plus de code.

PHP n'est pas Java, C ++ ou C #. PHP est différent et joue avec des rôles différents.

magallanes
la source
10
J'ai tendance à être d'accord avec cette idée; c'est $dog->name = 'fido'mieux que $dog->setName('fido'). Lors de la mutation d'une propriété (ex: $dog->increaseAge(1)je peux développer la méthode qui effectue la validation nécessaire et mute cette propriété. Mais toutes les actions ne nécessitent pas vraiment de mutation dans ce sens.
Charlie Schliesser
11
L'article ne dit pas « ne pas », il dit «des arrangeurs et des getters naïfs».
Brett Santore
13
Il est prudent de supposer qu'un article écrit par Google qui a le titre "PHP performance tips" n'est PAS destiné à suggérer un bon style de codage, mais une exécution rapide du code. Le chapitre qui traite des setters et des getters est intitulé "Évitez d'écrire des setters et des getters naïfs", et l'exemple de code est exactement cela: Naive. Il définit et obtient juste une variable sans aucune validation. CE type de setter / getter est inutile. Faire la validation à l'intérieur d'un setter (utilisez simplement un indice de type pour l'argument de la méthode) rendra les setters / getters utiles car maintenant votre code SAIT de quoi il s'agit.
Sven
3
c'est comme dire que le style en ligne est meilleur. bien sûr, les performances sont meilleures, mais est-ce un meilleur code? Je ne savais pas de toute façon que les ingénieurs de Google utilisaient php
Claudiu Creanga
13

L'encapsulation est importante dans n'importe quelle langue OO, la popularité n'a rien à voir avec cela. Dans les langages typés dynamiquement, comme PHP, il est particulièrement utile car il existe peu de moyens de garantir qu'une propriété est d'un type spécifique sans utiliser de setters.

En PHP, cela fonctionne:

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

En Java, cela ne fait pas:

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

L'utilisation de méthodes magiques ( __getet __set) fonctionne également, mais uniquement lorsque vous accédez à une propriété dont la visibilité est inférieure à celle à laquelle l'étendue actuelle peut accéder. Il peut facilement vous donner des maux de tête lorsque vous essayez de déboguer, s'il n'est pas utilisé correctement.

netcoder
la source
7
Getters et setter n'apportent pas d'encapsulation. Encapsulation == les objets font quelque chose avec leurs propres données au lieu de les donner à l'extérieur. Les getters et setters ne sont pas un outil pour appliquer le type dans des langages typés dynamiquement comme PHP.
smentek
14
@smentek: Il vous manque clairement au moins la moitié de ce qu'est réellement l'encapsulation.
netcoder
2
En tant que mise à jour pour ceux qui recherchent, PHP 7.4 sera livré avec un support de propriété typé. Vous pouvez donc déclarer $barcomme un intdans le premier exemple: wiki.php.net/rfc/typed_properties_v2
Kevin
7

Si vous préférez utiliser la fonction __call, vous pouvez utiliser cette méthode. Cela fonctionne avec

  • OBTENIR => $this->property()
  • SET => $this->property($value)
  • OBTENIR => $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
la source
2
@ krzysztof-przygoda: Ces "méthodes magiques" ont toujours un prix. Ils doivent utiliser la récursivité property_exists(get_class($this), $name)et la récursivité est lente. Il existe un moyen d'y remédier avec le cache, mais cela sera toujours plus lent que de créer les getters et setters à la main. Je l'ai seulement écrit comme alternative. En fait, je ne recommande pas d'utiliser les "méthodes magiques". Le temps supplémentaire de création des getters et setters est généralement insignifiant.
J-Rou
7

En plus des réponses déjà excellentes et respectées ici, je voudrais développer PHP n'ayant pas de setters / getters.

PHP n'a pas de syntaxe getter et setter . Il fournit des méthodessous-classées ou magiques pour permettre le "hooking" et le remplacement du processus de recherche de propriété, comme l'a souligné Dave .

Magic nous permet aux programmeurs paresseux de faire plus avec moins de code à un moment où nous sommes activement engagés dans un projet et le connaissons intimement, mais généralement au détriment de la lisibilité.

Performances Chaque fonction inutile, qui résulte du forçage d'une architecture de code de type getter / setter en PHP, implique son propre cadre de pile de mémoire lors de l'invocation et gaspille les cycles CPU.

Lisibilité: la base de code entraîne des lignes de code gonflées, ce qui a un impact sur la navigation dans le code, car plus de LOC signifie plus de défilement.

Préférence: Personnellement, comme règle de base, je prends l'échec de l'analyse de code statique comme un signe pour éviter de descendre sur la route magique tant que des avantages évidents à long terme m'échappent à ce moment-là.

Illusions:

Un argument commun est la lisibilité. Par exemple, c'est $someobject->widthplus facile à lire que $someobject->width(). Cependant, contrairement à une planète circumferenceou width, ce qui peut être supposé être static, l'instance d'un objet telle que $someobject, qui nécessite une fonction de largeur, prend probablement une mesure de la largeur d'instance de l'objet.
Par conséquent, la lisibilité augmente principalement en raison de schémas de dénomination affirmés et non en masquant la fonction qui génère une valeur de propriété donnée.

__get / __set utilise:

  • pré-validation et pré-assainissement des valeurs immobilières

  • chaînes par exemple

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

    Dans ce cas generatelatex, adhérerait à un schéma de dénomination actionname + methodname

  • cas spéciaux et évidents

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

Remarque: PHP a choisi de ne pas implémenter la syntaxe getter / setter. Je ne prétends pas que les getters / setter sont généralement mauvais.

Lorenz Lo Sauer
la source
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

Prêt!

joas
la source
Trop de passe-partout. Imaginez ce genre de choses dans chaque classe. Évitez IMO
DarkNeuron
PHP ne prend pas en charge les getters et setters pour les mêmes raisons que vous mentionnez. Toute implémentation de ce type affecte gravement les performances du script côté serveur.
joas
Je pense que cela va à l'encontre des propriétés «privées». Vous les encapsulez, mais vous autorisez également à y accéder directement.
Koray Küpe
@ KorayKüpe uniquement si le getter est défini. J'utilise beaucoup cette encapsulation (avec de nombreuses améliorations) et cela fonctionne parfaitement. Vous pouvez également étendre la classe et l'utiliser facilement dans tout le code.
joas
5

Eh bien, PHP a des méthodes magiques __get, __set, __issetet __unset, ce qui est toujours un début. Hélas, les propriétés OO sont bien plus que des méthodes magiques. Le principal problème avec l'implémentation de PHP est que les méthodes magiques sont appelées pour toutes les propriétés inaccessibles. Ce qui signifie que vous devez vous répéter (par exemple en appelant property_exists ()) dans les méthodes magiques pour déterminer si le nom est réellement une propriété de votre objet. Et vous ne pouvez pas vraiment résoudre ce problème général avec une classe de base à moins que toutes vos classes héritent de ie. ClassWithProperties, car PHP manque d'héritage multiple.

En revanche, Python propose de nouvelles classes de style property(), ce qui vous permet de définir explicitement toutes vos propriétés. C # a une syntaxe spéciale.

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

Emanuel Landeholm
la source
1
Appeler property_exists, class_vars ou array_key_exists (c'est-à-dire vérifier si la propriété existe réellement) n'est qu'une étape afin d'éviter une erreur fatale à l'exécution. Je ne suis pas sûr que le fait de ne pas déplaire revient à répéter le codage.
Davis Peixoto
1
C'est suffisant. Mais en Python et C # cette répétition n'est pas nécessaire. Je pense que c'est une force.
Emanuel Landeholm
4

J'ai fait une expérience en utilisant la méthode magique __call. Je ne sais pas si je devrais le poster (à cause de tous les avertissements "NE PAS UTILISER DE MÉTHODES MAGIQUES" dans les autres réponses et commentaires) mais je vais le laisser ici .. juste au cas où quelqu'un le trouverait utile.


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];
    }
}

Ajoutez simplement cette méthode ci-dessus dans votre classe, vous pouvez maintenant taper:

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


De cette façon, vous pouvez obtenir / définir tout dans votre classe s'il existe, donc si vous n'en avez besoin que pour quelques éléments spécifiques, vous pouvez utiliser une "liste blanche" comme filtre.

Exemple:

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];
    }
}

Maintenant, vous ne pouvez obtenir / définir que "foo" et "fee".
Vous pouvez également utiliser cette "liste blanche" pour attribuer des noms personnalisés pour accéder à vos vars.
Par exemple,

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

Avec cette liste, vous pouvez maintenant taper:

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

.
.
.
C'est tout.


Doc: __call () est déclenché lors de l'appel de méthodes inaccessibles dans un contexte d'objet.

Sabaz
la source
Le problème avec toutes ces solutions "magiques" est qu'elles utilisent simplement ces méthodes magiques parce qu'elles sont là, et elles sont utiles parce que le problème posé est simple et générique. Une fois que vous quittez ce niveau de problème générique, vous rencontrerez des exigences spécifiques qui ne peuvent pas être résolues par une méthode magique simple, mais nécessiteraient une méthode magique très complexe - ou des setters et des getters codés individuels.
Sven
2

Après avoir lu les autres conseils, j'ai tendance à dire que:

En tant que GÉNÉRIQUE règle, vous définirez pas toujours setters pour TOUS les propriétés, spécialement les « internes » (sémaphores, drapeaux internes ...). Les propriétés en lecture seule n'auront évidemment pas de setters, donc certaines propriétés n'auront que des getters; c'est là que __get () vient réduire le code:

  • définir un __get () (getters globaux magiques) pour toutes ces propriétés qui se ressemblent,
  • regroupez-les en tableaux afin:
    • ils partageront des caractéristiques communes: les valeurs monétaires apparaîtront / peuvent être correctement formatées, les dates dans une disposition spécifique (ISO, US, Intl.), etc.
    • le code lui-même peut vérifier que seules les propriétés existantes et autorisées sont lues à l'aide de cette méthode magique.
    • chaque fois que vous avez besoin de créer une nouvelle propriété similaire, déclarez-la et ajoutez son nom au tableau approprié et c'est fait. C'est bien PLUS RAPIDE que de définir un nouvel getter, peut-être avec quelques lignes de code REPETE encore et encore partout dans le code de la classe.

Oui! nous pourrions également écrire une méthode privée pour le faire, mais là encore, nous aurons de nombreuses méthodes déclarées (++ mémoire) qui finiront par appeler une autre méthode, toujours la même. Pourquoi tout simplement pas écrire un SEULE méthode pour les gouverner tous ...? [oui! jeu de mots absolument destiné! :)]

Les régleurs magiques peuvent également répondre UNIQUEMENT à des propriétés spécifiques, de sorte que toutes les propriétés de type date peuvent être filtrées contre les valeurs non valides dans une seule méthode. Si les propriétés de type date étaient répertoriées dans un tableau, leurs paramètres peuvent être définis facilement. Juste un exemple, bien sûr. il y a beaucoup trop de situations.

À propos de la lisibilité ... Eh bien ... C'est un autre débat: je n'aime pas être lié aux utilisations d'un IDE (en fait, je ne les utilise pas, ils ont tendance à me dire (et à me forcer ) comment écrire ... et j'ai mes goûts sur le codage "beauté"). J'ai tendance à être cohérent sur le nommage, donc l'utilisation de ctags et de quelques autres aides me suffit ... Quoi qu'il en soit: une fois que tous ces setters et getters magiques sont terminés, j'écris les autres setters qui sont trop spécifiques ou "spéciaux" pour être généralisé dans une méthode __set (). Et cela couvre tout ce dont j'ai besoin pour obtenir et définir des propriétés. Bien sûr: il n'y a pas toujours un terrain d'entente, ou il y a tellement de propriétés qui ne valent pas la peine de coder une méthode magique, et puis il y a toujours la bonne vieille paire setter / getter traditionnelle.

Les langages de programmation ne sont que cela: les langages artificiels humains. Donc, chacun d'eux a sa propre intonation ou accent, syntaxe et saveur, donc je ne prétendrai pas écrire un code Ruby ou Python en utilisant le même "accent" que Java ou C #, ni écrire JavaScript ou PHP pour ressembler Perl ou SQL ... Utilisez-les comme ils sont censés être utilisés.

Manuel
la source
1

De manière générale, la première méthode est plus populaire dans l'ensemble, car ceux qui ont des connaissances en programmation peuvent facilement passer à PHP et effectuer le travail de manière orientée objet. La première voie est plus universelle. Mon conseil serait de rester fidèle à ce qui a fait ses preuves dans de nombreuses langues. Ensuite, quand et si vous utilisez une autre langue, vous serez prêt à accomplir quelque chose ( au lieu de passer du temps à réinventer la roue ).

Anthony Rutledge
la source
0

Il existe de nombreuses façons de créer un code source dans une convention netbeans. C'est sympa. Cela rend la pensée tellement plus facile === FAUX. Utilisez simplement la traditionel, surtout si vous n'êtes pas sûr de laquelle des propriétés doit être encapsulée et laquelle ne l'est pas. Je sais, c'est un code boi .... pla ..., mais pour les travaux de débogage et beaucoup d'autres pense que c'est le meilleur moyen clair. Ne passez pas trop de temps avec des milliers d'arts à faire de simples getters et setters. Vous ne pouvez pas implémenter trop de modèles de conception comme la règle déméter et ainsi de suite, si vous utilisez des magies. Dans une situation spécifique, vous pouvez utiliser magic_calls ou pour des solutions petites, rapides et claires. Bien sûr, vous pouvez également apporter des solutions aux concepteurs de design, mais pourquoi vous rendre la vie plus difficile.

Dennis Komnenovic
la source
0

Validation + formatage / dérivation des valeurs

Les setters vous permettent de valider les données et les getters vous permettent de formater ou de dériver les données. Les objets vous permettent d'encapsuler les données et leur code de validation et de mise en forme dans un package soigné qui encourage DRY.

Par exemple, considérez la classe simple suivante qui contient une date de naissance.

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

Vous voudrez valider que la valeur définie est

  • Une date valide
  • Pas à l'avenir

Et vous ne voulez pas faire cette validation partout dans votre application (ou sur plusieurs applications d'ailleurs). Au lieu de cela, il est plus facile de rendre la variable membre protégée ou privée (afin de faire du setter le seul point d'accès) et de valider dans le setter car vous saurez alors que l'objet contient une date de naissance valide, quelle que soit la partie du l'application d'où provient l'objet et si vous souhaitez ajouter plus de validation, vous pouvez l'ajouter en un seul endroit.

Vous voudrez peut-être ajouter plusieurs formateurs qui fonctionnent sur la même variable membre, c.-à-d. getAge()Et getDaysUntilBirthday()et vous voudrez peut-être appliquer un format configurable en getBirthDate()fonction des paramètres régionaux. Par conséquent, je préfère accéder systématiquement aux valeurs via des getters plutôt que de les mélanger $date->getAge()avec $date->birth_date.

les getters et setters sont également utiles lorsque vous étendez des objets. Par exemple, supposons que votre application devait permettre des dates de naissance de plus de 150 ans dans certains endroits mais pas dans d'autres. Une façon de résoudre le problème sans répéter aucun code serait d'étendre l' BirthDateobjet et de mettre la validation supplémentaire dans le setter.

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
la source
Cependant, plusieurs fois, vous devez valider les propriétés ensemble (et pas seulement individuellement). Je pense qu'autoriser les «setters» signifie que vous ne capturez pas non plus le contexte. La validation doit être effectuée sur une base contextuelle. De plus, vous serez obligé de vérifier un indicateur "isValid" sur chaque méthode que vous avez (et d'effectuer une validation si elle est fausse). Cela étant dit, les setters fournissent un moyen utile de taper un indice, par exemple lorsque vous avez une propriété que vous souhaitez être un objet de valeur (c'est-à-dire de l'argent).
prograhammer
Je ne comprends pas ce que vous voulez dire par être forcé de cocher un drapeau "isValid"? Si la seule façon de définir des valeurs est d'utiliser des setters qui effectuent la validation, alors vous savez que les données sont valides du fait qu'elles ont été correctement définies. Si vous devez valider des propriétés ensemble, vous pouvez écrire une méthode de validation commune que les programmeurs de ces propriétés appellent.
FuzzyTree
Disons que vous avez une classe d'employés avec setHiredet setHireDate. Il n'est pas valide de laisser quelqu'un fixer une date d'embauche sans définir également l'employé comme embauché. Mais il n'y a aucun moyen que vous appliquiez cela. Si vous l'imposez dans l'un de ces setters, alors vous forcez l'ordre de "setting", et cela nécessite plus de lecture de code d'un développeur pour le savoir. Ensuite, lorsque vous allez faire une méthode comme $employee->promote($newPosition);vous devez vérifier un indicateur pour voir si la validation a été effectuée ou supposer que cela n'a pas été fait et recommencer (redondant).
prograhammer
Capturez plutôt les interactions. Peut $employee->updateWorkStatus($hired, $hireDate);- être ou si plus avancé $employee->adminUpdate(\Employee\AdminUpdateDTO $dto);. Vous pouvez maintenant valider dans le contexte dont vous avez besoin et décider si une validation supplémentaire est nécessaire.
prograhammer
updateWorkStatus est essentiellement une fonction de définition qui définit 2 au lieu de 1, mais le concept est le même. C'est une façon de le faire, mais vous pouvez également placer la validation contextuelle dans une méthode commune qui n'est exécutée que lorsque toutes les propriétés qui doivent être validées ensemble sont définies, c'est-à-dire que la partie contextuelle de la validation serait appelée par setHiredDate et setHired mais ne s'exécuterait que si isset hired et isset hiredDate étaient tous les deux vrais.
FuzzyTree
0

Ce message n'est pas spécifiquement sur __getet __setmais c'est plutôt __callla même idée, sauf pour l'appel de méthode. En règle générale, je reste à l'écart de tout type de méthodes magiques qui permettent la surcharge pour les raisons décrites dans les commentaires et les messages CEPENDANT , j'ai récemment rencontré une API tierce que j'utilise qui utilise un SERVICE et un SOUS-SERVICE, par exemple :

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

La partie importante de cela est que cette API a tout de même sauf la sous-action, dans ce cas doActionOne. L'idée est que le développeur (moi-même et les autres utilisateurs de cette classe) pourrait appeler le sous-service par son nom par opposition à quelque chose comme:

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

Je pourrais faire à la place:

 $myClass->doActionOne($args);

Pour coder en dur ce serait juste beaucoup de duplication (cet exemple ressemble très vaguement au code):

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();
    }

Mais avec la méthode magique de __call()je suis en mesure d'accéder à tous les services avec des méthodes dynamiques:

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

L'avantage de cet appel dynamique pour le retour de données est que si le fournisseur ajoute un autre sous-service, je n'ai pas besoin d'ajouter une autre méthode dans la classe ou de créer une classe étendue, etc. Je ne sais pas si cela est utile pour tout le monde, mais je pensais que je montrerais un exemple où __set, __get, __call, etc. peut être une option pour une contrepartie puisque la fonction principale est le retour des données.


ÉDITER:

Par coïncidence, j'ai vu cela quelques jours après la publication qui décrit exactement mon scénario. Ce n'est pas l'API à laquelle je faisais référence mais l'application des méthodes est identique:

Est-ce que j'utilise correctement l'api?

Rasclatt
la source
-2

Mise à jour: n'utilisez pas cette réponse car c'est un code très stupide que j'ai trouvé pendant que j'apprends. Utilisez simplement getter et setter, c'est beaucoup mieux.


J'utilise généralement ce nom de variable comme nom de fonction, et j'ajoute un paramètre facultatif à cette fonction, donc lorsque ce paramètre facultatif est rempli par l'appelant, je lui attribue la propriété et je renvoie $ cet objet (chaînage), puis lorsque ce paramètre facultatif n'est pas spécifié par appelant, je rends juste la propriété à l'appelant.

Mon exemple:

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
la source
Maintenant, la question demeure: comment définir une propriété sur la chaîne vide? Et comment détectez-vous que la définition d'une propriété sur la chaîne vide a réellement échoué et a fonctionné comme getter? Pensez simplement aux formulaires HTML qui envoient des champs vides sous forme de chaînes ... Et non: l'utilisation d'une valeur différente telle que NULL comme valeur par défaut ne résout pas le problème.
Sven