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

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

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

благодаря

Вы можете использовать магические методы 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. Масштабируемость : проще регенерировать геттер, чем искать все назначения var в коде проекта.
  2. Отладка : вы можете установить точки останова в сеттерах и геттерах.
  3. Cleaner : Magic функции не являются хорошим решением для написания меньше, ваша IDE не будет предлагать код. Лучше использовать шаблоны для быстрых геттеров.

прямое назначение и геттеры / сеттеры

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

Нет геттера и сеттера Оптимизация PHP

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

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

PHP не является ни Java, ни C ++ или 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 , экземпляр объекта, такой как $someobject , который требует функции ширины, скорее всего, $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, как правило, плохие.

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

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

Я провел эксперимент с использованием магического метода __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».
Вы также можете использовать этот «белый список», чтобы назначать пользовательские имена для доступа к вашим варам.
Например,

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

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

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

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

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

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

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

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

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

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

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

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

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

 class BirthDate { private $birth_date; public function getBirthDate($format='Ym-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() принудительно настроить настраиваемый формат в 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's setter return parent::setBirthDate($birth_date); } } 

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


РЕДАКТИРОВАТЬ:

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

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

Геттер и сеттер в 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 ?> 

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


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

Мой пример:

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