Comments 37
Магические сеттеры и геттеры не могут быть эквивалентны публичным свойствам. Например:
Вложенные массивы и Magic Methods
Нельзя сделать вот так вот:
Вложенные массивы и Magic Methods
Нельзя сделать вот так вот:
$a = 1;
$foo->bar = &$a;
+2
UFO just landed and posted this here
Валится на этапе интерпретации. 3v4l.org/TOKV0#v525
+1
Магические… В этом весь PHP, а не правильнее называть «Поля» (то что в первом случае) и «свойства» (то что в случае __get __set)?
0
Жалко wiki.php.net/rfc/propertygetsetsyntax-v1.2 не приняли :(
+2
использование магии увеличивает время работы и уменьшает читаемость кода.
+2
Можете показать пример ухудшения читаемости?
+4
class Human
{
private $passport;
private $age;
public function __get($name)
{
if ($this->isPassportPropertyName($name)) {
$name = $this->convertToPassportPropertyName($name);
$value = $this->passport->$name;
} elseif (property_exists($this, $name)) {
$value = $this->$name;
} else {
throw new \Exception('Oops, there is no such property here!');
}
}
}
vs
class Human
{
private $passport;
private $age;
public function getPassportIssueDate()
{
return $this->passport->issueDate;
}
...
public function getAge()
{
return $this->age;
}
}
-1
Да, согласен. Здесь действительно «чёрт ногу сломит».
С другой стороны так «изощрённо» можно использовать любую языковую фичу. Пожалуй, нужно было задать вот такой вопрос: действительно не существует таких случаев, когда применение магических методов __set и __get не ухудшает (улучшает) читаемость кода?
С другой стороны так «изощрённо» можно использовать любую языковую фичу. Пожалуй, нужно было задать вот такой вопрос: действительно не существует таких случаев, когда применение магических методов __set и __get не ухудшает (улучшает) читаемость кода?
0
Ну, в общем-то, я писал в контексте статьи — сравнение public, get() и __get().
Конечно, как большинство языковых конструкций, __get/__set имеют свои области применения. Но имхо, зачастую они используются не по назначению. Вот пример: gist.github.com/elfet/7081560
Кстати, интересно было бы послушать поставивших минусы (на сами минусы пофиг, конечно).
P.S. В том комментарии не стал писать (т.к. разговор шёл именно про геттеры в пределах одного класса), но вообще, конечно, идеальная читаемость достигается в этом случае:
Конечно, как большинство языковых конструкций, __get/__set имеют свои области применения. Но имхо, зачастую они используются не по назначению. Вот пример: gist.github.com/elfet/7081560
Кстати, интересно было бы послушать поставивших минусы (на сами минусы пофиг, конечно).
P.S. В том комментарии не стал писать (т.к. разговор шёл именно про геттеры в пределах одного класса), но вообще, конечно, идеальная читаемость достигается в этом случае:
$vasya->getPassport()->getIssueDate()
0
Однозначного выбора все равно нет.
Где-то нужно использовать именно сеттеры, где то достаточно (и очень удобно) — использовать магику. А в части случаев вообще достаточно публичного атрибута.
Тут главное без фанатизма. PHP-кодеры, повсеместно использующие гетеры и сеттеры меня, честно говоря, тоже убивают. А те, кто отказываются от них полностью только из принципа, мол, «геттеры/сеттеры — это моветон» ничем не отличаются от первых.
Где-то нужно использовать именно сеттеры, где то достаточно (и очень удобно) — использовать магику. А в части случаев вообще достаточно публичного атрибута.
Тут главное без фанатизма. PHP-кодеры, повсеместно использующие гетеры и сеттеры меня, честно говоря, тоже убивают. А те, кто отказываются от них полностью только из принципа, мол, «геттеры/сеттеры — это моветон» ничем не отличаются от первых.
0
Имхо, стоит придерживаться одного стиля.
Меня, например, убивает ситуация, когда в каких-то классах публичные свойства, а в каких-то геттеры. Иногда ещё умудряются сочетать и то, и другое в одном классе.
Кстати, изначальное использование геттеров всегда сильно упрощает рефакторинг. Скажем, вы решили запретить повторное переназначение имени для пользователя. Если имя у вас было публичным свойством, то вам придётся добавлять геттер и менять логику во всех местах, где это свойство использовалось, в случае геттера — достаточно изменить логику геттера.
Меня, например, убивает ситуация, когда в каких-то классах публичные свойства, а в каких-то геттеры. Иногда ещё умудряются сочетать и то, и другое в одном классе.
Кстати, изначальное использование геттеров всегда сильно упрощает рефакторинг. Скажем, вы решили запретить повторное переназначение имени для пользователя. Если имя у вас было публичным свойством, то вам придётся добавлять геттер и менять логику во всех местах, где это свойство использовалось, в случае геттера — достаточно изменить логику геттера.
0
А реализуйте ка геттерами это:
public function __get($name)
{
if(isset($this->_attributes[$name]))
return $this->_attributes[$name];
elseif(isset($this->getMetaData()->columns[$name]))
return null;
elseif(isset($this->_related[$name]))
return $this->_related[$name];
elseif(isset($this->getMetaData()->relations[$name]))
return $this->getRelated($name);
else
return parent::__get($name);
}
:)
public function __get($name)
{
if(isset($this->_attributes[$name]))
return $this->_attributes[$name];
elseif(isset($this->getMetaData()->columns[$name]))
return null;
elseif(isset($this->_related[$name]))
return $this->_related[$name];
elseif(isset($this->getMetaData()->relations[$name]))
return $this->getRelated($name);
else
return parent::__get($name);
}
:)
0
Жесть какая, в смысле «очевидности»
0
Я вообще не буду это реализовывать так.
Через некоторое время вы поймёте, почему. И время это приблизится, если вы начнёте использовать тег source, расставлять фигурные скобки там, где надо, стараться не использовать множественных возвратов внутри одного метода и использовать array_key_exists вместо isset.
Ещё ближе к этому моменту вы подойдёте тогда, когда вместо
будете писать:
И совсем близко вы к этому подойдёте тогда, когда поймёте, что в этом магическом __get вы пытаетесь получить некую сущность, которая является конкатенацией множеств атрибутов объекта, различных свойств метаданных и неких свойств родительского класса. Скажем, вы назовёте её «properties». И тогда вы подумаете, что и для метаданных колонки+связи — это некоторые «properties». И т.д.
И получится что-то вроде этого:
Через некоторое время вы поймёте, почему. И время это приблизится, если вы начнёте использовать тег source, расставлять фигурные скобки там, где надо, стараться не использовать множественных возвратов внутри одного метода и использовать array_key_exists вместо isset.
Ещё ближе к этому моменту вы подойдёте тогда, когда вместо
isset(($this->getMetaData()->...)
будете писать:
$this->getMetadata()->has...();
И совсем близко вы к этому подойдёте тогда, когда поймёте, что в этом магическом __get вы пытаетесь получить некую сущность, которая является конкатенацией множеств атрибутов объекта, различных свойств метаданных и неких свойств родительского класса. Скажем, вы назовёте её «properties». И тогда вы подумаете, что и для метаданных колонки+связи — это некоторые «properties». И т.д.
И получится что-то вроде этого:
public function getProperties()
{
return array_merge(parent::getProperties(), $this->_attributes, $this->getMetadata()->getProperties());
}
public function getProperty($name)
{
$properties = $this->getProperties();
if (!array_key_exists($name, $properties)) {
throw new \InvalidArgumentException('There is no [' . $name . '] property here.');
}
return $properties[$name];
}
0
Вот только всё равно это не избавит от конфликтов именований. Поэтому я бы вообще не стал вводить такую «property»-сущность, а продолжал бы работать со всеми составляющими отдельно.
0
На самом деле я не буду так думать, это забота ребят из Yii Framework.
Однако им удалось создать весьма популярный и один из самых быстрых PHP-фреймворков современности.
Не думаю, что у вас есть что-то, что может сравнится с ним по популярности. Да, пускай ваш код более удобно читать новичкам. Но кто его будет читать, если он окажется никому не нужным? Кичиться надо результатом, а не теорией и размышлениями «о смысле жизни».
Ваши примеры явно страдают в производительности перед примером из Yii. C таким же успехом можно писать на Java и не париться. Путь PHP же — это «быстрее, выше, сильнее», а понятный для новичков код — это уже вторичное и зависит от конкретного проекта.
Однако им удалось создать весьма популярный и один из самых быстрых PHP-фреймворков современности.
Не думаю, что у вас есть что-то, что может сравнится с ним по популярности. Да, пускай ваш код более удобно читать новичкам. Но кто его будет читать, если он окажется никому не нужным? Кичиться надо результатом, а не теорией и размышлениями «о смысле жизни».
Ваши примеры явно страдают в производительности перед примером из Yii. C таким же успехом можно писать на Java и не париться. Путь PHP же — это «быстрее, выше, сильнее», а понятный для новичков код — это уже вторичное и зависит от конкретного проекта.
0
Каким образом всё написанное вами связано с программированием и кодом? Yii — хорошая штука. Но это не делает её идеальной. Тем более при разговоре о коде, который присутствует в первом коммите в гитхаб 5 с лишним лет назад (после этого был лёгкий рефакторинг к упомянутому виду 4 с лишним года назад). В коде класса 2550 строк и 17 контрибьюторов.
Производительность? Зависит от того, как использовать. В сферическом вакууме оптимизировать нечего, в другом вакууме можно лениво подсчитывать свойства один раз. В случае с ActiveRecord (как в приведённом коде), проще было бы один раз подсчитать (схема не так часто меняется). Но это всё экономия на спичках с учётом, что всё это происходит в ORM.
В современном мире нужно не только «быстрее, выше, сильнее». Предположим, у вас итеративный процесс. Наговнокодили «быстро, круто, сильно» первую итерацию, а дальше пошли новую работу искать.
Java-way? Если у вас есть желание и дальше считаться тупоголовыми php-шниками — не вопрос. Я предпочитаю использовать нестрогость PHP для получения некоторых преимуществ перед остальными платформами, вы, видимо, предпочитаете эту нестрогость использовать для говнокода.
Хороший код нужен не только для новичков, но и чтобы более опытные коллеги вас дебилом не считали. Ну и чтобы экономить деньги работодателя.
Кичиться? Лет 8 назад во времена засилия чёрного SEO мой php-софт, написанный за 2 месяца, делал на автомате по 100к уников в день и приносил неслабые деньги, т.е. со своей задачей справлялся. Вот только трогать его я старался как можно меньше — всё висело на соплях. А трогать хотелось, т.к. развивать было что. Даже через год после разработки хотел запустить это на новом сервере — не удалось. Мораль сей басни такова — говнокод тоже бывает крутым и полезным. Чаще всего один раз и в неизменном виде.
P.S. Класс Record в Doctrine 1 состоит примерно из 2700 строк. Doctrine 1 — хороший для своих задач и своего времени ORM. В настоящее время есть Doctrine 2.
Производительность? Зависит от того, как использовать. В сферическом вакууме оптимизировать нечего, в другом вакууме можно лениво подсчитывать свойства один раз. В случае с ActiveRecord (как в приведённом коде), проще было бы один раз подсчитать (схема не так часто меняется). Но это всё экономия на спичках с учётом, что всё это происходит в ORM.
В современном мире нужно не только «быстрее, выше, сильнее». Предположим, у вас итеративный процесс. Наговнокодили «быстро, круто, сильно» первую итерацию, а дальше пошли новую работу искать.
Java-way? Если у вас есть желание и дальше считаться тупоголовыми php-шниками — не вопрос. Я предпочитаю использовать нестрогость PHP для получения некоторых преимуществ перед остальными платформами, вы, видимо, предпочитаете эту нестрогость использовать для говнокода.
Хороший код нужен не только для новичков, но и чтобы более опытные коллеги вас дебилом не считали. Ну и чтобы экономить деньги работодателя.
Кичиться? Лет 8 назад во времена засилия чёрного SEO мой php-софт, написанный за 2 месяца, делал на автомате по 100к уников в день и приносил неслабые деньги, т.е. со своей задачей справлялся. Вот только трогать его я старался как можно меньше — всё висело на соплях. А трогать хотелось, т.к. развивать было что. Даже через год после разработки хотел запустить это на новом сервере — не удалось. Мораль сей басни такова — говнокод тоже бывает крутым и полезным. Чаще всего один раз и в неизменном виде.
P.S. Класс Record в Doctrine 1 состоит примерно из 2700 строк. Doctrine 1 — хороший для своих задач и своего времени ORM. В настоящее время есть Doctrine 2.
+1
В таких случаях я стараюсь использовать публичные свойства, а геттеры и сеттеры я использую для критических свойств, которые необходимо более строго контролировать
Считаю, что удобнее для восприятия использовать что-то одно в отдельно взятом классе. Программист, работающий с классом, может не знать, какое свойство критическое, а какое — нет, и придется ему отвлекаться на выяснение «а как же правильно» для каждого свойства.
0
Всегда надо использовать геттеры, т.к. это позволяет сохранять интерфейс доступа к объекту при изменении логики работы объекта без применения «магии».
0
«Без фанатизма»
0
Никакого фанатизма. Единообразие и гибкость при отсутствии каких-либо значимых дополнительных временных затрат.
При наличии современных IDE вообще удивляюсь, что кому-то это может быть неудобно. Создал приватные свойства, на нужные повесил геттеры (public или protected, private для lazy-load'а и т.п.) В итоге пользователь интерфейса набирает в IDE «get» и видит все доступные геттеры.
Это реально ускоряет разработку:
1. Не надо раздумывать, какие свойства делать приватными, а какие — нет.
2. Становится доступен рефакторинг доступа к свойствам.
3. Список доступных для изменения/получения свойств доступен через набор префиксов get/set (при выборе стандартного паттерна именования геттеров/сеттеров).
При наличии современных IDE вообще удивляюсь, что кому-то это может быть неудобно. Создал приватные свойства, на нужные повесил геттеры (public или protected, private для lazy-load'а и т.п.) В итоге пользователь интерфейса набирает в IDE «get» и видит все доступные геттеры.
Это реально ускоряет разработку:
1. Не надо раздумывать, какие свойства делать приватными, а какие — нет.
2. Становится доступен рефакторинг доступа к свойствам.
3. Список доступных для изменения/получения свойств доступен через набор префиксов get/set (при выборе стандартного паттерна именования геттеров/сеттеров).
0
Не нужно забывать о возможных изменениях кода. Допустим $a->bar у нас был когда-то int и мы делали так
Со временем мы пришли к выводу, что нам нужно изменить int на array $a->bar = array(); и теперь по ВСЕМУ коду, который в своей работе использует класс $a, необходимо вносить изменения.
Если бы мы использовали get/set то изменения коснулись бы только этих методов класса
$a->bar = 1;
switch($a->bar) { case 1: ...
Со временем мы пришли к выводу, что нам нужно изменить int на array $a->bar = array(); и теперь по ВСЕМУ коду, который в своей работе использует класс $a, необходимо вносить изменения.
Если бы мы использовали get/set то изменения коснулись бы только этих методов класса
// было
public function setBar($value) {
$this->bar = (int) $value;
}
// стало
public function setBar($value) {
$this->bar[] = $value;
}
+2
Не нужно забывать и о том, что изменений вида $a->id = (int) в $a->id = (array) в принципе не должно быть. Переменные следует именовать, чтобы не было неоднозначности. Программист, который единичный целочисленный id преобразовал в массив и оставил его с тем же именем — очень плохой программист.
А если мы уверены в том, что конкретные (а зачастую все) атрибуты классы в дальнейшем не поменяют свой тип — зачем «перестраховываться» от того, чего не будет?
А если мы уверены в том, что конкретные (а зачастую все) атрибуты классы в дальнейшем не поменяют свой тип — зачем «перестраховываться» от того, чего не будет?
0
Мне тоже странно видеть размышления на тему, а если вдруг пользователь станет емейл сообщением, то что делать с его возрастом?
+2
Не хочу переходить, на личности, но зачем спорить? За плечами 8 лет ежедневного кодинга на PHP, сейчас мне этот язык нравится все меньше, в больших проектах «магия» и отсутствие типов иногда влечет за собой печальные последствия (в одном месте возвращает метод boolean в другом void и прочее)… «Мы уверены» — да ты можешь быть уверен, что ТВОЙ код не поменяется. Но если ты работаешь не один над проектом, который постоянно развивается, нельзя быть в чем-то уверенным. Это с родни преждевременной оптимизации, когда ты пишешь код основываясь на фичах текущей версии языка забывая о том, что язык изменяется — меняя свое поведение. Тем самым у тебя получается нечитаемый говнокод, который оптимально работает только с одной версией PHP.
Пример из жизни «напиши опрос — ну там ставишь один вариант ответа и голосуешь» -> «ну нам бы хотелось что бы было несколько опросов сразу» -> «ну да, но кроме radio должен быть еще выбор нескольких вариантов ответов» -> «а добавьте так что если выбрали один вариант, появляются другие зависимые от него»… Между -> может пройти и месяц и год и неделя… Код изначально нужно проектировать так что бы его можно было легко модифицировать.
Вариант с магией и огромным switch — плохая практика, во первых он разрастется со временем до такой степени, что понять его через 2 месяца не представится возможным, а заказчику нужно добавить фичу еще вчера…
Пример из жизни «напиши опрос — ну там ставишь один вариант ответа и голосуешь» -> «ну нам бы хотелось что бы было несколько опросов сразу» -> «ну да, но кроме radio должен быть еще выбор нескольких вариантов ответов» -> «а добавьте так что если выбрали один вариант, появляются другие зависимые от него»… Между -> может пройти и месяц и год и неделя… Код изначально нужно проектировать так что бы его можно было легко модифицировать.
Вариант с магией и огромным switch — плохая практика, во первых он разрастется со временем до такой степени, что понять его через 2 месяца не представится возможным, а заказчику нужно добавить фичу еще вчера…
0
1. Вот здесь habrahabr.ru/post/197332/#comment_6883056 вы писали от восторга, какой прекрасный код написали ребята из Yii. Почему же у них нет постфиксов «Array» в названиях переменных?
2. Кроме смены типа может смениться логика:
И вдруг (по объективным причинам, которые изначально не учли) мы захотели ограничить максимальное возможное количество возвращаемых записей.
3. Кто вам сказал, что в ваш прекрасный int не насуют массивов? Вы это как-то отслеживаете? Почему вы вообще говорите о типе свойства, если разговор идёт о public в языке без строгой типизации?
4. Сменить типа свойства просто — скажем, сменили строковую дату в ISO 8601 на DateTime.
2. Кроме смены типа может смениться логика:
$query->limit = 100000;
И вдруг (по объективным причинам, которые изначально не учли) мы захотели ограничить максимальное возможное количество возвращаемых записей.
public function setLimit($limit)
{
if ($limit > self::MAX_LIMIT) {
$limit = self::MAX_LIMIT;
}
$this->limit = $limit;
}
3. Кто вам сказал, что в ваш прекрасный int не насуют массивов? Вы это как-то отслеживаете? Почему вы вообще говорите о типе свойства, если разговор идёт о public в языке без строгой типизации?
4. Сменить типа свойства просто — скажем, сменили строковую дату в ISO 8601 на DateTime.
0
Ребята из Yii говнокодеры, я понял вашу мысль. А вы гораздо умнее их, даже при том что выносите логику поведения на уровень моделей, да-да.
0
А мне вот такой вариант нравится: gist.github.com/elfet/7081560
-4
Руководствуюсь принципом «поменьше магии».
0
Эх, опять.
Что мне всегда «нравилось» в подобных размышлениях, так это выводы в духе «мне удобно использовать этот вариант».
Т.е. мы как будто думаем о поддержке кода, но только со своей стороны.
Без обид, но посмотрите все статьи на эту тему, итог всегда один и тот же: одни советуют одно, другие — другое. Т.е. рано или поздно выбранный вами вариант будет ломать головы тем, кому придётся поддерживать ваш код, потому что онидругой религии предпочитают другой вариант.
А выбора всё равно лишаем…
Что мне всегда «нравилось» в подобных размышлениях, так это выводы в духе «мне удобно использовать этот вариант».
Т.е. мы как будто думаем о поддержке кода, но только со своей стороны.
Без обид, но посмотрите все статьи на эту тему, итог всегда один и тот же: одни советуют одно, другие — другое. Т.е. рано или поздно выбранный вами вариант будет ломать головы тем, кому придётся поддерживать ваш код, потому что они
А выбора всё равно лишаем…
0
Я считаю что вместо того чтобы вводить магические свойства необходимо рефакторить код. Насчет геттеров и прямого доступа, я уверен, что необходимо чтобы все кодеры писали в одном стиле. Или не используем геттеры совсем или не используем публичный доступ. Конечно же, мы выбираем геттеры. Любая современная IDE должна умет их автоматически генерировать.
0
За достаточно продолжительный срок разработки на php я использовал все три приведённых способа. И пришёл к такому выводу: когда с кодом работают от 2 человек, то количество магии должно быть сведено к минимуму. Поэтому лучше использовать геттеры и сеттеры. Да, кода становится чуть больше. Да, мы теряем какие-то наносекунды на вызов метода, но плюсы перевешивают минусы. Помимо указанных в публикации, я добавлю такие:
1. Проще рефакторинг
2. Программист всегда знает что происходит: вызов метода или обращение к свойству, а это о многом может рассказать.
1. Проще рефакторинг
2. Программист всегда знает что происходит: вызов метода или обращение к свойству, а это о многом может рассказать.
0
Sign up to leave a comment.
Публичные свойства, геттеры и сеттеры или магические методы?