Comments 50
Достаточно много видел разных вариантов реализации возможностей которые есть в обычных ОО языках (Delphi, Java, ActionScript) — и которых частенько не хватает php. А именно get & set методы.
ru.php.net/manual/ru/language.oop5.overloading.php
или это не то? %)
К сожалению, нет) это далеко не тоже самое =)
Между привычным(обычным) ооп в других языках и этой(которую Вы указали) большая разница.
Опять таки, на мой взгляд — я привел наиболее близкую к реальности реализацию данной функциональности в пхп.
Возможно найду время чуть позже. и постараюсь расписать разницу — чем такой ооп(с массивами) отличается от привычного)
Между привычным(обычным) ооп в других языках и этой(которую Вы указали) большая разница.
Опять таки, на мой взгляд — я привел наиболее близкую к реальности реализацию данной функциональности в пхп.
Возможно найду время чуть позже. и постараюсь расписать разницу — чем такой ооп(с массивами) отличается от привычного)
Определять геттеры/сеттеры вручную — сомнительный подход.
Что значит вручную?
Как то не понятно.
Когда пишешь геттер/сеттер — ты в любом случае пишешь его вручную. где бы ты этого не писал.
А вот, если Вы имели ввиду — полностью автоматизация без прописывания методов setProp|getProp — вот это действительно сомнительный подход. При таком подходе — вобще теряется смысл в геттерах/сеттерах ))
Как то не понятно.
Когда пишешь геттер/сеттер — ты в любом случае пишешь его вручную. где бы ты этого не писал.
А вот, если Вы имели ввиду — полностью автоматизация без прописывания методов setProp|getProp — вот это действительно сомнительный подход. При таком подходе — вобще теряется смысл в геттерах/сеттерах ))
или Source Code Highlighter не умеет пхп, или вы его не включили, но выглядит не очень красиво
скорее хабр не отображает, ввиду моей малой кармы или хабрасилы — потому что подстветка в коде была)
отображает теги наполовину? сомнительно.
да и не вижу я тут упоминания пхп:
да и не вижу я тут упоминания пхп:
именно там и подсвечивал. при редактировании — все фонты с цветами видны — но при просмотре топика — они по какой то причине вырезаются.
Должно хватать кармы, попробуйте снова :)
попробуйте убрать нумерацию строк — должна появиться подсветка.
ну вот, чтото более менее похожее на подстветку =)
www.phphighlight.com/ вот это подсвечивает приятнее
Используя такой способ, мы лишаемся возможности автоподстановки, да и лишнее наследование ни к чему. Если самому влом писать геттеры/сеттеры, можно пользоваться Zend Studio for Eclipse, там можно автоматически генерировать их.
мне кажется вы не совсем поняли.
автоподстановка никуда не пропадает, точно также можно сгененировать их в ZDE
НО данный класс — добавляет функциональности.
Если просто сгенерировать/прописать сеттеры/геттеры — вы не сможете воспользоваться элементарным присваиванием без функции.
Вы должны будете писать $obj->setName('Michael') вместо того, чтобы просто написать $obj->name='Michael';
Иногда второй вариант более чем предпочтителен.
автоподстановка никуда не пропадает, точно также можно сгененировать их в ZDE
НО данный класс — добавляет функциональности.
Если просто сгенерировать/прописать сеттеры/геттеры — вы не сможете воспользоваться элементарным присваиванием без функции.
Вы должны будете писать $obj->setName('Michael') вместо того, чтобы просто написать $obj->name='Michael';
Иногда второй вариант более чем предпочтителен.
Решение интересное, но (как всегда) подходить к этому стоит с головой =) Нужно держать в голове, что при использовании подобного подхода при интенсивных вычислениях вы на ровном месте получаете пенальти — это медленнее обычного использования на порядок.
Код test.php
Результаты:
Так что вот так.
PS: Ну и, разумеется, преждевременная оптимизация — корень всех зол (с) противоположность пряника
Код test.php
// определения классов Object и Rectangle <...> // Стандартный аналог class Rectangle2 { public $width = 400; public $height = 1; public function getArea(){ return $this->width*$this->height; } } $iterations = 10000; echo "Let's perform $iterations simple iterations!\n"; $r = new Rectangle(); $t = microtime(true); for ($i = 0; $i < $iterations; ++$i) { $r->height++; $r->height - $r->width - $r->area; } echo "Advanced class took ".round($res = microtime(true) - $t, 3)." sec\n"; $r = new Rectangle2(); $t = microtime(true); for ($i = 0; $i < $iterations; ++$i) { $r->height++; $r->height - $r->width - $r->getArea(); } echo "Standard class took ".round($res2 = microtime(true) - $t, 3)." sec\n"; echo "Difference: ", round($res/$res2, 2), " times!\n";
Результаты:
$ php test.php Let's perform 10000 simple iterations! Advanced class took 1.323 sec Standard class took 0.102 sec Difference: 13.03 times!
Так что вот так.
PS: Ну и, разумеется, преждевременная оптимизация — корень всех зол (с) противоположность пряника
«...-преждевременная оптимизация — корень всех зол-...»
именно =)
разница достаточно копеечная (учитывая что 10к присваиваний врятли будет в рамках одного вызова)
а быстродействие — как бы ни старался, на серьезных проектах без кеширования не обойтись.
а там где есть мудрое кеширование — вопрос с подобным быстродействием впринципе гораздо менее актуален, чем предельная прозрачность, удобство архитектуры и скорость правки, расширения =))
именно =)
разница достаточно копеечная (учитывая что 10к присваиваний врятли будет в рамках одного вызова)
а быстродействие — как бы ни старался, на серьезных проектах без кеширования не обойтись.
а там где есть мудрое кеширование — вопрос с подобным быстродействием впринципе гораздо менее актуален, чем предельная прозрачность, удобство архитектуры и скорость правки, расширения =))
А что нить типа этого?
class Container{
private $_islockedNames = true;
protected $_parameters;
public function __constructor($islockedNames = true){
$this->_islockedNames = (bool)$islockedNames;
}
public function __get($name){
if (isset($this->_parameters[$name])) {
return $this->_parameters[$name];
}
throw new EConteiner('__get()->'.$name);
}
public function __set($name, $value){
if (!$this->_islockedNames || isset($this->_parameters[$name])) {
$this->_parameters[$name] = $value;
}
throw new EConteiner('__set()->'.$name);
}
class Container{
private $_islockedNames = true;
protected $_parameters;
public function __constructor($islockedNames = true){
$this->_islockedNames = (bool)$islockedNames;
}
public function __get($name){
if (isset($this->_parameters[$name])) {
return $this->_parameters[$name];
}
throw new EConteiner('__get()->'.$name);
}
public function __set($name, $value){
if (!$this->_islockedNames || isset($this->_parameters[$name])) {
$this->_parameters[$name] = $value;
}
throw new EConteiner('__set()->'.$name);
}
Это не сильно отличается по смыслу от упоминавшегося выше
ru.php.net/manual/ru/language.oop5.overloading.php
ru.php.net/manual/ru/language.oop5.overloading.php
В принцепе да, только защелка на сет для неизвестных переменных.
Хорошо, еще вариант
class Container{
public function __get($name){
if (isset ($this->$name)) {
return $this->$name;
}
}
public function __set($name, $value){
if (isset ($this->$name)) {
$this->$name = $value;
}
throw new Exception();
}
}
Хорошо, еще вариант
class Container{
public function __get($name){
if (isset ($this->$name)) {
return $this->$name;
}
}
public function __set($name, $value){
if (isset ($this->$name)) {
$this->$name = $value;
}
throw new Exception();
}
}
1. isset в данном случае использовать ни в коем случае нельзя, посколько если значения свойства будет NULL — isset = вернет false, в то время как само свойство всё таки существует =)
2. если не пользоваться сеттерами/геттерами и присваивать вот так вот — в атоматическом режиме. т.е. если есть свойство (protected|private) то его присваивать — теряется смысл самих методов __get & __set и сокрытия свойства в protected
2. если не пользоваться сеттерами/геттерами и присваивать вот так вот — в атоматическом режиме. т.е. если есть свойство (protected|private) то его присваивать — теряется смысл самих методов __get & __set и сокрытия свойства в protected
1. насчет isset, да не проверил каюсь
2. не согласен, никто не мешает поставить дополнительную логику с одной стороны, а с другой ваш вариант делает тоже самое хотя на нем больше навернуто:-)
ЗЫ: вообще использую свой первый вариант в лоченом режиме с инициализацией в конструкторе нужных ключей/регистрацией ключей через отдельный метод.
2. не согласен, никто не мешает поставить дополнительную логику с одной стороны, а с другой ваш вариант делает тоже самое хотя на нем больше навернуто:-)
ЗЫ: вообще использую свой первый вариант в лоченом режиме с инициализацией в конструкторе нужных ключей/регистрацией ключей через отдельный метод.
на самом деле — никаких наворотов, а наоборот — проще некуда.
просто функции __get & __set различают в данном случае свойства типа
1.protected
2.protected у которых есть getter|setter
3.свойства которых у объекта вовсе нет
плюс к тому, debug_backtrace — позволяет показать — где в действительности произошла ошибка(т.е. в каком скрипте на какой строке произошла попытка вызова несуществующей переменной)
просто функции __get & __set различают в данном случае свойства типа
1.protected
2.protected у которых есть getter|setter
3.свойства которых у объекта вовсе нет
плюс к тому, debug_backtrace — позволяет показать — где в действительности произошла ошибка(т.е. в каком скрипте на какой строке произошла попытка вызова несуществующей переменной)
>>1.protected
>>2.protected у которых есть getter|setter
что можно отдовать, что нет можно определять префиксом '_' (или каким нибудь еще) просто договоренность в именовании
debug_backtrace тоже легко цепляется, хотя мне больше нравится ошибки ловить через систему ексепшенов
ченить типа этого:
class Container{
public function __get($name){
if (property_exists($this,$name) ) {
if ( $name[0] !=='_' ){
return $this->$name;
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Cannot access protected property %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line']), E_USER_ERROR);
*/
throw new EContainer('Cannot access protected property'. $name);
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Notice: Undefined property: %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line'])
, E_USER_NOTICE);
*/
throw new EContainer('Notice: Undefined property'. $name);
}
public function __set($name, $value){
if (property_exists($this,$name) ) {
if ( $name[0] !=='_' ){
$this->$name = $value;
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Cannot access protected property %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line']), E_USER_ERROR);
*/
throw new EContainer('Cannot access protected property'. $name);
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Notice: Undefined property: %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line'])
, E_USER_NOTICE);
*/
throw new EContainer('Notice: Undefined property'. $name);
}
}
class ExContainer extends Container {
private $_a = 'aaaa';
protected $c = 'cccc';
}
>>2.protected у которых есть getter|setter
что можно отдовать, что нет можно определять префиксом '_' (или каким нибудь еще) просто договоренность в именовании
debug_backtrace тоже легко цепляется, хотя мне больше нравится ошибки ловить через систему ексепшенов
ченить типа этого:
class Container{
public function __get($name){
if (property_exists($this,$name) ) {
if ( $name[0] !=='_' ){
return $this->$name;
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Cannot access protected property %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line']), E_USER_ERROR);
*/
throw new EContainer('Cannot access protected property'. $name);
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Notice: Undefined property: %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line'])
, E_USER_NOTICE);
*/
throw new EContainer('Notice: Undefined property'. $name);
}
public function __set($name, $value){
if (property_exists($this,$name) ) {
if ( $name[0] !=='_' ){
$this->$name = $value;
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Cannot access protected property %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line']), E_USER_ERROR);
*/
throw new EContainer('Cannot access protected property'. $name);
}
/*
$backtrace=debug_backtrace();
trigger_error(
sprintf('Notice: Undefined property: %s::$%s in %s on line %s',$this->getClass(),$name,$backtrace[0]['file'],$backtrace[0]['line'])
, E_USER_NOTICE);
*/
throw new EContainer('Notice: Undefined property'. $name);
}
}
class ExContainer extends Container {
private $_a = 'aaaa';
protected $c = 'cccc';
}
По поводу отлавливания ошибок через Exception — вопрос спорный довольно.
По скольку исключения и ошибки все таки — разные по своей сути.
Деление на 0 в большинстве случае исключительная ситуация, которая произошла скорее по непонятным причинам — чем осознанно или в следствии опечатки — была вызвана конструкция $x/0;
А вот ошибки нам указывают на явные огрехи, которые были допущены при проектировании, разработки и т.д.
В то время как ошибку можно устранить всегда, исключительные ситуации в истинном своем лице происходят по различным независящим практически от нас причинам =)
По скольку исключения и ошибки все таки — разные по своей сути.
Деление на 0 в большинстве случае исключительная ситуация, которая произошла скорее по непонятным причинам — чем осознанно или в следствии опечатки — была вызвана конструкция $x/0;
А вот ошибки нам указывают на явные огрехи, которые были допущены при проектировании, разработки и т.д.
В то время как ошибку можно устранить всегда, исключительные ситуации в истинном своем лице происходят по различным независящим практически от нас причинам =)
дело вкуса.
юзер запросил, что не положено.
функция проверки кинула ексепшен (ексепшен сам в лог пишет)
на более верхнем уровне словили и написали юзеру, что он кака при этом оформили в рюшечку и внесли бал неблагонадежности в базу
опять же банально вводим уровень ошибки как часть ексепшена
0 Fatal
1 Exception
2 Warning
3 Notice
ну и кому сколько надо
юзер запросил, что не положено.
функция проверки кинула ексепшен (ексепшен сам в лог пишет)
на более верхнем уровне словили и написали юзеру, что он кака при этом оформили в рюшечку и внесли бал неблагонадежности в базу
опять же банально вводим уровень ошибки как часть ексепшена
0 Fatal
1 Exception
2 Warning
3 Notice
ну и кому сколько надо
— «юзер запросил, что не положено»
согласен — это как раз таки случай Исключительной ситуации, поскольку юзер может запросить что угодно, и ошибка («что не положено») произойдёт не по нашей вине, но мы как порядочные кодеры — ее не пропустим — отловим и обработаем.
но вот присваивания значения неизвестным свойствам, запрос неизвестных свойств — это явно ошибка логики, поэтому такой запрос — именно ошибка, а не Exception который упомянут чуть выше =)
согласен — это как раз таки случай Исключительной ситуации, поскольку юзер может запросить что угодно, и ошибка («что не положено») произойдёт не по нашей вине, но мы как порядочные кодеры — ее не пропустим — отловим и обработаем.
но вот присваивания значения неизвестным свойствам, запрос неизвестных свойств — это явно ошибка логики, поэтому такой запрос — именно ошибка, а не Exception который упомянут чуть выше =)
я не считаю, что надо разделять.
если что то идет не так, то не важно кто виноват надо это пресекать, а единая система
другой пример:
модуль запросил, что то из контейнера (думал, что там это уже есть)
функция которая забирает(может __get, а может еще кто то) сгенерила ексепшен
модуль словил сказал вах и сделал запрос в БД(или еще куда)
ЗЫ: дело вкуса
если что то идет не так, то не важно кто виноват надо это пресекать, а единая система
другой пример:
модуль запросил, что то из контейнера (думал, что там это уже есть)
функция которая забирает(может __get, а может еще кто то) сгенерила ексепшен
модуль словил сказал вах и сделал запрос в БД(или еще куда)
ЗЫ: дело вкуса
— «если что то идет не так, то не важно кто виноват надо это пресекать, а единая система „
скажу отличие Error от Exception по другому ))
Exception — это те Error которые случаются время от времени, неизвестно когда будет следующая. При этом мы их можем спокойно обработать и пойтить работать(скрипт) дальше. Нам нет необходимости изменять существующий скрипт.
Но истинные Error — как бы не обрабатывали — не исчезнут, для пресекания подобной ошибки нужно лезть в код и править там ))
скажу отличие Error от Exception по другому ))
Exception — это те Error которые случаются время от времени, неизвестно когда будет следующая. При этом мы их можем спокойно обработать и пойтить работать(скрипт) дальше. Нам нет необходимости изменять существующий скрипт.
Но истинные Error — как бы не обрабатывали — не исчезнут, для пресекания подобной ошибки нужно лезть в код и править там ))
это можно организовать разными уровнями ексепшена, ексепшен с англицкого исключение.
да и просто когда у тебя проскакивает ошибка аж до боего сервера. пользователю намного приятней смотреть на надпись «сообщите пользователю», чем на пустой лист или на текст с логином и паролем от БД.
Пользователю должно быть глобоко до фени, запланированный это ексепшен или косяк в коде
да и просто когда у тебя проскакивает ошибка аж до боего сервера. пользователю намного приятней смотреть на надпись «сообщите пользователю», чем на пустой лист или на текст с логином и паролем от БД.
Пользователю должно быть глобоко до фени, запланированный это ексепшен или косяк в коде
Соглашения об именования — можно было бы и придерживаться. но опять таки
посмотрите на пример в посте с прямоугольником.
У него есть виртуальное свойство «area» которое я могу прочитать, но по понятным причинам — я его не могу установить — было несколько бредово))
И в данном случае соглашение об именовании никак бы не помогло разрешить ситуацию =)
посмотрите на пример в посте с прямоугольником.
У него есть виртуальное свойство «area» которое я могу прочитать, но по понятным причинам — я его не могу установить — было несколько бредово))
И в данном случае соглашение об именовании никак бы не помогло разрешить ситуацию =)
если цепляться за соглашения об именах, то сделать с морды две черточки для закрытах совсем, одну для закрытых га запись
а можно сделать список с правами аля $rights = [$paramName]=>rigt, где райт 0,1,2
заполнять этот список будет проще, чем прописывать гетеры
а можно сделать список с правами аля $rights = [$paramName]=>rigt, где райт 0,1,2
заполнять этот список будет проще, чем прописывать гетеры
Если у вас методы называются «по-верблюжьи», и к свойствам вы хотите обращаться со строчной, то в классе Object хорошо бы чуть изменить строчки, где формируется имя метода по переданной переменной. Например:
А вообще, мне нравится подход. :)
$getterMethod = 'get' . ucfirst($name);
А вообще, мне нравится подход. :)
К недостаткам данного варианта, можно добавить то, что в классах унаследованных от Rectangle работать getter/setter уже не будет
class MyRectangle extends Rectangle{
function dummy(){
//getHeight() не вызывается
var_dump($this->height);
}
}
$mr = new MyRectangle();
$mr->dummy();
class MyRectangle extends Rectangle{
function dummy(){
//getHeight() не вызывается
var_dump($this->height);
}
}
$mr = new MyRectangle();
$mr->dummy();
в Вашей интерпретации метод getHeight и не должен вызываться, поскольку к свойству height Вы обращаетесь внутри класса.
В данном случае height находится в области видимости класса MyRectangle — и следовательно magic метод __get не вызывается.
если вы попробуете вызвать снаружи —
метод getHeight — сработает.
Так что — работает так как и должно))
В данном случае height находится в области видимости класса MyRectangle — и следовательно magic метод __get не вызывается.
если вы попробуете вызвать снаружи —
class MyRectangle extends Rectangle{
}
$mr = new MyRectangle();
echo $mr->height;
метод getHeight — сработает.
Так что — работает так как и должно))
UFO just landed and posted this here
UFO just landed and posted this here
Не вижу противоречий в Ваших комментах и моём топе =)
Логику никто не отменял, и я вроде не предлагал использовать повсеместно ;)
Собственно статья вовсе не рассказывает где и как именно применяются Getter/Setter в программировании,
она лишь показывает достаточно красивое и эффективное решение данного вопроса не более, и ни в коей мере не является панацей от всего =)
Например, мне в силу особенностей некоторых моих реализаций — более чем необходим был инструментарий с которым я мог бы совершать прямые присваивания $this->myProp=$value;
и при этом пользоваться функциональностью Getter/Setter и более того, мне крайне необходимо было избежать случайных присваиваний =)
А про быстродействия — то, что кешируется — выводится «моментально» — а! микросекундные! задержки при совершении административных операций — мне кажется обсуждать нет смысла ;)
Логику никто не отменял, и я вроде не предлагал использовать повсеместно ;)
Собственно статья вовсе не рассказывает где и как именно применяются Getter/Setter в программировании,
она лишь показывает достаточно красивое и эффективное решение данного вопроса не более, и ни в коей мере не является панацей от всего =)
Например, мне в силу особенностей некоторых моих реализаций — более чем необходим был инструментарий с которым я мог бы совершать прямые присваивания $this->myProp=$value;
и при этом пользоваться функциональностью Getter/Setter и более того, мне крайне необходимо было избежать случайных присваиваний =)
А про быстродействия — то, что кешируется — выводится «моментально» — а! микросекундные! задержки при совершении административных операций — мне кажется обсуждать нет смысла ;)
не понимаю как топ мог залесть в минус… ведь клевое решение поставленной задачи, если вам это не нужно это не повод ставить «-»
Вообще, конечно, есть смысл автоматизировать написание свойств когда при присвоении есть еще какая-то логика, например проверка полученного значения.
Можно через __call().
Можно через __call().
Есть ещё одна тонкость.
выведет false.
Надо в класс Object добавить метод __isset, к примеру так:
Теперь
выведет true false
Как это реализованно в YII.
// выше используем код из примера к публикации
$rect->height=1000;
var_export(isset($rect->height));
выведет false.
Надо в класс Object добавить метод __isset, к примеру так:
public function __isset($name)
{
$getter='get'.$name;
if(method_exists($this,$getter)){
return $this->$getter()!==null;
}elseif(property_exists($this,$name)){
return true;
}
return false;
}
Теперь
$rect->height=1000;
var_export(isset($rect->height));
var_export(isset($rect->weight));
выведет true false
Как это реализованно в YII.
Sign up to leave a comment.
Getters/Setters getting real