Геттер и сеттер?

Я не разработчик PHP, поэтому мне интересно, является ли PHP более популярным для использования явных геттеров/сеттеров в чистом стиле ООП с частными полями (как мне нравится):

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

или просто открытые поля:

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

спасибо

15 ответов


можно использовать php магические методы __get и __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;
  }
}
?>

зачем использовать геттеры и сеттеры?

  1. масштабируемость: проще рефакторинг геттер, чем поиск по всем заданиям ВАР в проекте кодекса.
  2. отладка: вы можете поставить точки останова на сеттеров и геттеров.
  3. пылесос: магические функции не являются хорошим решением для записи меньше, ваша IDE не предложит код. Лучше использовать шаблоны для быстрой записи газопоглотители.

direct assignment and getters/setters


Google уже опубликовал руководство по оптимизации PHP и вывод был:

нет геттера и сеттера оптимизация PHP

и нет, вы должны не использовать магические методы. Для PHP магический метод-это зло. Почему?

  1. их трудно отлаживать.
  2. отрицательное влияние представления.
  3. они требуют написания дополнительного кода.

PHP не является Java, C++ или С.# PHP отличается и играет разные роли.


инкапсуляция важна на любом языке OO, популярность не имеет к этому никакого отношения. В динамически типизированных языках, таких как PHP, это особенно полезно, потому что есть мало способов обеспечить свойство определенного типа без использования сеттеров.

в PHP это работает:

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

в Java, это не так:

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

использование магических методов (__get и __set) также работает, но только при доступе к свойству, которое имеет более низкую видимость, чем текущая область может получить доступ. Он может легко дать вам головные боли при попытке отладки, если он не используется должным образом.


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

готово!


если вы предпочитаете использовать функцию _ _ call, вы можете использовать этот метод. Он работает с

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

в дополнение к уже отличным и уважаемым ответам здесь, я хотел бы расширить PHP, не имеющий сеттеров/геттеров.

PHP не имеет синтаксиса getter и setter. Он предоставляет подклассы или магия методы, позволяющие "зацепить" и переопределить процесс поиска свойств, как указано Дэйв.

магия позволяет мы, ленивые программисты делать больше меньше кода в то время, когда мы активно занимаемся проектом и знаем его близко, но обычно за счет читаемости.

производительность каждая ненужная функция, которая возникает из-за принуждения геттера/сеттера-подобной архитектуры кода в PHP, включает свой собственный стек-фрейм памяти при вызове и тратит циклы процессора.

читаемость: кодовая база вызывает раздувание строк кода, что влияет на навигацию кода как больше LOC больше прокрутки.

предпочтения: лично, как мое эмпирическое правило, я принимаю отказ статического анализа кода как знак, чтобы не идти вниз по волшебной дороге, пока очевидные долгосрочные выгоды от меня в то время.

ошибки:

общим аргументом является читаемость. Например,$someobject->width легче читать, чем $someobject->width(). Однако в отличие от планеты circumference или width, что можно считать static, an экземпляр объекта, такой как $someobject, который требует функции width, вероятно, принимает измерение ширины экземпляра объекта.
Поэтому читаемость увеличивается главным образом из-за утвердительных схем именования, а не путем сокрытия функции, которая выводит заданное значение свойства.

_ _ get / _ _ set использует:

  • предварительная проверка и предварительная санитария стоимости недвижимости

  • строки например,

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

    в этом случае generatelatex будет придерживаться схемы именования actionname + methodname

  • специальные, очевидные случаи

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

Примечание: PHP решил не реализовывать синтаксис getter/setter. Я не утверждаю, что геттеры/сеттеры обычно плохие.


Ну PHP имеет магические методы __get, __set, __isset & __unset - это всегда начало. Увы, правильно (понимаете?) OO properties-это больше, чем магические методы. Основная проблема с реализацией PHP заключается в том, что длявсе недоступные свойства. Это означает, что вы должны повторить себя (например. вызывая property_exists ()) в магических методах при определении if имя на самом деле это свойство объекта. А вы невозможно решить эту общую проблему с базовым классом, если все ваши классы не наследуются от ie. ClassWithProperties, так как PHP не имеет множественного наследования.

