Форум программистов, компьютерный форум, киберфорум
PHP: ООП
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.89/37: Рейтинг темы: голосов - 37, средняя оценка - 4.89
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522

Объект класса с подключением PDO

11.12.2013, 12:57. Показов 7556. Ответов 43
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть класс для подключения с БД (используется PDO). В этом же классе есть основные ф-ии для работы с БД.
Подключаюсь нормально.
Теперь при попытке обратиться к методу этого класса, у меня выходит ошибка.

Code
1
Fatal error: Call to undefined method PDO::Select() in
получается, код
PHP
1
$this->db = Pi_DB::Instance(); // Действие происходит в какой-то модели
возвращает объект класса PDO, а не класса Pi_DB. Не понимаю почему.

Вот класс
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Pi_DB
 {
    private static $instance = null;
 
    public static function Instance()
    {
        if (!self::$instance) {
           try {
              self::$instance = new PDO("mysql:host=".PI_HOSTNAME.";dbname=".PI_DBNAME."", PI_USERNAME, PI_PASSWORD /* ,
                                         array( PDO::ATTR_PERSISTENT => true ) постоянное соединение */);
              self::$instance->exec('SET NAMES utf8'); 
              self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // ERRMODE_SILENT  |  ERRMODE_WARNING
           } catch (PDOException $e) {
              self::PDO_Error('Ошибка подключения к БД!', $e->getMessage(), $e->getLine());
           }
        }
        return self::$instance;
    }
 
 
    public function Select($sql, $mode = 'FETCH_ASSOC', $id = null, $in_params = array())
    {
       $pdo = self::$instance;
       try {
         $result = $pdo->prepare($sql);
         $result->execute($in_params);
       
         $rows = $result->fetchAll(PDO::$mode);
         
       } catch (PDOException $e) {
         self::PDO_Error('Выборка не удалась!', $e->getMessage(), $e->getLine());
       }
 
       $select = array();
       
       if ($id) {
          $select[$id] = $rows;
       } else {
          $select = $rows;
       }
       
       return $select;
    }
 
 
 
}
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
11.12.2013, 12:57
Ответы с готовыми решениями:

