Forum Webscript.Ru

Программирование => PHP => Тема начата: Алексей от 28 Октября 2004, 19:10:54

Название: Помогите с ООП
Отправлено: Алексей от 28 Октября 2004, 19:10:54
У меня цель - написать класс для гостевой книги работающей с мускулом.
Изучил ООП в 4 версии. Собственно, изучать то там толком нечего... да вот только не могу я класс создать. ну не представляю, как это правильно сделать. Начинаю делать - получается какая то смесь ООП с процедурным. Бредятина...

вот какая то фигня получилась:


class db{
var $lnk;

//Соеденение с MySql, выбор базы данных, возвращение идентификатора ссылки
function db($server, $username, $password, $db){
$this->lnk = mysql_connect($server, $username, $password) or die("Не могу соедениться");
mysql_select_db($db, $this->lnk) or die("Не могу выбрать базу $db");
}

/* Запрос к БД, возвращение массив многомерный вида
Array
(
    [user_id] => Array
        (
            [0] => 12
            [1] => 45
        )

    [user_name] => Array
        (
            [0] => "Вова"
            [1] => "Костя"
        )
...
*/
function db_get_rezult($query){
$result = mysql_query($query, $this->lnk) or die();
if(mysql_num_rows($result)>0){
while($array = mysql_fetch_assoc($result)){
foreach($array as $key=>$value)
$HTML[$key][] = $value;
}
return $HTML;
}
return false;
}

//количество записей в гостевой возвращает...
function db_count_query(){
$c = mysql_fetch_row(mysql_query("SELECT COUNT(*) FROM gb", $this->lnk));
return $c[0];
}
//закрытие соеденения
function close(){
@mysql_close($this->lnk);
}
}




$myDb = new db($server, $username, $password, $db);

$HTML = $myDb->db_get_rezult("SELECT .........");

$c = $myDb->db_count_query();


ну и непойму что делать дальше... т.е. как то неуклюже всё это. надо данные теперь обрабатывать, массив $HTML - как это делать?... и вообще, мне кажется я всё в корне не верно делаю...
Название: Помогите с ООП
Отправлено: Меняздесьдавнонет от 28 Октября 2004, 19:33:09
Нормально ты все делаешь.
вот только функцию db_count_query() надо, конечно, выкинуть оттуда.
ведь ее функционал прекрасно покрывается функцией db_get_rezult

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

массив $HTML здесь же обрабатывать не надо.
пишешь класс, выводящий, к примеру, записи, и в нем используешь дб.

Вообще, по-моему, к веб-программированию ООП сильно притянут за уши.
но напиши класс, который имеет методы шоу, инпут, и так далее
Название: Помогите с ООП
Отправлено: Макс от 28 Октября 2004, 23:28:39
Алексей
что должен делать твой класс ?
Если абстракция от базы данных или упрощенная работа с MySQL - то это один класс.
Если управление записями - это другой класс.

Если тебе надо и то и другое, то тебе надо 2 класса, а не один.

По твоему классу замечение только одно - имя таблицы вынеси в переменную класса:
var $table = "gb";
Название: Помогите с ООП
Отправлено: Алексей от 29 Октября 2004, 08:39:03
Цитировать
RomikChef:
ты делаешь какой-то очень странный массив.

ну..это для шаблона.
Цитировать
RomikChef:
массив $HTML здесь же обрабатывать не надо.

а где надо?
Цитировать
Макс:
что должен делать твой класс ?

всё, что нужно с простейшей гостевой - вносить запись, получать результат, обрабатывать результат (htmlspecialchars etc) делать отстраничиватель и всё остальное.

Суть в том, что я не знаю, как правильно и сколько именно нужно сделать классов.

Вообще то хотел сделать один супер-класс, который манипулировал бы с гостевой, но как я понимаю, так не делают, а нужно сделать общий класс для работы с БД, класс для вывода записей, класс для отстраничивателя и т.д. Так?
Название: Помогите с ООП
Отправлено: Меняздесьдавнонет от 29 Октября 2004, 10:52:57
Цитировать
Алексей:
ну..это для шаблона.

для шаблона он все равно очень странный.

один супер-класс действительно, не делают.
в нем нет никакого смысла.
классы пишутся как раз для многоразового использования, а не для того, чтобы одну книгу написать.
Но с другой стороны,Ю в веб-программировании классы- вещь весьма спорная, и в гостевой притянута за уши.

Но если так уж хочется - напиши два класса - 1 дб, а второй - гостевая, расширяющий первы.
Будет у тебя супер крутое ооп приложение.

лабу-то когда сдавать?
Название: Помогите с ООП
Отправлено: Меняздесьдавнонет от 29 Октября 2004, 10:55:11
похоже, ты и с процедурным гостевую не очень хорошо представляешь, как написать, то есть, твоя задача усложняется в два раза.
Название: Помогите с ООП
Отправлено: Алексей от 29 Октября 2004, 11:24:46
Цитировать
RomikChef:
Но с другой стороны,Ю в веб-программировании классы- вещь весьма спорная, и в гостевой притянута за уши.

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

