Карта сайта Хакер в RSS Энциклопедия Хакера PDA версия сайта Почтовые рассылки Хакера    Хакер в Twitter Хакер в ВКонтакте Приложение Хакер для Facebook Хакер на Formspring.me
Журнал Новости Форум Видео Life Xakep Live (блоги)
Bugtrack Статьи Блог Поиск English
Взлом как образ мысли Взлом как образ мысли
Интервью с человеком, который, как оказалось, является не только талантливым пентестером в одной из крупных ИБ-компании, но и хакером-ветераном, который уверенными шагами вышел на свет и прикоснулся к истории российской хак-сцены....
Полный гид по накрутке онлайн-голосований Полный гид по накрутке онлайн-голосований
Конкурсы с голосованием привлекают большое количество посетителей, а трафик, как известно, — это деньги. Особый интерес вызывают конкурсы, где за победу предлагаются лакомые призы....

Архитектура доступа к данным в PHP

Bookmark and Share

Современные системы бизнес уровня, а также информационные сайты и блоги редко обходятся без использования СУРБД, реже других источников данных. Наиболее удобная реализация взаимодействия бизнес логики, источника данных и представления зависит от планирования архитектуры приложения. Объектно-ориентированное программирование позволяет реализовать вашу систему так, что отдельные модули можно использовать в других системах. В данной статье мы реализуем архитектуру взаимодействия с источником данных. И назовем этот модуль Data Source Object.

Дело в том, что источников данных может быть несколько в одном приложении, что затрудняет ее структуру. Хотелось бы иметь некий абстрактный интерфейс для работы с источником данных, т.е. желательно использовать полиморфные классы для сокрытия реализации подключения к источнику, извлечения и изменения данных. Полиморфизмом называется возможность применения методов с одинаковыми именами в группе классов, связанных наследованием. Применяя подобные классы, в связке с паттерном Фабрика, который позволяет манипулировать различными вариациями в зависимости от необходимого случая, работа с данными становится намного проще. Давай рассмотрим два случая, когда данные хранятся в двух различных СУРБД, MySQL и MSSQL.

Первым делом мы реализуем класс, моделирующий данные для подключения к источнику данных.

<?
class Db
{
	private $user;
	private $password;
	private $host;
	private $name;
	
	public function __construct($user, $password, $host, $name)
	{
		$this->user = $user;
		$this->password = $password;
		$this->host = $host;
		$this->name = $name;
	}
	
	public function getUser()
	{
		return $this->user;
	}
	public function getPassword()
	{
		return $this->password;
	}
	public function getHost()
	{
		return $this->host;
	}
	public function getName()
	{
		return $this->name;
	}
}
?>

Действительно, php функции подключения к СУРБД имеют одинаковый интерфейс. Класс представляет необходимые параметры в объектном виде. Члены класса можно объявить как public, это дело вкуса, но я привык именно так работать :). Так как мы только моделируем функциональность, мы воспользуемся абстрактными классами. Если в классе объявлены абстрактные методы, то и класс тоже должен быть объявлен как абстрактный. Абстрактный класс может содержать и обычные методы и поля. Нельзя создавать объект абстрактного класса, но его нужно переопределять классами потомками. Т.е. абстрактные классы служат прототипами классов, которые наследуют их основу. Мы безоговорочно должны принять правила, диктуемые нам абстрактными классами. Если мы объявили абстрактный метод с двумя параметрами, то в последующих классах мы должны реализовать одноименный метод именно с тем же числом параметров.

<?
abstract class Connection
{
	protected $db;
	protected $connection;
	
	final public function __construct($db)
	{
		$this->db = $db;
		$this->Open();
	}

	abstract protected function Open();
	
	final public function getConnection()
	{
		return $this->connection;
	}
}
?>

В данном случае мы видим абстрактный класс, моделирующий подключение уже для конкретной СУРБД, т.е. класс, реализующий подключение к MySQL будет наследоваться от данного класса. Класс имеет два protected члена - $db и $connection. Первый – это объект типа Db из предыдущего листинга, второй - указатель на соединение, необходимый для выполнения запросов. В конструкторе класса реализуется инициализация объекта типа Db, а также вызывается метод, открывающий соединение с источником данных. Метод getConnection() соответственно открывает доступ извне к указателю на соединение. Давай рассмотрим реализацию подключения к БД на примере MySQL. Для других источников данных архитектура окажется подобной.

