ivan-lev: Константа - не свойство объекта, а заранее заданное выражение ("захардкожено" до начала выполнения скрипта).. И задать её при инициализации объекта не получится :) даже формально..
Ну если придираться к словам, то у класса могут быть constants (константы), members (члены, переменные), methods (методы), а все вместе они - class properties - т.е. свойства класса, которые его и определяют.
---------- Добавлено 07.06.2013 в 14:17 ----------
Константа - не свойство объекта, а свойство класса.
ivan-lev: for93t, на всякий случай.. Подход понимаю, но пытаюсь донести, что /на мой взгляд/ он не всегда является приемлемым и/или оптимальным. Особенно, если речь про работу с данными, получаемыми от пользователя. C ValueObject - Вполне логично, что если хост для подключения к DB указан неверно, то и смысла создавать соединение нет.
А я и не претендую на то, что озвучил истину во всех инстанциях :) Мой совет по использованию private-свойств относится к конкретной проблеме, которую озвучил ТС:
kiowas: К тому же если я создаю класс, то в конструкторе уже какие-то свойства класса проверяются и вроде бы этого должно быть достаточно, но нет же... Первый же вызов метода из конструктора и меня опять клонит в этом методе все переменные перепроверить.... А вдруг в конструкторе не проверилось?
Если применить озвученный мной подход, то можно быть уверенным, что экземпляр класса содержит корректное значение свойства и можно не беспокоиться, вызывая методы, работающие с ним.
---------- Добавлено 07.06.2013 в 13:43 ----------
ortegas: for93t, а еще можно использовать константы, в случае если она задается раз и навсегда.
Боюсь, ТС работает с user input, поэтому константы тут не помогут.
Хотя формально
kiowas: свойство в объекте, которое будет доступно для чтения из вне, но не доступно для изменения из вне?
kiowas: Ок. Переформулирую вопрос. Есть ли возможность создать свойство в объекте, которое будет доступно для чтения из вне, но не доступно для изменения из вне?
Да, такое свойство должно иметь модификатор доступа private (или protected, если есть классы-потомки, которые работают с этим свойством) и чтение его значения извне должно осуществляться с помощью public-геттера.
kiowas: Если появляется идея каким-то образом написать функцию или переопределить какую-то другую, так чтобы она была видна из любого куска скрипта и проверяла переменную на целое число положительно, в этом случае как лучше поступить? Как переопределить или создать свою функцию?
Простая глобальная функция (function isId($input) { ... }) будет видна из любой точки скрипта, если будет несколько таких функций, можно сделать их статическими методами какого-либо класса:
class Validator { static final public function isId($input) { ... } static final public function isEmail($input) { ... } ... }
, но это не панацея, потому что вместо кучи проверок типа is_numeric(), !empty() и т.п. вы будете писать кучу проверок типа Validator::isId(). Это немного сократит код, но не устранит проблему. Нужно изолировать данные и делать доступ к ним контролируемым, для этого (в т.ч.) и была придумана инкапсуляция.
ivan-lev: Пользователю в "цивильном" виде сообщать..
С отображением ему формы ввода.. с теми же данными, которые он ввёл на предыдущем шаге.. и подсветкой ошибки по соседству с полем, в котором ошибка.
Цивильное отображение - это уровень View, а валидация - уровень модели. Вьюхе, грубо говоря, все равно, как происходит проверка данных, ей важно получить список сообщений об ошибках и инструкции, куда их вставлять. А как был сформирован этот список - через исключения, возвращение false или прямым инжектом в какой-нибудь инстанс-коллектор сообщений - View может, точнее должен, не знать.
ivan-lev: А чего ж сразу про сеттер не написать?
ИМХО, гиблый подход.. смешивать конструктор/сеттеры и валидацию.
Думаю, корректнее выносить валидацию в отдельный метод, в котором (в зависимости от задачи либо проверять все поля сразу, либо до первой ошибки) а вызывать явно в контроллере или неявно из методов модели. /Хотя, вполне возможно, иногда такой подход может быть оправданным.../
Я привел в пример класс, который реализует паттерн ValueObject. Для таких классов нет смысла создавать инстанс с заведомо неправильным значением какого-либо поля, поэтому бросание исключение там вполне уместно.
Валидация данных в модели крайне желательна, поскольку с одной и той же моделью может работать несколько контроллеров и в каждом потребуется делать валидацию, если этого не будет делать модель, а это дублирование кода со всеми вытекающими.
---------- Добавлено 07.06.2013 в 00:49 ----------
ivan-lev: ИМХО, гиблый подход.. смешивать конструктор/сеттеры и валидацию
Вот тут вы ошибаетесь. Если public-сеттер работает с private переменной, он содержит логику валидации значений для этой переменной и существует соглашение о том, что извне задать значение переменной класса можно только с помощью этого метода, то получается единая точка ввода, которая гарантирует, что данная переменная всегда будет иметь корректное значение (или не иметь его вовсе, т.е. быть null). Такой подход очень распространен в серьезных приложениях/фреймворках. Если вы посмотрите в исходный код ZF, Symfony, Yii и т.п., то обязательно увидите примеры таких сеттеров. Самый упрощенный пример - это сеттер с type hint'ом для аргумента, где type hint как раз служит валидатором. Думаю, таких примеров можно найти предостаточно в любом "неговнокоде" на PHP.
/**
* Class constructor
*
* @access public
*
* @param string $host
* @param int $priority
* @param int $port
*
* @throws Core_Dns_Exception
* @return Core_Dns_Mx_Route
*/
public function __construct($host, $priority = 10, $port = 25)
{
if (!Zend_Validate::is($host, 'Hostname', array('allow' => Zend_Validate_Hostname::ALLOW_DNS + Zend_Validate_Hostname::ALLOW_IP + Zend_Validate_Hostname::ALLOW_LOCAL))) {
throw new Core_Dns_Exception(
escsprintf(_("MX hostname '%s' is invalid. It should be either valid DNS hostname or valid IP address"), $host)
);
}
ivan-lev: Валидатор он на то и валидатор, чтобы "проверять" (возвращать тру/не тру + получать список ошибок) , а не исключения кидать.
kiowas: К тому же если я создаю класс, то в конструкторе уже какие-то свойства класса проверяются и вроде бы этого должно быть достаточно, но нет же... Первый же вызов метода из конструктора и меня опять клонит в этом методе все переменные перепроверить.... А вдруг в конструкторе не проверилось?
Чтобы быть спокойным относительно переменных класса, их нужно делать private и задавать значения только через конструктор и только там делать валидацию. Если валидация не проходит, то конструктор должен бросать исключение. Для правильной валидации существуют такие библиотеки, как, например, Zend Validate - поначалу использование таких решений может выглядеть избыточно, но когда набивается достаточно шишек на собственных велосипедах, приходишь к выводу, что ничего особо лишнего на самом деле там нет.
Константа - не свойство объекта, а заранее заданное выражение ("захардкожено" до начала выполнения скрипта).. И задать её при инициализации объекта не получится :) даже формально..
Ну если придираться к словам, то у класса могут быть constants (константы), members (члены, переменные), methods (методы), а все вместе они - class properties - т.е. свойства класса, которые его и определяют.
---------- Добавлено 07.06.2013 в 14:17 ----------
Константа - не свойство объекта, а свойство класса.
for93t, на всякий случай.. Подход понимаю, но пытаюсь донести, что /на мой взгляд/ он не всегда является приемлемым и/или оптимальным. Особенно, если речь про работу с данными, получаемыми от пользователя. C ValueObject - Вполне логично, что если хост для подключения к DB указан неверно, то и смысла создавать соединение нет.
А я и не претендую на то, что озвучил истину во всех инстанциях :) Мой совет по использованию private-свойств относится к конкретной проблеме, которую озвучил ТС:
К тому же если я создаю класс, то в конструкторе уже какие-то свойства класса проверяются и вроде бы этого должно быть достаточно, но нет же... Первый же вызов метода из конструктора и меня опять клонит в этом методе все переменные перепроверить.... А вдруг в конструкторе не проверилось?
Если применить озвученный мной подход, то можно быть уверенным, что экземпляр класса содержит корректное значение свойства и можно не беспокоиться, вызывая методы, работающие с ним.
---------- Добавлено 07.06.2013 в 13:43 ----------
for93t, а еще можно использовать константы, в случае если она задается раз и навсегда.
Боюсь, ТС работает с user input, поэтому константы тут не помогут.
Хотя формально
свойство в объекте, которое будет доступно для чтения из вне, но не доступно для изменения из вне?
и есть определение константы класса.
Ок. Переформулирую вопрос. Есть ли возможность создать свойство в объекте, которое будет доступно для чтения из вне, но не доступно для изменения из вне?
Да, такое свойство должно иметь модификатор доступа private (или protected, если есть классы-потомки, которые работают с этим свойством) и чтение его значения извне должно осуществляться с помощью public-геттера.
Есть ли возможность защитить от изменения публичное свойство объекта?
Публичное свойство по определению нельзя защитить от прямых изменений - на то оно и публичное, что каждый может его читать и изменять.
Если появляется идея каким-то образом написать функцию или переопределить какую-то другую, так чтобы она была видна из любого куска скрипта и проверяла переменную на целое число положительно, в этом случае как лучше поступить? Как переопределить или создать свою функцию?
Простая глобальная функция (function isId($input) { ... }) будет видна из любой точки скрипта, если будет несколько таких функций, можно сделать их статическими методами какого-либо класса:
, но это не панацея, потому что вместо кучи проверок типа is_numeric(), !empty() и т.п. вы будете писать кучу проверок типа Validator::isId(). Это немного сократит код, но не устранит проблему. Нужно изолировать данные и делать доступ к ним контролируемым, для этого (в т.ч.) и была придумана инкапсуляция.
Пользователю в "цивильном" виде сообщать..
С отображением ему формы ввода.. с теми же данными, которые он ввёл на предыдущем шаге.. и подсветкой ошибки по соседству с полем, в котором ошибка.
Цивильное отображение - это уровень View, а валидация - уровень модели. Вьюхе, грубо говоря, все равно, как происходит проверка данных, ей важно получить список сообщений об ошибках и инструкции, куда их вставлять. А как был сформирован этот список - через исключения, возвращение false или прямым инжектом в какой-нибудь инстанс-коллектор сообщений - View может, точнее должен, не знать.
А чего ж сразу про сеттер не написать?
ИМХО, гиблый подход.. смешивать конструктор/сеттеры и валидацию.
Думаю, корректнее выносить валидацию в отдельный метод, в котором (в зависимости от задачи либо проверять все поля сразу, либо до первой ошибки) а вызывать явно в контроллере или неявно из методов модели. /Хотя, вполне возможно, иногда такой подход может быть оправданным.../
Я привел в пример класс, который реализует паттерн ValueObject. Для таких классов нет смысла создавать инстанс с заведомо неправильным значением какого-либо поля, поэтому бросание исключение там вполне уместно.
Валидация данных в модели крайне желательна, поскольку с одной и той же моделью может работать несколько контроллеров и в каждом потребуется делать валидацию, если этого не будет делать модель, а это дублирование кода со всеми вытекающими.
---------- Добавлено 07.06.2013 в 00:49 ----------
ИМХО, гиблый подход.. смешивать конструктор/сеттеры и валидацию
Вот тут вы ошибаетесь. Если public-сеттер работает с private переменной, он содержит логику валидации значений для этой переменной и существует соглашение о том, что извне задать значение переменной класса можно только с помощью этого метода, то получается единая точка ввода, которая гарантирует, что данная переменная всегда будет иметь корректное значение (или не иметь его вовсе, т.е. быть null). Такой подход очень распространен в серьезных приложениях/фреймворках. Если вы посмотрите в исходный код ZF, Symfony, Yii и т.п., то обязательно увидите примеры таких сеттеров. Самый упрощенный пример - это сеттер с type hint'ом для аргумента, где type hint как раз служит валидатором. Думаю, таких примеров можно найти предостаточно в любом "неговнокоде" на PHP.
эм.. а если надо задать "по ходу".. не в конструкторе?
Тогда нужно сделать public setter-метод, запрограммировать в нем валидацию и использовать его в конструкторе для установки значения переменной класса.
Вы реально так делаете? Можно кусок кода?
Если значение переменной класса критично для функционирования всего класса, то да, я реально так делаю. Пример кода:
Валидатор он на то и валидатор, чтобы "проверять" (возвращать тру/не тру + получать список ошибок) , а не исключения кидать.
А что вы предлагаете дальше делать с тру/не тру?
движок аукционов phpprobid (платный)
Попробую свои непрокачанные навыки телепата...
Предварительно нужно убедиться, что используемые кодировки совпадают.
К тому же если я создаю класс, то в конструкторе уже какие-то свойства класса проверяются и вроде бы этого должно быть достаточно, но нет же... Первый же вызов метода из конструктора и меня опять клонит в этом методе все переменные перепроверить.... А вдруг в конструкторе не проверилось?
Чтобы быть спокойным относительно переменных класса, их нужно делать private и задавать значения только через конструктор и только там делать валидацию. Если валидация не проходит, то конструктор должен бросать исключение. Для правильной валидации существуют такие библиотеки, как, например, Zend Validate - поначалу использование таких решений может выглядеть избыточно, но когда набивается достаточно шишек на собственных велосипедах, приходишь к выводу, что ничего особо лишнего на самом деле там нет.