какую лабу? :-/

Цитировать
RomikChef:
для шаблона он все равно очень странный.

я знаю, что в меня щас камни полетят, но я делаю так:


Имя: .html"  onclick="infoWindow(this.href); return false;">


Комментарий:




Название: Помогите с ООП
Отправлено: Макс от 29 Октября 2004, 11:48:58
Если английский знаешь - то поищи информацию про паттерн DAO
Цитировать
Алексей:
вносить запись, получать результат, обрабатывать результат (htmlspecialchars etc) делать отстраничиватель и всё остальное.

никаких "все остальное". Должен быть четкий список, что будет делать класс.

Можно сделать 2 или 3 класса.
1. Работа с MySQL
2. Работа с одной записью (добавление/редактирование/удаление/проверка)
3. Работа со списком записей (постраничный вывод, сортировка)

2 и 3 можно объединить в один класс.
Название: Помогите с ООП
Отправлено: Алексей от 29 Октября 2004, 12:25:42
Цитировать
Макс:
1. Работа с MySQL

что ты под этим подразумеваешь? что значит работа с майскёл?

Цитировать
Макс:
2. Работа с одной записью (добавление/редактирование/удаление/проверка)

хм.. я чес. говоря с бооооольштим трудом представляю, как в класс можно запихнуть "добавление/редактирование/удаление/проверка" записей... вот смотри, это проверка информации перед внесением в базу:


if($_SERVER["REQUEST_METHOD"]=="POST")
{
foreach($_POST as $key=>$value)
$_POST[$key]=clearPost($value);

if(!$user_is_login)
$err .= "
  • Вы не являетесь зарегестрированным пользователем.
    Вероятно, Вы начинающий кулхацкер? Хе-хе...
  • ";
    if(!$_POST["user_message"])
    $err .= "
  • Вы не ввели сообщение.
  • ";
    else if($t = bad_max_length($_POST["user_message"],$ml_user_message))
    $err .= "
  • Введённое Вами сообщение слишком велико. Удалите $t символов.
  • ";
    if(is_referer())
    $err .= "
  • Форма, с которой отправляется запрос, должна находиться на сервере.
  • ";

    //Ошибок нет
    if(!$err){
    //Только тут используется ООП...
    $myDb = new db;
    $myDb->db_query("INSERT INTO gb VALUES(NULL, $_COOKIE[user_id], \'".adds($_POST["user_message"])."\', NOW(), \'\', 0)");
    $myDb->close();
    header("Location: ./?".time()); exit;
    }
    else $err = "
    Ошибка!
      $err
    ";
    } else $_POST["user_message"] = "";

    не нужно вникать в этот код. Просто это мой наглядный пример - смесь ООП с структ. программированием. Просто я не могу понять, как можно все эти проверки в класс запихнуть.. да и стоит ли так делать?
    Название: Помогите с ООП
    Отправлено: Меняздесьдавнонет от 29 Октября 2004, 12:34:13
    Этот код оформляется метод класса, скажем, Write.

    Но, по хорошему, как мне кажется, на гостевой ООП изучать неправильно..

    Макс, ты можешь привести пример нормального приложения, в котором клсссы, кроме дб, действительно нужны?
    Хотя бы одну пару с наследованием?
    Только не заумное, плиз.
    Название: Помогите с ООП
    Отправлено: Алексей от 29 Октября 2004, 13:59:01
    Цитировать
    RomikChef:
    Этот код оформляется метод класса, скажем, Write.

    ..и получился бред какой-то. только всё стало намного замороченее, непонятнее, гибкость исчезла. условно говоря, классы стали неким подобием боольшх кривых и убогих функций.
    Название: Помогите с ООП
    Отправлено: Меняздесьдавнонет от 29 Октября 2004, 14:13:31
    скажем так.
    Для гостевой даже и функции-то не нужны.

    Классы, как и функции - это модули. Кубики. Для многократного использования.
    В частности, кубик для работы с базой можно написать, и он пригодится для написания гостевой, новостей и чего угодно.

    Писать же целиком гостевую с использованием ООП имеет смысл тому, кто все всегда так пишет.
    Название: Помогите с ООП
    Отправлено: Макс от 29 Октября 2004, 14:27:43
    Цитировать
    Алексей:
    что ты под этим подразумеваешь? что значит работа с майскёл?

    Если тебе не нужна работа с несколькими СУБД (абстрактный доступ к базам данных), то класс для работы с БД ИМХО нужен лишь для упрщения твоей работы. В класс вынести некоторые часто используемые операции. Например :
     - получить одно значение из таблицы
     - получить одну запись из таблицы.
    ....
    В качестве примера я могу привести лишь класс для ПХП5 (http://www.livejournal.com/users/max_m/5096.html#cutid1).
    Можешь посмотреть там идеи и реализовать что-то похожее для ПХП4

    Цитировать
    RomikChef:
    Макс, ты можешь привести пример нормального приложения, в котором клсссы, кроме дб, действительно нужны?
    Если ключевыми словами в твоем вопросе являются "действительно нужны", то нет, не могу. Я сейчас сяду писать текст, как у меня используются классы, но  он будет большой (не знаю, насколько заумный)
    Название: Помогите с ООП
    Отправлено: Макс от 29 Октября 2004, 16:31:16
    Итак, как у меня используютя классы.

    Я не буду описывать работу с БД. У меня пока используется adodb (статья по нему есть на detail.phpclub.ru).
    Также я пропущу работу с классом шаблонизатора (pear::html::teamplate::sigma) - работа с ним принципиально ничем не отличается от работы с другими шаблонизаторами.

    Первое о чем я упомяну - это регистр объектов.
    Изнутри, регистр объектов (ObjectRegistry) представляет собой массив, в котором хранятся ссылки
    на объекты.
    Зачем это нужно ? Допустим у нас класс, которому нужен доступ к классу базы данных и шаблонизатору.
    Если не использовать global (а я его принципиальноне использую), то эти объекты
    можно получить лишь передав их через конструктор или соответствующий метод.
    $object = new SomeObject($conn, $tmpl);
    Если допустим появятся еще и настройки (почему у меня настройки в своем классе объяснять не хочу - это необязательное решение)
    то надо будет
     1. переписать класс, чтобы он принимал 3 параметра
     2. переписать код в котром используется класс и добавить еще один параметр:
    $object = new SomeObject($conn, $tmpl, $settings);
    Мало того, что мы кучу вего написали, так еще и конструктор стал получать кучу параметров

    В случае с ObjectRegistry у меня всегда вызов конструктора выглядит :
    $object = new SomeObject($registry);

    Хотя основная его цель - позволить объектам взаимодействовать друг с другом.

    Теперь переходим к основной части.
    Допустим на сайте есть Новости, Регистрация, Статьи.
    То есть на сайте есть такие сущности как новость, пользователь и статья
    У этих 3-х сущностей есть общее поведение:
     - добавление (проверяем входные данные, если все Ок, генерируем INSERT-запрос и выполняем его)
     - уделаение (тут все ясно)
     - редактирование (очень похоже на добавление только UPDATE-запрос генерируется)
    Но есть и различия. Например у разных сущностей разные проверки.
    Также некоторые сущности имеют еще и дополнительное поведение (например у юзера есть авторизация, "Вспомнить пароль")

    У меня есть абстрактный класс DBObject.
    В его задачу входит работа с сущностями - проверка/добавление/редактирование/удаление и т.д.
    Рассмотрим добавление сущности в таблицу.
    Здесь можно выделить такие этапы:
    1. Получение данных из POST-запроса
    2. Проверка данных
    3. Подготовка данных к добавлению
    Например при регистрации юзера надо пароль зашифровать.
    Также здесь к данным добавляется время добавления (если в таблице есть такое поле).
    Также на этом этапе при регистрации юзера генерируется случайное число, которое шлется ему на e-mail для подтверждения регистрации
    Вобщем здесь генерируются некоторые значения для полей, которых нет в реальной форме добавления.
    4. Выполняем INSERT-запрос и получаем идентификатор новой записи
    5. Еще что-нибудь
    Например если юзер закачивал картинку, то она из временной папки копируется в папку с картинками и получает имя
    $user_id.jpeg
    Также здесь можно выполнить некоторые другие SQL-запросы, если например сущность
    хранится в нескольких таблицах. Например юзер указал свои интересы и здесь они записываются в отдельную таблицу (связь "многие-ко-многим")

    Метод add() класса DBObjects выполняет вставку записи. Выглядит он так (упрощенно) :

       function add() {
          $this->loadFromPost();     // загружаем данные из ПОСТ
          $this->checkData(\'add\');   // проверяем
          if (sizeof($this->errors) == 0) { // если нет ошибок
             // if data OK
             $this->prepareData(\'add\');  // подготавливаем данные в вставке
             // генерируем INSERT-запрос (это фича adodb)
             $res = $this->conn->Execute("SELECT * FROM ".$this->table." WHERE ".$this->id_name." = -1");
             $sql = $this->conn->GetInsertSQL($res, $this->fields);
             // запрос сгенерировали, теперь выполняем
             $this->conn->Execute($sql);
             $this->id = $this->conn->Insert_ID();
             $this->onAdd(); // вызывается после вставки данных
             return $this->id;
          } else {
             return false;
          }
       }

    я убрал некоторые проверки, чтобы код был более понятным.
    У трех упомянутых сущностей только 3 метода (используемые в этом коде) могут
    различаться : $this->onAdd(), $this->prepareData(), $this->checkData()
    Именно эти методы надо будет переопределить в наследниках (можно и не переопределять если они не нужны)

    Класс DBObject имеет такие методы (только основные)
    setID($id) - получает ИД объекта, делает запрос к БД и получает данные о сущности
    add() - описан выше
    edit() - редактирование объекта
    delete() - удаление объекта
    Эти объекты переопределять не надо
    onAdd(), onEdit(), onDelete() - эти объекты можно переопределить
    checkData() - проверка данных (тоже переопределяется)
    prepareData() - подготовка данных (тоже переопределяется)
    --------
    Как все это работает.
    Делаем наследника (для этого есть генератор), ручкам пишем методы
    checkData(), prepareData() + события on*() а в скриптах код выглядит :


    добавление

          $news = new News($reg); // $reg == ObjectRegistry
          if ($_SERVER[\'REQUEST_METHOD\'] == \'POST\') {
             if ($news->add()) {
                // новость добавлена
                _header(\'news.php?\'.SID);
                exit;
             } else {
                // произошла ошибка
                $errors = $news->errors;
                $tpl->setVariable(er_convert($errors)); // передаем ошибку в шаблон
             }
          }
          /// тут работа с шаблонизатором

    редактирование

          $cat = new Category($reg);
          // идентификатор категории, которую редактируем
          $cat_id = max(0, intval($_REQUEST[\'cat_id\']));
          if ($_SERVER[\'REQUEST_METHOD\'] == \'POST\') {
             $cat->setID($cat_id);
             if ($cat->edit()) {
                // если ошибок нет
                _header("cats.php?".SID);
                exit;
             } else {
                $errors = $cat->errors;
                // передаем ошибки шаблону
                $tpl->setVariable(er_convert($errors));
                // передаем введенные данные шаблону
                $tpl->setVariables($cat->getFields()) //
             }
          } else {
             $cat->setID($cat_id);
             // передаем шаблону данные, которые надо отредактировать
             $tpl->setVariables($cat->getFields()) //
          }

    Удаление

          $cat_id = max(0, intval($_GET[\'cat_id\']));
          $cat = new Category($reg);
          $cat->setID($cat_id);
          $cat->delete();
          _header(\'cats.php?\'.SID);


    Если используемые методы не предоставляют нужную мне функциональность, то
    я могу дописать метод или реализовать его без класса (я на классах не помешан)

    Позволяет ли этот подход повторно использовать код ?
    Да позволяет.
    Написал класс User. Сделал наследника, чуть модфицировал - получил класс Admin.
    Сделал класс для управления файлами (с upload-ом), сделал наследника, чуть модифицировал - получил класс для управления только изображениями.

    Пример, как создается наследник.
    Сначало создаем ini-файл. Примерно такого типа

    [db]
    table = tgp_links # название таблицы
    id_name = link_id # имя первичного ключа

    [class]
    name = Link # имя класса
    extends = DBObject # имя родительского класса

    [fields] # это поля и фильтры для них
    cat_id = intval
    url =
    title = htmlspecialchars
    description = "htmlspecialchars|nl2br"

    По этому ini-файлу генератор генерирует класс.
    Дальше ручками надо написать нужные методы:

       // проверка просто для примера
       function checkData($mode=\'add\') {
          if (empty($this->fields[\'title\'])) {
             $this->errors[\'title\'] = _("Title is empty");
          }
          if (empty($this->fields[\'description\'])) {
             $this->errors[\'description\'] = _("Description is empty");
          }
          if (empty($this->fields[\'url\'])) {
             $this->errors[\'url\'] = _("URL is empty");
          }  
       }
       function prepareData($mode = \'add\') {
          if ($mode == \'add\')    { // в массив данных (которые будут вставлены в таблицу)
                                   // добавляем время записи.
             $this->fields[\'date\'] = time();
          }
       }

    создав эти 2 метода класс полностью готов к работе.
    Правда еще шаблоны сверсать надо, но это уже другая история.

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

    Одно из преимуществ того, что классы всех сущностей похожи, в том, что очень
    много кода можно генерировать (не толлько классы, но и код)

    По поводу объемов кода
    DBObject + DBObjectList  = 15 кб
    Наследники по разному = от 1 - 17 кб (в среднем около 7 кб)

    Все дальше писать лень.
    Название: Помогите с ООП
    Отправлено: Макс от 29 Октября 2004, 16:40:53
    Ключевые слова по теме:
    DAO (pattern)
    ORM
    Название: Помогите с ООП
    Отправлено: Меняздесьдавнонет от 29 Октября 2004, 16:44:27
    Папа, ты с кем сейчас разговаривал? ;-)
    Название: Помогите с ООП
    Отправлено: Макс от 29 Октября 2004, 16:55:34
    RomikChef
    денек пусть повисит, если у автора не будет вопросов - прибью свою речь
    Название: Помогите с ООП
    Отправлено: Меняздесьдавнонет от 29 Октября 2004, 17:21:21
    Да не, пусть висит.
    Хорошая речь - я даже половину понял :-)
    Друое дело, что, боюсь, для клиента будет сложновато, все-таки...

    Я потому факи и пишу, что не далеко от них ушел ;-)
    Название: Помогите с ООП
    Отправлено: Slastik от 30 Октября 2004, 01:14:37
    Макс
    Моща :)
    На целую статью потянет.
    Главное что я извлек, это то что надо брать умные книжки по ООП,
    и штудировать штудировать и еще раз штудировать :)
    Мне всегда было интересно, это людям с рождения дано,
    или если я буду к примеру года два ботать пхп,  то тоже
    умным стану?

    ЗЫ
    Приятно видеть что RomikChef тоже не все на свете знает :)
    Название: Помогите с ООП
    Отправлено: Макс от 30 Октября 2004, 10:40:18
    Slastik
    насчет книг, то по этой теме можно почитать
    http://www.books.ru/shop/books/156126

    А если английский знаешь то полезно почитать форум
    http://www.sitepoint.com/forums/forumdisplay.php?f=147

    Цитировать
    Slastik:
    Мне всегда было интересно, это людям с рождения дано, или если я буду к примеру года два ботать пхп, то тоже умным стану?
    я из тех, кто считает, что программистами рождаются :)
    Название: Помогите с ООП
    Отправлено: Алексей от 31 Октября 2004, 17:44:45
    Макс
    Я посмотрел код твоего класса, очень много дало, только не понял некоторых вещей, а именно: DB::$conn - что это такое? Я не пойму назначение :: , ведь это вроде инструмент для обращения из потомков к функциям или переменным базовых классов, получается что ты из класса DB обращаешся же к классу DB... Или это уже из 5 серии заморочки? Объясни плиз. Я ненашёл что-то об этом инфы..
    Спасибо.
    Название: Помогите с ООП
    Отправлено: Алексей от 31 Октября 2004, 21:21:22
    class A{
      function B(){ echo "!";
      }
      function C(){
      A::B(); //напечатает !
      }
    }

    т.е. насколько я понимаю, :: служит не только для обращения из класов-потомков к главным классам, но и к функциям текущего класса? В мане так написано, что не очень понятно.. тогда почему ты Макс используешь такой подход: db::$var, а не $this->$var???
    Название: Помогите с ООП
    Отправлено: Макс от 01 Ноября 2004, 15:54:35
    оператор :: позволяет обращаться к методам без создания экземпляра класса (не важно где - в классе, в потомке или просто в коде).
    Поскольку объект не создается, то и переменная $this отсутствует.

    Работа с этим классом основана на статических переменных - это фича ПХП5
    http://www.php.net/manual/en/language.oop5.static.php
    http://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php
    Название: Помогите с ООП
    Отправлено: Алексей от 01 Ноября 2004, 15:56:06
    Спасибо.
    Название: Помогите с ООП
    Отправлено: Босc всех зон от 01 Ноября 2004, 21:30:46
    2 Макс

    Сам использую репозиторий объектов. Удобно.
    Но терзают какие-то непонятные сомнения насколько это правильно :)

    Мне кажется, читая код сложно понять где Аргегация, а где Композиция объектов должна быть. То-есть абсолютно не задумываемся как же нужный объект должен оказаться в классе: то-ли передавать как ссылку, то-ли создавать его в нём, а просто берём из репозитория и всё. И как бы теряется какой-то шаг проектирования.

    Ты UML диаграмки классов рисуешь?

    Цитировать
    Работа с этим классом основана на статических переменных - это фича ПХП5


    Но можно и на 4-ой версии сделать репозиторий (правда ущербный :) )
    Название: Помогите с ООП
    Отправлено: Макс от 01 Ноября 2004, 21:51:36
    Цитировать
    Босc всех зон:
    Ты UML диаграмки классов рисуешь?

    Пока что нет. Я программист-одиночка и не вижу смысла рисовать их для себя.

    Насчет правильности использования репозитария.
    1. Пока не вижу ничего в нем неправильного.
    2. Если это неправильно то отвечу фразой с  http://blogs.msdn.com/micahel/archive/2004/06/16/157202.aspx :
    Цитировать
    A great developer not only knows the canonical implementation but understands it is the canonical implementation.  A great developer can tell when the canonical implementation is not the best answer for a particular problem.


    Цитировать
    Босc всех зон:
    Но можно и на 4-ой версии сделать репозиторий (правда ущербный )

    там речь ишла о классе работы с БД.
    Название: Помогите с ООП
    Отправлено: Алексей от 02 Ноября 2004, 12:32:17
    Блин! И всё же я не пойму, где и когда лучше использовать ::, а где $this->var...
    Название: Помогите с ООП
    Отправлено: Меняздесьдавнонет от 02 Ноября 2004, 12:33:59
    почему-то мне кажется, что это не самая важная проблема, которая перед тобой стоит...

    используй $this->var
    Название: Помогите с ООП
    Отправлено: Алексей от 02 Ноября 2004, 12:49:41
    RomikChef
    просто я ООП начал усердно постигать, вот и хочется его постигнуть как можно глубже :)
    Название: Помогите с ООП
    Отправлено: Меняздесьдавнонет от 02 Ноября 2004, 13:00:26
    это не та глубина.
    сейчас ты решаешь, какой формы у тебя будут иллюминатры на корабле.
    при том, что сам корабль пока больше напоминает велосипед.

    Я доступно объясняю?
    Название: Помогите с ООП
    Отправлено: Алексей от 02 Ноября 2004, 13:25:11
    RomikChef
    доступно :)
    Название: Помогите с ООП
    Отправлено: Макс от 02 Ноября 2004, 15:02:28
    Алексей
    Допустим есть класс у которого есть конструктор и у которого есть какой-то метод.

    class A {
       function __construct() { ... }
        function someMethod () { ... }
    }

    Теперь есть 2 варианта вызова метода someMethod().
    1.
    $obj = new A();
    $obj->someMethod();


    2. A::someMethod();

    В первом случае будет выполнен конструктор, создан экземпляр объекта ($obj - он же $this, при обращении внутри класса) , и только потом выполнен someMethod()
    Во-втором случае будет выполнен только метод.

    Чаще используют первый вариант.
    Когда используют второй (из того что сейчас помню) :
     - иногда просто схожие по теме функции объединяют в класс (в нем даже конструктор не создают). Получается такое примитивное пространство имен (namespace). Пример http://pear.php.net/package/HTTP
     - Бывают случаи когда  есть какое-то часто используемое применение класса, Эдакая последовательность вызовов методов класса, которая часто используется в программах и на этом работа с классом завершается. Тогда в класс добавляют один статический метод, который внутри себя создает объект своего же класса и выполняет эту последовательность методов. Пример http://pear.php.net/package/HTTP_Download и его метод staticSend()
    Пример использования :
    http://pear.php.net/manual/en/package.http.http-download.intro.php (первый пример)
     - работа со статическими переменными. Пример мой класс. Я в ЖЖ объяснял, что такой подход был принят по одной причине - я хочу работать с БД везде (в функциях, методах других классов) прозрачно, без передачи экземпляра класса работы с бд. Что и позволяет такой подход (так как классы находятся в глобальном пространстве имен)
    Название: Помогите с ООП
    Отправлено: Алексей от 02 Ноября 2004, 19:43:54
    Макс
    большой Спасиб.
    Название: Помогите с ООП
    Отправлено: NAS от 03 Ноября 2004, 10:52:47
    Макс

    [OFF]А давай твой пост как статью оформим ?
    [/OFF]
    Название: Помогите с ООП
    Отправлено: fuza от 03 Ноября 2004, 13:29:43
    Люди!!! у меня тоже вопрос по php и точнее по ООП вот смотрите: у меня есть два поля в одном вводиться название фото, в другом, путь к нему, и вот при закачки фотки я ему пишу: echo $this->foto_name; и он мне ничего не выводит, в чем глюк???

    и второй вопрос, уже не по ооп. Как сделать что бы в ХР выводились парс ерроры и варнинги и т.п. дела?
    Название: Помогите с ООП
    Отправлено: Босc всех зон от 03 Ноября 2004, 15:03:47

    class Photo
    {
      var $name = \'name\';
      var $path = \'path\';
     
      function Photo() { /* Constructor */ }
    }

    $photo =& new Photo;
    var_dump($photo->path);


    Неужто не выводится?
    Есть такая вещь - отладка!
    Название: Помогите с ООП
    Отправлено: Макс от 03 Ноября 2004, 15:16:46
    Цитировать
    fuza:
    Как сделать что бы в ХР выводились парс ерроры и варнинги и т.п. дела?

    скорее всего в php.ini надо параметр display_errors включить
    или error_reporting = E_ALL сделать

    Цитировать
    NAS:
    А давай твой пост как статью оформим ?

    Я подумаю. Дело в том, что здесь нет никаких реальных ПХП-кодов (то что я привел, это упрощенные варианты). Причем у меня пока нет желания публиковать свои классы (к тому же в при использовании этих в реальных проектах я иногда сталкиваюсь с некоторыми неудобствами). Если изложить в статье просто идеи, а реализацию возложить на читателей - то можно.
    Есть идеи что добавить, объяснить подробнее ?
    Название: Помогите с ООП
    Отправлено: NAS от 03 Ноября 2004, 15:19:17
    Макс Именно идеи, а реализацию оставить на читателя, потом при желании можно будет сделать вторую часть с реализацией.
    Название: Помогите с ООП
    Отправлено: Макс от 03 Ноября 2004, 18:16:36
    NAS, напомни мне это на следующей неделе.
    Если в армию не заберут, то начну делать :)
    Название: Помогите с ООП
    Отправлено: Алексей от 09 Ноября 2004, 16:49:23
    Вообщем, че то написал, не знаю, правильно или нет. Хочется вашего мнения.

    Создал 3 класса - главный класс DB, выполняющий некоторые операции с БД, класс гостевой книги GB, выполняющий лишь обработку ПОСТ-массива от слешей и пробелов и проверку на допустимость пользовательского ввода, и класс "Отстраничиватель":

    Многие идеи брал из класса Макса, но у него там всё сложнее намного.. Вот что получилось:

    //Main class DB
    class DB{
    private $lnk, $result, $server, $username, $password, $db;

    //Устанавливаем коннект, выбираем БД
    private function connect(){
    if(is_resource($this->lnk)) return true;
    //тут определяем парольи и имя сервера - для локалхоста и хостинга...........

    $this->lnk = mysql_connect($this->server, $this->username, $this->password)
    or die("Не могу соедениться c MySql");
    mysql_select_db($this->db, $this->lnk) or die("Не могу выбрать базу $db");
    }

    //КОНСТРУКТОР
    function __construct(){
    $this->connect();
    }

    //ДЕСТРУКТОР
    function __destruct(){
    @mysql_close($this->lnk);
    }

    //Выполняет Sql запрос
    //результатом будет либо возвращённое булево значение, либо ресурс в области видимости класса
    function query($query){
    $what = mysql_query($query, $this->lnk) or die("Не могу сделать SQL-запрос ($query)");
    if(is_resource($what)) $this->result = $what;
    else return $what;
    }


    // "Универсальная" функция запросов.
    //Принимает строку $query, которая является запросом и флаг $flag
    //нужно использовать флаг, который может иметь 2 параметра:
    // ROW && ASSOC. Флаги указывают, как возвращать результат:
    //ROW: Как массив, где каждый элемент массива - массив данных - одна запись из таблицы
    //ASSOC: Ккак массив, где каждый элемент массива - массив содержащий перечисленный массив
    //данных одного типа
    //В случаях ошибки возвращает false
    public function get_rezar($query, $flag=false){
    $this->connect();
    $this->query($query);

    if(!$flag || !is_resource($this->result)) return false;
    if(!mysql_num_rows($this->result)) return false;

    //Возвращает как массив, где каждый элемент массива -
    //массив данных - одна запись из таблицы
    if($flag=="ROW"){
    while($temp = mysql_fetch_row($this->result))
    $array[] = $temp;
    return $array;
    //Возвращает как массив, где каждый элемент массива -
    //массив содержащий перечисленный массив данных одного типа
    } else if ($flag=="ASSOC"){
    while($temp = mysql_fetch_assoc($this->result)){
    foreach($temp as $key=>$value)
    $array[$key][] = $value;
    }
    return $array;
    } else return false;
    }

    //Получает простое единственное значение (не массив) из запроса
    public function get_one($query){
    $this->connect();
    $this->query($query);

    if(!is_resource($this->result) || !mysql_num_rows($this->result))
    return false;

    $row = mysql_fetch_row($this->result);
            return $row[0];
    }

    //получает количество рядов в результате
    public function db_num_rows(){
    return mysql_num_rows($this->result);
    }
    }


    //Класс ГОСТЕВАЯ КНИГА
    class gb extends DB{
    public $err;
    //Очищает POST - убивает пробелы и магические кавычки
    function clearPost(){
    foreach($_POST as $key=>$value)//clearPost-a код не привожу....
    $_POST[$key]=clearPost($value);
    }

    //Метод проверяет введённые POST-данные, и если они верны, то делает выполняет запрос $query
    //Если всё ок, возвращает true, наче - false
    function add($query){
    global $user_is_login, $ml_user_message;
    if(!$user_is_login)
    $this->err[] = "
  • Вы не являетесь зарегестрированным пользователем.
    Вероятно, Вы начинающий кулхацкер? Хе-хе...
  • ";
    if(!$_POST["user_message"])
    $this->err[] = "
  • Вы не ввели сообщение.
  • ";
    else if($t = bad_max_length($_POST["user_message"],$ml_user_message))
    $this->err[] = "
  • Введённое Вами сообщение слишком велико. Удалите $t символов.
  • ";
    if(is_referer())
    $this->err[] = "
  • Форма, с которой отправляется запрос, должна находиться на сервере.
  • ";

    if(count($this->err)) return false;
    else return $this->query($query);
    }
    }


    //Класс "отстраничиватель"
    class Pages{
    private $startLimit, $stopLimit;

    //Принимает число, символизирующее количество записей, которые нужно вывести
    function __construct($in){
    $this->stopLimit = $in;
    }

    //Возвращает стартовый лимит для SQL-оператора LIMIT
    //(конечный предел объявляется при инициализации класса).
    public function get_start_limit(){
    $_GET["page"] = !isset($_GET["page"]) ? 1 : $_GET["page"];
    if(!$_GET["page"] = intval($_GET["page"])) return 1;
    return ($_GET["page"]-1) * $this->stopLimit;
    }

    //Возвращает верхний предел для цикла...
    public function get_fornum($count){
    return ceil($count/$this->stopLimit);
    }
    }

    А вот гостбух:

    $myGB = new gb;


    if($_SERVER["REQUEST_METHOD"]=="POST")
    {
    //Очищаем ПОСТ от пробелов и слешей, если магические кавычки включены
    $myGB->clearPost();

    //Если все ПОСТ-значения верны, т.е. нет ошибок, то выполняем этот запрос
    if($myGB->add("INSERT INTO gb VALUES(NULL, $_COOKIE[user_id], \'".mysql_escape_string($_POST["user_message"])."\', NOW(), \'\', 0)")){
    //и сразу - релоад на себя
    header("Location: ./?".time()); exit;
    }
    //Тут ошибки - красиво оформлем:
    else $err = "
    Ошибка!
      ".implode("",$myGB->err)."
    ";
    } $_POST["user_message"] = "";



    //инициализируем класс "Отстраничиватель"
    $myPages = new Pages($gb_limit);
    //Получаем стартовый лимит для SQL-запроса, конечный предел - $gb_limit
    $start_limit = $myPages->get_start_limit();

    //Получаем общее кол-во записей в "гостевой"
    $count = $myGB->get_one("SELECT COUNT(*) FROM gb");

    $fornum[1] = $myPages->get_fornum($count);

    //В массив DHTML заношу значения - записи в таблице гостевой книги
    $DHTML = $myGB->get_rezar("SELECT gb.user_id, user_name, user_message, date_add,
    WEEKDAY(date_add) as weekday, DAYOFMONTH(date_add) as day, MONTH(date_add) as month, YEAR(date_add) as year, DATE_FORMAT(date_add, \'%H:%i:%s\') as time, user_active
    FROM gb, user_data, user_main
    WHERE  gb.user_id=user_data.user_id AND gb.user_id=user_main.user_id
    ORDER BY gb.message_id desc LIMIT $start_limit, $gb_limit", "ASSOC");

    //Количество рядов в результате
    $fornum[0] = $myGB->db_num_rows();

    //Далее всё это (DHTML-ассив) выводим
    Название: Помогите с ООП
    Отправлено: Макс от 09 Ноября 2004, 18:48:10
    ты писал для ПХП 5 ?

    По классам особо придираться не могу. По мелочам :
    1. код
    $this->connect(); во всех методах кроме конструктора можно убрать.

    2. Классы желательно делать как можно более автономными от среды использования, поэтому желательно не использовать global-ы и самописные функции (эти самописные функции лучше определять в виде методов класса)

    3. ИМХО то что класс gb наследуется от класса DB не совсем логично. То есть более правильной я считаю такую реализацию :

    class GB {
    ......
        private $db = null;
       function __construct($db) {
             $this->db = $db;
       }
       // и внутри класса GB работать с базой
       /// $this->db->query(\'SELECT ...\');
    }
    // а пример использования
    $db = new DB();
    $gb = new GB($db);
    // дальше работаешь с классом gb
     

    4. ИМХО формирование INSERT-запроса на лету - более красивое решение, чем напрямую написать его в коде.
    На крайний случай, можно просто в методе gb::add() в его коде прописать этот запрос. Или у тебя в метод add() могут попадать разные INSERT-запросы ?

    4. зачем в Pages  $startLimit ? Вообще можно пофантазировать и сделать этот класс более функциональным
    Название: Помогите с ООП
    Отправлено: Алексей от 10 Ноября 2004, 11:37:31
    Ой, Максим, большое тебе спасаибо!

    Только вот вопросы возникли на твои ответы:

    2) Понятно. А если нужно например какие-то параметры для функции указать, например 3 целых числа? В классе их объявлять? Просто я думал, что когда все параметры в конфигурационном файле хранятся, их удобнее отслеживать и изменять...

    3) хм.. интересно.. как этот приём называется? и в чем отличие от наследования? Не вижу принципиальной разницы, если честно...

    4) А как это, на "лету"?
    Цитировать
    Или у тебя в метод add() могут попадать разные INSERT-запросы ?

    нет

    5)
    Цитировать
    зачем в Pages $startLimit ?


    ...LIMIT $start_limit, $gb_limit...
    Название: Помогите с ООП
    Отправлено: Макс от 11 Ноября 2004, 17:17:31
    2. я в одном из предыдущих постингов писал, что  единственным "глобальным" объектом в системе у меня является Регистр Объектов (и то, он не через global попадает в функции а через параметры).
    Я просто создал класс Settings (настройки), который доступен через регистр объектов.
    В общем, так как написал ты, тоже иногда пишут, но я считаю это плохим стилем.

    3. композиция. Мне сложно написать чем отличается композиция от наследования. Что использовать композицию или наследование - этот вопрос поднимается часто, и об этом часто пишут в книгах

    4. у меня в классе (см код в ЖЖ) есть метод insert - который генерирует INSERT-запрос и выполняет его. Правда я не использую встроенных mysql-функций (типа now()). С ними он работать не будет
    Название: Помогите с ООП
    Отправлено: Алексей от 11 Ноября 2004, 17:27:22
    Макс
    Ну понятно... вообщем, спасибо тебе большое за все эти ответы!!!