<?
class MySQLConnection extends Connection
{
	protected function Open()
	{
		 $host = $this->db->getHost();
		 $pas = $this->db->getPassword();
		 $user = $this->db->getUser();
		 $name = $this->db->getName();
		 
		 if(!$this->connection = @mysql_pconnect($host, $user, $pas)) {
			$this->Errors[]="Не удается установить подключение";
			return FALSE;
		}
		 if(! @mysql_select_db($name)) {
        			$this->Errors[]="Не удается выбрать базу данных";\
			return FALSE;
		}
	}
}
?>

Как видно из листинга, класс MySQLConnection является потомком класса Connection, т.е. расширяет предыдущий класс до нового путём расширения функциональности, т.е. новый класс будет содержать те же методы (которые, кстати, могут и изменить свою реализацию) и свойства прежнего класса. Также видно, что класс реализует метод подключения к источнику. Количество строк можно сократить, но написано именно так для наглядности :). Очевидно, что для других СУРБД реализация будет схожей.

Настало время подумать над тем, как мы будем взаимодействовать с данными. И так, мы должны открыть соединение с СУРБД, выполнить запрос, если запрос на извлечение данных, представить их в удобном виде. Например, массив данных удобнее представить в массиве и т.д.

Идея состоит в том, что ниже реализованные методы функциональны в рамках одного SQL запроса, т.е. результат, возвращаемый ими, будет зависеть от самого запроса.

<?
abstract class DataAccess
{
	protected $db;
	protected $connection;
	protected $result;
	protected $Errors;
	
	final public function __construct($db)
	{
		$this->db = $db;
	}
	
	abstract public function Query($sql);
	abstract public function db2Array();
	abstract public function dbCol2Array($num);
	abstract public function dbRow2Array($num);
	abstract public function db2Var($row, $col);
	abstract public function dbNumRows();
	
	final public function getErrors() 
	{ 
		return $this->Errors; 
	}	
}
?>

Данные класса состоят из protected членов $db, объект типа DataBase; $connection представляет указатель на соединение; $result, является результатом запроса; $Errors – массив ошибок, возникших в процессе работы.

Функциональность представим следующими абстрактными методами.

  • Query($sql) – входной параметр данного метода есть SQL запрос. Сам же метод подключается к источнику данных, выполняет запрос, инициализирует результат выполнения запроса. Если запрос выполнился удачно, возвращаем истину, если нет, то кричим, что ложь :). Данный метод в дальнейшем мы будем использовать для запросов как на извлечение данных, так и на их изменение.
  • db2Array() – метод возвращает массив данных, в зависимости от запроса.
  • dbCol2Array($num) - представляет данные таблицы в виде одного столбца. Входной параметр имеет тип String.
  • dbRow2Array($num) – возвращает данные одной конкретной строки. Входной параметр – integer.
  • db2Var($row, $col) – представляет пересечение столбца и ячейки.
  • dbNumRows() – возвращает количество строк, выбранных запросом
  • getErrors()

Давайте теперь рассмотрим реализацию вышеприведенных методов.

<?
class MySQLDataAccess extends DataAccess
{
	public function Query($sql)
	{
		$con = new MySQLConnection($this->db);
		$this->connection = $con->getConnection();
		if(!$this->result = @mysql_query($sql, $this->connection))
		{
			$this->Errors[]="Не удается выполнить запрос: $sql";
			return FALSE;
		}
		else
			return TRUE;
	}
	
	public function setCharset($charset)
	{
		$sql = "SET NAMES '$charset'";
		$this->Query($sql);
	}
	
	public function db2Array()
	{ 
		$array = array();
		while($rows = @mysql_fetch_array($this->result, MYSQL_ASSOC))
		{
			$array[] = $rows;
		}
		return $array;
			
			
	 }
	 
	 public function dbCol2Array($num) 
	 {
		while($rows = @mysql_fetch_array($this->result, MYSQL_ASSOC)) 
		{ 
			$data[] = $rows[$num]; 
		} 
		if(count($data)<0)
		{
			$this->Errors[]="Массив столбцов пуст";
			return FALSE;
		}
		else
			return $data; 
	 }
	
	 public function dbRow2Array($num) 
	 {
		$num = (int)$num;
		$i=0;
		while($rows = @mysql_fetch_array($this->result, MYSQL_ASSOC)) 
		{ 
		  if($i === $num) 
		  { 
			 $data = $rows; 
			 break;
		  }
		  $i++; 
		} 
		if(count($data)<0)
		{
		  $this->Errors[]="Массив строк пуст";
		  return FALSE;
		}
		else
			return $this->data; 
	 }
	   
	 public function db2Var($row, $col) 
	 { 
		$i=0;
		while($rows = @mysql_fetch_array($this->result, MYSQL_ASSOC))
		{
			if($i == $row)
			{
				$data = $rows[$col];
				break;
			}
			$i++;
		}
		if(!empty($data))
		{
			return $data;
		}
		else
		{
			$this->Errors[]="Не удается извлечь $row x $col ячейку";
			return false;
		}
	 }
		
	 public function dbNumRows() 
	 {
		$numRows = @mysql_num_rows($this->result);       
		if($numRows > 0) 
			 return $numRows; 
		else
		{
			 $this->Errors[]="Не удается получить число выбранных строк";
			 return false;
		}
	 }
}
?>

Теперь осталось сделать так, что бы при необходимом условии выполнялась определенная реализация. Пишем класс…

class DataAccessor
{
	private $type;
	
	public function __construct($type)
	{
		$this->type = $type;
	}
	
	public function getObj($db)
	{
		switch($this->type)
        {
            case 'MySQL':
                $obj = new MySQLDataAccess($db);
                break;
            case 'MSSQL':
                $obj = new MSSQLDataAccess($db);
                break;
            default:
                $obj = new MySQLDataAccess($db);
	  break;
        }
		return $obj;
	}
}
?>

Как видно из листинга, есть дополнительный метод, который устанавливает кодировку, необходимую для запроса. Данный метод не был включен в общий список абстрактных методов, т.к. является специфическим для данной СУРБД.

Принцип действия данного класса заключается в том, что, в зависимости от параметра type, возвращается необходимый объект.

Рассмотрим, как все это реализуется в коде.

<?
$dbo = new Db('$user', '$password', '$host', '$database');
$dba = new DataAccessor('MySQL');
$db = $dba->getObj($dbo);
$db->setCharset('cp1251');
?>

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

В последующих статьях я расскажу, как использовать данный способ взаимодействия с источниками данных.



Теги: PHP , программирование , СУБД





СЛЕДУЮЩИЕ СТАТЬИ
Ударь копирайтом по работодателю: возвращаем финансы, честно заработанные на служебных произведениях
Руткит в сетевухе: фантазии программиста о создании непобедимого руткита
Чемпионаты по программированию и не только
Алгоритмическая симфония из одной строчки кода
Kinect: разбираемся с новым девайсом и учимся писать для него приложения
Программное обеспечение: поглощая мир
SMS-похититель для Android: Sсriрting Layer for Android - интересная среда разработки для мобильного телефона
13 утилит для безопасной разработки: инструменты от Microsoft для написания надежного кода
Silverlight: защита и нападение
Кроссплатформенный кодинг для мобильных платформ: покоряем iOS, Android, Bada, Symbian и WM с помощью AirPlaySDK
ПРЕДЫДУЩИЕ СТАТЬИ
Cross-Build Injection: сборка для хакера №2
SOAP-2, или восстание машин
Cross-Build Injection: сборка для хакера №1
Невидимые LKM-атаки на Windows NT: поваренная книга руткитмейкера
Не сыпь мне соль на password: реанимируем умерший MD5
Предательский антивирус: тырим данные с flash-модулей и CD/DVD
Большой брат для мобилы
12 языков программирования, которые потрясли мир (тем, что на них нельзя программировать)
Останься легальным: как написать программу для Windows, не потратив ни копейки
Прощай, кейген: защищаем софт от генераторов ключей
ОБСУЖДЕНИЕ СТАТЬИ
Логин:
Пароль:
Если у вас есть форумный логин - вы можете использовать его, иначе анонимный гостевой доступ.

Для оставления комментария вы можете зарегистрироваться по упрощенной процедуре.

Обсуждение этой статьи на forum.xakep.ru
Для отправки сообщения введите код, указанный на картинке
Сообщение