В противоположность Python новые классы стилей дает вам property(), что позволяет явно определить все ваши свойства. C# специальный синтаксис.

http://en.wikipedia.org/wiki/Property_ (Программирование)


прочитав другие советы, я склонен сказать, что:

Как GENERIC правило, вы не всегда будете определять сеттеры для все свойства, особенно "внутренние" (семафоры, внутренние флаги...). только для чтения свойства не будут иметь сеттеров, очевидно, поэтому некоторые свойства будут иметь только геттеры; вот где _ _ get () приходит, чтобы сжать код:

  • определите _ _ get () (магические глобальные геттеры) для всех тех свойства, которые похожи,
  • сгруппировать их в массивы так:
    • они будут иметь общие характеристики: денежные значения будут / могут быть правильно отформатированы, даты в определенном макете (ISO, US, Intl.), п.
    • сам код может проверить, что только существующие и разрешенные свойства читаются с помощью этого магического метода.
    • всякий раз, когда вам нужно создать новое подобное свойство, просто объявите его и добавьте его имя в соответствующий массив, и все готово. Вот так!--7-->быстрее чем определение нового геттера, возможно, с некоторыми строками кода, повторяемыми снова и снова по всему коду класса.

да! мы могли бы написать частный метод для этого, но опять же, у нас будет много методов, объявленных (память++), которые в конечном итоге вызывают другой, всегда тот же метод. Почему бы просто не написать один метод править...? [да! каламбур абсолютно намеренный! :)]

Magic setters также могут реагировать только на определенные свойства, поэтому все свойства типа даты могут быть экранированы от недопустимых значений только в одном методе. Если свойства типа даты были перечислены в массиве, их сеттеры можно легко определить. Просто пример, конечно. слишком много ситуаций.

о читабельности... Что ж... Это еще одна дискуссия: мне не нравится быть связанным с использованием IDE (на самом деле, я не использую их, они, как правило, говорят мне (и силу me) как писать... и мне нравится кодирование "beauty"). Я, как правило, последователен в именовании, поэтому мне достаточно использовать ctags и пару других вспомогательных средств... В любом случае: как только все эти магические сеттеры и геттеры сделаны, я пишу другие сеттеры, которые слишком специфичны или "особенные", чтобы быть обобщенными в методе __set (). И это охватывает все, что мне нужно о получении и настройке свойств. Конечно: не всегда есть общая точка зрения, или их так мало свойства, которые не стоит беспокоиться о кодировании магического метода, а затем все еще есть старая хорошая традиционная пара сеттер/геттер.

языки программирования - это просто: человеческие искусственные языки. Таким образом, каждый из них имеет свою интонацию или акцент, синтаксис и вкус, поэтому я не буду притворяться, что пишу код Ruby или Python, используя тот же "акцент", что и Java или C#, и не буду писать JavaScript или PHP, чтобы напоминать Perl или SQL... Используй их так, как они предназначены.


я провел эксперимент, используя магический метод _ _ call. Не уверен, что я должен опубликовать его (из-за всех предупреждений "не использовать магические методы" в других ответах и комментариях), но я оставлю его здесь.. на случай, если кому-то это пригодится.


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

просто добавьте этот метод выше в свой класс, теперь вы можете ввести:

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


таким образом, вы можете получить / установить все в своем классе, если он существует так, если вам это нужно только для нескольких конкретных элементы, вы можете использовать белый список в качестве фильтра.

пример:

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

теперь вы можете получить/установить только "foo"и " fee".
Вы также можете использовать этот "белый список" для назначения пользовательских имен для доступа к вашим vars.
Например,

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

С этим списком, теперь можно типа:

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

.
.
.
Вот и все.


Doc: _ _ call () срабатывает, когда вызове недоступных методов в контексте объекта.


вообще говоря, первый способ более популярен в целом, потому что те, у кого есть предыдущие знания программирования, могут легко перейти на PHP и получить работу в объектно-ориентированном виде. Первый путь более универсален. Мой совет-придерживаться того, что проверено и истинно на многих языках. Затем, когда и если вы используете другой язык, вы будете готовы получить что-то совершенное (вместо того, чтобы тратить время на изобретение колеса).


