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

Я не разработчик PHP, поэтому мне интересно, если в PHP более популярно использовать явные getter/setters в чистом стиле OOP с частными полями (как мне нравится):

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

Спасибо

Ответ 1

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

Ответ 2

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

  • Масштабируемость: проще регенерировать геттер, чем искать все назначения var в коде проекта.
  • Отладка. Вы можете поместить контрольные точки в сеттеры и геттеры.
  • Cleaner. Магические функции не являются хорошим решением для написания меньше, ваша IDE не предложит код. Лучше использовать шаблоны для быстрых геттеров.

direct assignment and getters/setters

Ответ 3

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

Отсутствие getter и setter Оптимизация PHP

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

  • Сложно отлаживать.
  • Существует влияние производительности.
  • Добавить код.

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

Ответ 4

Инкапсуляция важна для любого языка 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) также работает, но только при доступе к свойству, которое имеет более низкую видимость, чем текущая область доступа. Он может легко дать вам головные боли при попытке отладки, если он не используется должным образом.

Ответ 5

Если вы предпочитаете использовать функцию __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;
    }
}

Ответ 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

Готово!

Ответ 7

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

Напротив, новые классы стиля Python предоставляют вам свойство(), которое позволяет вам явно определять все ваши свойства. С# имеет специальный синтаксис. http://en.wikipedia.org/wiki/Property_(programming)

Ответ 8

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

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

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

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

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

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

Заблуждение:

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

__ 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. Я не утверждаю, что getters/setter, как правило, плохие.

Ответ 9

Я провел эксперимент с использованием магического метода __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() запускается при вызове недоступных методов в контексте объекта.

Ответ 10

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

Как правило GENERIC, вы не всегда будете определять сеттеры для свойств ВСЕ, особенно "внутренних" (семафоров, внутренних флагов...). Очевидно, что свойства только для чтения не имеют сеттеров, поэтому некоторые свойства будут иметь только геттеры; что где __get() приходит к сокращению кода:

  • определить __get() (магические глобальные геттеры) для всех тех свойств, которые одинаковы,
  • группируйте их в массивы так:
    • они будут обладать общими характеристиками: денежные значения будут/могут быть правильно отформатированы, даты в определенном макете (ISO, US, Intl.) и т.д.
    • сам код может проверить, что с помощью этого магического метода читаются только существующие и разрешенные свойства.
    • всякий раз, когда вам нужно создать новое подобное свойство, просто объявите его и добавьте его имя в соответствующий массив, и оно будет выполнено. Таким образом, FASTER, чем определение нового getter, возможно, с некоторыми строками кода REPEATED снова и снова по всему классу.

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

Магические сеттеры также могут реагировать ТОЛЬКО на определенные свойства, поэтому все свойства типа даты могут быть экранированы по недопустимым значениям только одним методом. Если свойства типа даты были перечислены в массиве, их установщики могут быть легко определены. Просто пример, конечно. существует слишком много ситуаций.

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

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

Ответ 11

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

Ответ 12

Существует множество способов создания исходного кода в соглашении netbeans. Это мило. Это заставляет думать так легче === ЛОЖЬ. Просто используйте традицию, особенно если вы не уверены, какой из свойств должен быть инкапсулирован, а какой нет. Я знаю, это код boi.... pla..., но для отладочных работ и многие другие считают, что это лучший, понятный путь. Не тратьте много времени с тысячами искусств, как сделать простых геттеров и сеттеров. Вы не можете реализовать слишком некоторые шаблоны проектирования, такие как demeter-rule и т.д., Если вы используете магию. В конкретной ситуации вы можете использовать magic_calls или для небольших, быстрых и понятных решений. Конечно, вы могли бы сделать решения для дизайн-паттернов таким же образом, но зачем делать вас живее сложнее.

Ответ 13

Проверка + форматирование/вывод значений

Сеттеры позволяют проверять данные и геттеры, чтобы вы могли форматировать или выводить данные. Объекты позволяют инкапсулировать данные и их код проверки и форматирования в аккуратный пакет, который поощряет 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.

getters и seters также полезны, когда вы расширяете объекты. Например, предположим, что ваше приложение должно разрешить даты рождения в возрасте до 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 setter
        return parent::setBirthDate($birth_date);
    }
}

Ответ 14

Это сообщение не относится конкретно к __get и __set, а скорее к __call, что является той же идеей, кроме вызова метода. Как правило, я избегаю любых магических методов, которые допускают перегрузку по причинам, изложенным в комментариях и сообщениях ОДНАКО, я недавно столкнулся с сторонним API, который я использую, который использует пример SERVICE и SUB-SERVICE

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

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

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

Я мог бы сделать вместо этого:

 $myClass->doActionOne($args);

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

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 и т.д. могут быть опцией для рассмотрения, поскольку первичная функция - это возврат данных.


EDIT:

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

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

Ответ 15

Getter и setter в PHP

<?php
class Car {

public  $color;
public  $model;
public function Model()
{
    return $this->color;

}    
public function New_Model(){
     return $this->model = "BMW CAR HERE";
}
}
$obj =  new Car();
echo $obj->color = "red"; //set color in $color variable
echo $obj->New_Model(); //get value from New_Model function
echo $obj->Model("red"); //set color in function Model 
?>

Ответ 16

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


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

Мой пример:

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