UserГость
30.10.2007 17:45:46
Ответить Ссылка
ответьте пожалуйста, что будет, если после вот этого: $dbo = new Db('$user', '$password', '$host', '$database'); и соотв. после вот этого: $con = new MySQLConnection($this->db); сервер уходит в ребут ? а такое бывает!
UserГость
30.10.2007 18:13:46
Ответить Ссылка
Для ежей
UserГость
30.10.2007 19:41:38
Ответить Ссылка
Базы данных на PHP: переписываем PEAR шаг за шагом :)
UserГость
30.10.2007 20:33:49
Ответить Ссылка
Интересно. Спасибо автору.
UserГость
30.10.2007 20:38:41
Ответить Ссылка
Автор первому guest
Вот смотрите...
1 - создается объект $dbo = new Db(?$user?, ?$password?, ?$host?, ?$database?);
2 - создается объект $dba = new DataAccessor(?MySQL?); В конструкторе инициализируется тип необходимой СУРБД.
3 - $db = $dba->getObj($dbo); Вот в этом шаге происходит инициализация параметров подключения. Само же постоянное соединение происходит при вызове метода Query($sql), в котором и выполняется сам запрос. Т.е. время подключения ненамного отстает от времени выполнения запроса.
Конечно можно реализовать поддержку транзакций... в принципе я займусь этим.... когда появится необходимость....
UserГость
30.10.2007 22:07:12
Ответить Ссылка
дайте угадаю - PDO запретили/отменили/забанили? Ааа, я понял - так знаков меньше и гонорара. Да и скучнее статья будет.
Очередной велосипед. Надоело. Автор - быдлокодер/негр.
UserГость
31.10.2007 1:44:07
Ответить Ссылка
Велосипедисты! давно уже есть PDO! И не говорите про пхп4, он уже сдох
UserГость
31.10.2007 2:28:28
Ответить Ссылка
жесть... Новинка.. =))
UserГость
31.10.2007 7:25:10
Ответить Ссылка
Data Source Object дай перевернем и получится Object Data Source. Не из ASP.NET 2.0 зачерпули такое красивое название?
UserГость
31.10.2007 10:33:33
Ответить Ссылка
Мне понравилась статья. Конструктивна достаточно. Я нечто подобное сам написал.
UserГость
31.10.2007 15:22:48
Ответить Ссылка
БАЯН!
UserГость
31.10.2007 23:42:30
Ответить Ссылка
Спасибо за объяснение, суть уловил,
вообщем для не кретичных задач - зелёный
свет!

А всем остальным "знатокам" : кидаться
пустыми словами не красиво! Если вы
считаете, что у вас данная задача получится
лучше, так покажите Ваш труд Нам!

Дело же не в том, что автор создал нечто
новое и решил всех нас удивить, а в том,
что наша кодокопательная братия живёт
этим. Разбор кода с целью подчерпнуть
новые идеи - задача многогранная, и в
знакомых чертах того или иного творения,
нет да и попадётся новая ниточка идей,
причём для каждого своя, в зависимости
от его экспириенса ;)
UserГость
02.11.2007 7:44:37
Ответить Ссылка
Я даже дочитывать не стал этот детсад)
А в UML это нельзя изобразить. Что это? Table data gateway в реализации php?

Я понимаю если это было бы статьей, где описывается алгоритм интересный или какой-нибудь трюк аля sql-injection.
UserГость
08.11.2007 22:40:37
Ответить Ссылка
класно, но кандела хуже!!!
@-people+
UserГость
23.11.2007 5:15:32
Ответить Ссылка
hy
UserГость
28.11.2007 17:49:53
Ответить Ссылка
Ребят, ну закрывайте вы этот детсад, епрст. Ну вам не стыдно еще писать на PHP? Протсо смешной язык для быдло-кодеров, смешное ООП и смешная реализация в данной статье. Не верите? rubyonrails.ru
А насчет UML диаграмм - выкинте их на помойку и ботайте eXtreme Programming.
UserГость
10.01.2008 18:03:12
Ответить Ссылка
1. Не всегда уместно (особенно когда производительность/ресурсоёмкость критична).

2. Я бы, пожалуй, для DataAccessor поюзал бы static. Ну зачем же в память зря гадить, а потом ещё и чистить.

3. Про PDO, PEAR +1. А те, кто не в состоянии это сами найти - тем статья не поможет.

4. Про ruby: хороший язык, только представлен здесь типовым быдлокодером. Не делайте о языке выводов по коменту от 28.11.2007 17:49:53. (сам предпочитаю PHP5 по целому ряду причин)

И всем, кричащим про баяны - 10% читают маны и теорию, а остальные копи-пейстят чужое, не понимая толком почему так, потом правят это под свои нужды (обычно криво) и хвастаются достижением, которое потом клонируют следующие недокодеры.

Спасибо автору за воспитательную работу в рядах начинающих.
UserГость
15.01.2008 14:20:24
Ответить Ссылка
Да и что тут новое. Смысл это писать на php если все это давно реализовано в Asp.Net и Java. Ничего оригинального.
UserXXHACKERXX
11.03.2008 23:19:50
Ответить Ссылка
cool




Keywords: zPOSTz zSOFTz, zHOWz, zINFOz z40895z
Для Авторов: edit Lock delete Lock



    Rambler's Top100