существует много способов создания исходного кода в NetBeans-конвенции. Это мило. Это заставляет думать, что такой easyer = = = FALSE. Просто используйте traditionel, особенно если вы не уверены, какое из свойств должно быть инкапсулировано, а какое нет. Я знаю, это бои.... ПЛМ... код, но для отладки-работает и многие другие думают, что это лучший, понятный способ. Не тратьте много времени на thousend искусств, как сделать простые геттеры и сеттеры. Нельзя реализовать некоторые шаблоны проектирования как правило Деметры и так далее, Если вы используете магию. В конкретной ситуации вы можете использовать magic_calls или для небольших, быстрых и четких решений. Конечно, вы могли бы сделать решения для дизайна-скороговорки таким же образом, но почему сделать вас жить сложнее.


Проверка + Форматирование / Получение Значений

сеттеры позволяют проверять данные, а геттеры позволяют форматировать или выводить данные. Объекты позволяют инкапсулировать данные и их код проверки и форматирования в аккуратный пакет, который поощряет DRY.

например, рассмотрим следующий простой класс, содержащий дату рождения.

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

вы захотите проверить, что заданное значение

  • действительная дата
  • не в будущем

и вы не хотите делать эту проверку по всему вашему приложению (или по нескольким приложениям, если на то пошло). Вместо этого проще сделать переменную-член защищенной или закрытой (чтобы сделать сеттер единственной точкой доступа) и проверить в сеттере, потому что тогда вы будете знать, что объект содержит действительную дату рождения независимо от того, из какой части приложения объект пришел, и если вы хотите добавить больше проверка, то вы можете добавить его в одном месте.

вы можете добавить несколько форматеров, которые работают с одной переменной-членом, т. е. getAge() и getDaysUntilBirthday() и вы, возможно, захотите применить настраиваемый формат в getBirthDate() в зависимости от локали. Поэтому я предпочитаю последовательно получать доступ к значениям через геттеры, а не смешивать $date->getAge() С $date->birth_date.

геттеры и сеттеры также полезны при расширении объектов. Например, предположим, что вашему приложению необходимо разрешить 150 + Год рождения даты в некоторых местах, но не в других. Одним из способов решения проблемы без повторения кода было бы расширение BirthDate объект и поставить дополнительную проверку в сеттере.

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

этот пост не конкретно про __get и __set а __call это та же идея, за исключением вызова метода. Как правило, я держусь подальше от любых магических методов, которые позволяют перегружать по причинам, изложенным в комментариях и сообщениях , недавно я столкнулся с сторонним API, который я использую, который использует службу и суб-службу, например:

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

важной частью этого является то, что этот API имеет все то же самое, кроме sub-action, в данном случае doActionOne. Идея заключается в том, что разработчик (я и другие, использующие этот класс) могут называть суб-сервис по имени, а не что-то вроде:

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

я мог бы вместо этого:

 $myClass->doActionOne($args);

для hardcode это будет просто много дублирования (этот пример очень слабо напоминает код):

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

но с помощью волшебного метода __call() Я могу получить доступ ко всем услугам с динамическим методы:

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

преимущество этого динамического вызова для возврата данных заключается в том, что если поставщик добавляет другой суб-сервис, мне не нужно добавлять другой метод в класс или создавать расширенный класс и т. д. Я не уверен, что это кому-то полезно, но я решил показать пример, где __set, __get, __call, etc. может быть вариантом для рассмотрения, так как основной функцией является возврат данные.


EDIT:

по совпадению, я увидел это через несколько дней после публикации, которая описывает именно мой сценарий. Это не API, о котором я говорил, но применение методов идентично:

правильно ли я использую api?


Update: Не используйте этот ответ, так как это очень глупый код, который я нашел, пока учусь. Просто используйте простой геттер и сеттер, это намного лучше.


Я обычно через имя переменной в качестве имени функции, и добавить дополнительный параметр в функцию так, когда это необязательный параметр заполняется абонента, затем установите его в собственность и вернуть $этот объект (цепочки), а затем, когда это необязательный параметр не указан абонент, я просто возвратить имущество абонент.

мой пример:

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