Расширение класса PDO
Здравствуйте, я пытаюсь расширить класс PDO и столкнулся с такой проблемой: class myPDO extends PDO { function __construct () { ...

Выполнение запросов PDO внутри класса
Здравствуйте. Недавно начал практиковаться писать сайты на PHP. Столкнулся с проблемой: не могу выполнить запросы к базе внутри класса....

Чем отличается объект класса от экземпляра класса
Читаю книгу "PHP Объекты, шаблоны и методики программирования" автор Мэт Зандстра. Перевод на русском. Там есть абзац про статические...

43
Иллюзионист
 Аватар для philin
154 / 153 / 27
Регистрация: 02.10.2013
Сообщений: 330
12.12.2013, 20:40
Студворк — интернет-сервис помощи студентам
Автолоад. Хм. Никогда не пользовался. Судя по описанию, он подгружает только классы, чье имя соответствует имени файла. Т.е. из model.php он найдет только class Model. Я неправ?
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6725 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
12.12.2013, 21:45
Цитата Сообщение от philin Посмотреть сообщение
Я неправ?
и да, и нет.. Смотря как код написать.. Но вообще, все достаточно подробно расписано на офф сайте http://php.net/manual/ru/langu... toload.php
2
Иллюзионист
 Аватар для philin
154 / 153 / 27
Регистрация: 02.10.2013
Сообщений: 330
12.12.2013, 21:54
Удобная вещь autoload, разобрался. Спасибо. Всем
И нет, я неправ. Почитал, потестил. Это просто обертка над include-require, исполняет весь файл.
И, кстати, кажется, нашел проблему. У меня-то PHP 5.3. Общие изменения относительно статических свойств. Как раз с 5.3. Хотя и он уже устаревает, 5.5 нужно брать)
1
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
12.12.2013, 22:00  [ТС]
Цитата Сообщение от philin Посмотреть сообщение
И, кстати, кажется, нашел проблему. У меня-то PHP 5.3.
может быть.., не можешь класс Pi_DB представить для вида 5.2.x?
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6725 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
12.12.2013, 22:06
все лень перечитывать, но..
Ошибка в том, что синглтон должен быть проинициализирован. Т.е., в любом случае, объект должен быть создан, просто создать возможно только один объект. Поэтому надо внутри либо использовать
PHP
1
self::Instance()
вместо обращения к пустому (еще пустому) статическому свойству, либо выполнять проверку, что объект уже был создан, а если нет - создавать. В общем, проще всего - воспользоваться заменой прямого обращения к свойству на обращение к методу-инициализатору
0
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
12.12.2013, 22:17  [ТС]
KOPOJI, если я правильно тя понял, внутри класса Pi_DB для получения экземпляра этого же класса, нужно писать self::Instance() ?
сделал
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Pi_DB
 {
    private static $instance = null;
 
    public static function Instance()
    {
        if (!self::$instance) {
           try {
              self::$instance = new PDO("mysql:host=".PI_HOSTNAME.";dbname=".PI_DBNAME."", PI_USERNAME, PI_PASSWORD /* ,
                                         array( PDO::ATTR_PERSISTENT => true ) постоянное соединение */);
              self::$instance->exec('SET NAMES utf8'); 
              self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // ERRMODE_SILENT  |  ERRMODE_WARNING
           } catch (PDOException $e) {
              self::PDO_Error('Ошибка подключения к БД!', $e->getMessage(), $e->getLine());
           }
        }
        return self::$instance;
    }
 
 
   public function Select($sql, $mode = PDO::FETCH_ASSOC, $id = null, $in_params = array())
   {
       $pdo = self::Instance();
    
       try {
         $result = $pdo->prepare($sql);
 
   // .......
}
вроде заработало, но по-моему скорость загрузки страниц чуть снизилась и на 2 строки в index.php
PHP
1
2
$d = new Pi_DB();
$b = new Pi_DB();
код не ругается. ведь это же синглтон, нет?
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6725 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
12.12.2013, 22:19
Цитата Сообщение от Love_and_Peace Посмотреть сообщение
нужно писать self::Instance() ?
да.
Цитата Сообщение от Love_and_Peace Посмотреть сообщение
код не ругается. ведь это же синглтон, нет?
нет. Конструктор, а также некоторые другие магические методы (если память не изменяет, то __sleep(), __walk() и __clone(), может, еще что запамятовал) должны быть закрытыми. Обращение снаружи должно осуществляться через тот же метод.
0
Иллюзионист
 Аватар для philin
154 / 153 / 27
Регистрация: 02.10.2013
Сообщений: 330
12.12.2013, 22:19
Зоопарк... 5.3, 5.4, 5.5. Теперь еще и 5.2.
Да, выловил твою ошибку. Pi_DB нельзя передать, оставив синглтоном. Обращаться только через имя класса.
Т.е. не прокатит
PHP
1
2
$db = Pi_DB2::init();
$db::method1();
Нужно
PHP
1
2
Pi_DB2::init();
Pi_DB2::method1();
И можно убрать return __CLASS__

Повторяешь ту же ошибку! в self::instance ты заносишь экземпляр класса PDO. Это не ссылка на сам Pi_DB
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6725 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
12.12.2013, 22:21
Цитата Сообщение от philin Посмотреть сообщение
не прокатит
можно заюзать костыль, например, с помощью call_user_func.. А вообще, методы можно делать и не статическими, ведь Instance() возвращает созданный объект, которому доступны точно те же методы, что и объекту, созданному через обычное "new"
0
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
12.12.2013, 22:53  [ТС]
philin, не получается
здесь
PHP
1
 $result = self::$_pdo->prepare($sql);
снова поломка
а так ч/з self::Instance() вроде работает, но мне нужен синглтон!!!
Ребята представьте мне окончательный вариант подключения к БД с помощью PDO синглтон. полностью запутался
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6725 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
12.12.2013, 23:22
Цитата Сообщение от Love_and_Peace Посмотреть сообщение
а так ч/з self::Instance() вроде работает, но мне нужен синглтон!!!
эмм.. Вы лучше объясните, что в вашем понимании "синглтон", раз вас не устраивает такой вариант.
0
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
12.12.2013, 23:31  [ТС]
Цитата Сообщение от KOPOJI Посмотреть сообщение
эмм.. Вы лучше объясните, что в вашем понимании "синглтон"
в моем понимании - не возможность создания более одного экземпляра. а у меня при вызове класса 2 раза в index.php, скрипт спокойно это проглатывает.
нужен одиночка, просто покажите как правильно
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6725 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
12.12.2013, 23:34
я же уже сказал. Нужно закрыть все методы, через которые можно так или иначе создать объект. __construct, __clone, __wakeup, __sleep должны быть закрытыми. Объект создаете с помощью статического метода. Все
0
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
13.12.2013, 00:38  [ТС]
так, мужики, представляю на ваш суд окончательный вид класса и его вызова в рабочем варианте.
Скажите, это правильная реализация синглтона?

Вот класс
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<?php
 
 class Pi_DB
 {
    private static $instance = null;
 
    public static function Instance() {
        if (self::$instance == null)
            self::$instance = new Pi_DB();
        return self::$instance;
    }
    
    // 
    private function __construct() {
        try {
          $this->_pdo = new PDO("mysql:host=".PI_HOSTNAME.";dbname=".PI_DBNAME."", PI_USERNAME, PI_PASSWORD /* ,
                                     array( PDO::ATTR_PERSISTENT => true ) постоянное соединение */);
          $this->_pdo->exec('SET NAMES utf8'); 
          $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // ERRMODE_SILENT  |  ERRMODE_WARNING
       } catch (PDOException $e) {
          self::PDO_Error('Ошибка подключения к БД!', $e->getMessage(), $e->getLine());
       }
    }
    
    
    // запрещаем клонирование
    private function __clone() {}
    // Защищаем от создания через unserialize
    private function __wakeup() {}
    // Не спать!
    private function __sleep() {}
    
    
   /**
   *  Выборка строк
   *  @param string $slq - полный текст sql-запроса
   *  @param string $mode - тип выборки (нумерованный, ассоциативный (по умолчанию), объект, FETCH_CLASS)
   *  @param string $id - название id таблицы (в качестве ключа возвращаемого массива)
   *  @param array $in_params - массив возможных входящих значений
   *  return Array - массив строк
  */ 
   
    public function Select($sql, $mode = PDO::FETCH_ASSOC, $id = null, $in_params = array())
    {
       try {
         $result = $this->_pdo->prepare($sql);
         $result->execute($in_params);
       
         $rows = $result->fetchAll($mode);
         
       } catch (PDOException $e) {
         self::PDO_Error('Ошибка подключения к БД!', $e->getMessage(), $e->getLine());
       }
 
       $select = array();
       
       if ($id) {
          $select[$id] = $rows;
       } else {
          $select = $rows;
       }
       
       return $select;
    }
    
    
    
   /**
    *  Ошибка при работе с БД
    *  @param string $err - Сообщение об ошибке
    *  @param string $err_message - Содержимое ошибки
    *  @param string $err_line - Строка, на которой обнаружена ошибка
    *  return string & append to logs file
    */
    
    public function PDO_Error($err, $err_message, $err_line = null) {
        echo $err;
        file_put_contents(PI_DB_LOGSPATH, 
                          date('d.m.Y H:i:s') . ' | ' . $err_message . ' | ' . __FILE__ . ' | Строка ' . $err_line . PHP_EOL, FILE_APPEND);
        
    }
    
 }
и его применение - модель получающая настройки сайта из БД
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
 
 class Models_Settings
 {
    private $_settings = array();
    private $db;
    
    public function __construct() {
        $this->db = Pi_DB::Instance();
    }
 
    public function Pi_Settiings() 
    {
       $sql = "SELECT `options`, `value` FROM `".PI_DB_PREFIX."settings` WHERE `active` = 1";
       $settings = $this->db->Select($sql);
       
       foreach ($settings as $v) {
           $this->_settings[$v['options']] = $v['value'];
       }
       return $this->_settings;
    }
 
 }
Так работает, но при попытке создания в index.php экземпляра класса Pi_DB
PHP
1
$b = Pi_DB::Instance();
почему-то не выдает ошибку, которую я ожидаю(( неужели снова не синглтон?

Добавлено через 5 минут
код philin'a, я так и не смог настроить(
philin, если у тя работает, скинь обратно архив, посмотрю реализацию
0
Почетный модератор
Эксперт HTML/CSSЭксперт PHP
 Аватар для KOPOJI
16844 / 6725 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
13.12.2013, 07:55
Цитата Сообщение от Love_and_Peace Посмотреть сообщение
почему-то не выдает ошибку, которую я ожидаю((
а какую ошибку он должен выдать (чего вы ожидаете?)

Добавлено через 51 секунду
Цитата Сообщение от Love_and_Peace Посмотреть сообщение
это правильная реализация синглтона?
реализация - более менее правильная..
0
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
13.12.2013, 11:31  [ТС]
Цитата Сообщение от KOPOJI Посмотреть сообщение
а какую ошибку он должен выдать (чего вы ожидаете?)
а.., я все понял, не должно быть ошибки)
Цитата Сообщение от KOPOJI Посмотреть сообщение
реализация - более менее правильная..
чего не хватает до хорошей реализации? что бы ты исправил?
0
91 / 91 / 23
Регистрация: 02.09.2013
Сообщений: 276
13.12.2013, 12:06
PHP
1
2
3
4
5
6
    public static function Instance() {
        if (self::$instance == null)
            //self::$instance = new Pi_DB();
            self::$instance = new self();
        return self::$instance;
    }
Я бы так сделал. Чтобы при смены названия класса и бла бла бла, ну вы поняли

А константы PI_HOSTNAME и пр. где определены?

Вобщем я занимался чем-то подобным и совсем не давно. Сделал следующее:
Конфигурационный yml файл, в котором в том числе была конфигурация подключения к базе данных. Чтобы была возможность динамического свитчинга баз данных/хостов/пользователей (чего у вас не получится) сделал алиасы соединений, каждый из которых содержал в себе набор host/username/password/dbname и еще всякие настройки.
Я все это к чему, у вас получается только одно соединение. КОнечно, в большинстве случаев этого достаточно, но не всегда. Если вы хотите сделать универсальную штуку, то наверное стоит вынести из __construct() подключение в отдельный метод типа connect(), завести аттрибут - менеджер соединений (а в идеале отдельный класс, только тогда он будет синглтоном, а не Pi_Db) и в каком-нибудь виде конфигурации.
Как это было сделано у меня (примерно):
Класс контейнер для активных соединений, ConnectionManager(). КЛасс - синглтон.
КЛасс самого соединения Connection(). Создается через спциальный метод в классе ConnectionManager()
В конструкторе Connection() прием данных для подключения username/password/database etc
В конечном итоге выглядело примерно вот так:
PHP
1
2
3
4
5
6
// подразумевается, что ConnectionManager() уже инициализирован при загрузке приложения
ConnectionManager::getInstance()->registerConnection(App::config['my_connection_alias']);
$connection1 = ConnectionManager::getActiveConnection(); // получаем экземпляр класса Connection();
$connection1->select('blablabla');
$connection2 = ConnectionManager::switchConnection(App::config['my_connection_alias2'])
// .......
А потом еще сделал отдельный менеджер запросов типа QueryBuilder();
Вобщем тут широкие просторы для воображения
1
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
13.12.2013, 12:56  [ТС]
Цитата Сообщение от Zvook Посмотреть сообщение
А константы PI_HOSTNAME и пр. где определены?
они в config.php, если интересно можешь посмотреть в архиве (прикреплен к теме).
Цитата Сообщение от Zvook Посмотреть сообщение
Я все это к чему, у вас получается только одно соединение.
да, думал сначала так сделать, но конечно подумываю об универсальном решении. спасибо за способ решения этого момента.
Не мог бы ты написать зависимости твоих классов (имеется в виду - классов БД) схематически, примерно так
Code
1
2
3
Connection() // главный файл (синглтон / или нет) 
ConnectionManager() // 2-ой уровень 
QueryBuilder() // то же че-то делает
и кто за что отвечает
0
91 / 91 / 23
Регистрация: 02.09.2013
Сообщений: 276
13.12.2013, 13:49
Щас попробую вспомнить ... исходники гдето дома валяются
PHP
1
2
3
4
App() // Синглтон. Головной класс всего приложения, хранит в себе фактически все, с чем работает система
Configuration() // Синглтон. Класс конфигурации в который при инициализации парсятся настройки из yml файла
ConnectionManager() // Синглтон. Головной класс управления соединениями с базой(ми) данных. Нечто вроде IoC
Connection() // Класс отвечающий за одно конкретное соединение
Насчет Query() тут палка о двух концах. Если следовать MVC парадигме, то по идее нужно иметь что-то типа базовой модели, скажем BaseModel(). Это абстрактный класс в котором определены и частично реализованы методы обработки данных модели.
Сам Query() это класс наподобие Criteria() в симфони (первой) и CdbCriteria() в уии. В двух словах это конструктор для сборки запросов, то есть имеет методы, скажем, ->oder(); ->group(); ->limit(); ->join(); и т. д.
А вот как лучше прикрутить этот конструктор к модели это другой вопрос. Но это, впрочем, уже другая тема.
1
 Аватар для __PION__
960 / 801 / 85
Регистрация: 21.07.2010
Сообщений: 3,522
13.12.2013, 18:11  [ТС]
люблю развернутые ответы
Цитата Сообщение от Zvook Посмотреть сообщение
Головной класс всего приложения, хранит в себе фактически все, с чем работает система
например можешь привести, ну кроме БД, еще как понимаю там должно быть управление файлами, ... хочу знать, что еще туда вложил

В Configuration() находятся настройки только БД?

Я как понимаю, у меня класс Pi_DB включает в себя 2 последних класса у тя в списке, так?

Еще, я чиатл, что чем меньше классов, тем лучше, а то, что для подготовки системы нужно мин. 4 класса - это разве не борщ?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
13.12.2013, 18:11

Свойству класса присвоить объект другого класса
Добрый день форумчане. Подскажите, пожалуйста. Свойствам класса можно при описании присваивать скалярные значения или массив. А как можно...

Объект класса
Содержимое файла test.php: &lt;?php namespace Test; class BlogManager { public function get() { return...

Возвращать объект класса
Здравствуйте, форумчане! Первый раз реализовываю такую структуру, требуется Ваша помощь :) Необходимо реализовать класс фабрику,...

Не передаются значения в объект класса
Здравствуйте. Я только начинаю изучать PHP и ООП и решил сделать простейшую форму регистрации и авторизации... Но у меня почему-то никак...

Получить объект класса по имени
Получается какая проблема, работаю с loader, который подключает файлы и объявляет классы. К примеру есть файл test.php ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
GitDOS: Когда я решил скрестить MS-DOS, GitHub и Claude
MaGz GoLd 08.07.2026
Привет, Киберфорум! Хочу поделиться своим проектом, который, надеюсь, вас заинтересует. GitDOS — это веб-приложение, которое позволяет запускать DOS в браузере с тремя крутыми фичами: диски живут в. . .
Учебный мини "цифровой двойник" = (Сервер = Java + Flask_питон )+(3D = Гугловскаая 4T nvideo + google colab) +( WMS = Odoo на Docker)
anaschu 07.07.2026
Увидел в требованиях к вакансии создателя имитационных моделей связку (REst + Api + WMS + Anylogic + питон + 3D Nvideo), и решил посмотреть, что это такое. Из этого всего я работал более менее. . .
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
Doom для терминала без стрельбы и монстров. 3D Raycasting на ascii.
dcc0 05.07.2026
Попросил нейронную сеть deepai. org написать рейкастинг 3D с библиотекой ncurses для Linux. Чтобы можно было ходить на стрелочки. Чтобы стены были отрисованы символами. Справилась. Первый вариант. . .
Установка статуса документа по условию
Maks 05.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "НарядПутевка" разработанного в КА2. Задача: в табличной части "Материалы" документа при записи автоматически устанавливать статус. . .
Сезонность и суточность закисления почв
anaschu 04.07.2026
200 часов это все равно моловато. Есть ситуации, но нестандартные, когда смена происходит за 5 лет. Но обычно это 50 лет и более. Наверное, закисление почвы происходит сезонно в средней. . .
В чем ценность человеческого опыта в глобальном смысле?
kumehtar 03.07.2026
Возможно, ценность человека не в том, что он однажды достигает мудрости, а в том, что он становится носителем карты пути. Он знает не только истину, но и последовательность внутренних изменений,. . .
интеграция AnyLogic с самописным REST API и переход на Odoo
anaschu 03.07.2026
Успешная интеграция AnyLogic с самописным REST API и переход на промышленную Odoo WMS Сегодня проделал огромный путь от простой симуляции физических процессов до построения полноценной. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru