Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель PHP 4 - Котеров Д. В.

.pdf
Скачиваний:
99
Добавлен:
24.05.2014
Размер:
4 Мб
Скачать
☆

468

Часть V. Приемы программирования на PHP

var $Fields; // все физические поля таблицы (имя=тип, имя=тип...) var $Error; // текст последней ошибки ("", если нет)

var $JustCreated; // 1, если таблица была создана, а не загружена //*** Внутренние функции

//Упаковывает поля массива в строку, за исключением тех, которые

//сами являются непосредственными полями в базе данных. function _PackFields(&$Hash)

{ $Data=array();

foreach($Hash as $k=>$v) if($k!=DataField) if(!isSet($this->Fields[$k])) $Data[$k]=$v; return Serialize($Data);

}

//Виртуальная функция производного класса вызывается ПЕРЕД любым

//занесением данных в таблицу (добавлением и обновлением). То есть

//она предназначена для "прозрачной" автоматической генерации некоторых

//полей записи (например, времени ее изменения) в производном классе

//перед ее сохранением.

//Можно, к примеру, в таблице держать какую-нибудь дату в формате

//SDN, а "делать вид", что она хранится в обычном представлении

//"дд.мм.гггг".

//Если эта функция возвратит 0, то операция закончится с ошибкой. function PreModify(&$Rec) { return 1; }

//Виртуальная функция вызывается ПОСЛЕ выборки записи из таблицы, а

//также в конце модификации записи. То есть она предназначена для

//"прозрачной" модификации только что полученной из таблицы записи.

//Возвращаясь к предыдущему примеру, мы можем при извлечении записи

//из таблицы STM-поле преобразовать в "дд.мм.гггг", и "никто ничего

//не заметит".

function PostSelect(&$Rec) { return; } // Возвращает имя таблицы

function GetTableName() { return $this->TableName; }

//Возвращает результат запроса select. В дальнейшем этот результат

//(дескриптор) будет, скорее всего, обработан при помощи GetResult().

//$Expr — выражение SQL, по которому будет идти выборка

//$Order — правила сортировки (по умолчанию — по убыванию id) function TableSelectQuery($Expr="",$Order="id desc")

{ $this->Error=""; if(!$Expr) $Expr="1=1";

$r=mysql_query("select * from ".$this->TableName.

Глава 31. Объектно-ориентированное программирование на PHP

469

" where ($Expr) and (id>1) order by $Order"); if(!$r) { $this->Error=mysql_error(); return; } return $r;

}

function SelectQuery($Expr="",$Order="id desc") { return $this->TableSelectQuery($Expr,$Order); }

//Возвращает результат предыдущего запроса select (точнее, очередную

//найденную запись) в виде распакованного (!) массива. Если

//SelectQuery() нашла несколько записей, то, последовательно вызывая

//GetResult(), можно считать их все. Метод делает всю "черную" работу

//по сериализации. Еще раз: если у результата несколько строк, то метод

//возвращает очередную. Если строки кончились, возвращает "".

//Чаще всего в вызове этой функции (и функции SelectQuery) нет

//необходимости — можно воспользоваться методом Select(), который по

//запросу сразу возвращает массив со всеми обработанными результатами! function TableGetResult($r)

{ $this->Error="";

//Выбираем очередную строку в виде массива

if($r) $Result=mysql_fetch_array($r); else $this->Error=mysql_error(); if(!@is_array($Result)) return;

//Перебираем все поля таблицы и записываем их в массив $Hash $Hash=array();

foreach($this->Fields as $k=>$i) if(isSet($Result[$k])) $Hash[$k]=$Result[$k];

//Распаковываем поле с данными

$Hash+=SqlUnpack($Hash[DataField]); unSet($Hash[DataField]); $this->PostSelect($Hash);

// Все сделано return $Hash;

}

function GetResult($r) { return $this->TableGetResult($r); }

//Примечание: мы используем две функции, из которых GetResult()

//просто является синонимом для TableGetResult(), чтобы позволить

//производному классу вызывать функции MysqlTable, даже если они

//переопределены в нем. К сожалению, в PHP это единственный метод

//добиться цели.

//Аналог mysql_num_rows()

function GetNumRows($r) { return mysql_num_rows($r); }

470

Часть V. Приемы программирования на PHP

//Аналог mysql_data_seek(). После вызова этой функции указатель на

//дескриптор $r "перескочит" на найденную запись номер $to, после

//чего GetResult() ее и возвратит.

function DataSeek($r,$to) { return mysql_data_seek($r,$to); }

//Создает или загружает таблицу по имени $Name.

//$Fields — список полей базы. Именно по ним в дальнейшем можно

//будет вести поиск и строить индекс. Кроме того, в запись можно будет

//добавлять ЛЮБЫЕ другие переменные, но они будут сериализованы, а

//потом восстановлены. Формат списка: массив с ключами — именами

//переменных и значениями — их типами. Если $Fields — не массив, то

//считается, что таблица открывается такой, какой она есть. В противном

//случае производится проверка: не добавились или не удалились ли какие-

//то поля или индексы и, если это так, то выполняется соответствующая

//модификация таблицы (кстати, это процесс довольно длительный).

//ВНИМАНИЕ: если в таблице было какое-то поле, которое сериализуется, то

//в будущем при добавлении этого поля к $Fields оно НЕ будет

//автоматически переведено в ранг несущих, т. е. попросту

//пропадет (и наоборот).

//РЕКОМЕНДАЦИЯ: перечисляйте в $Fields те поля, для которых вы ТОЧНО

//уверены, что они будут всегда присутствовать в базе, а также те,

//по которым нужно будет вести поиск, строить индекс и использовать

//distinct.

//$Index — по каким полям нужно строить индекс. Индекс несколько

//увеличивает размер базы, но зато вырастает скорость поиска по ней

//(точнее, по тем полям, для которых используется индекс). Ключи — имена

//столбцов, значения — "размер" индекса (0, если по умолчанию, что чаще

//всего наиболее разумно)

function MysqlTable($Name,$Fields="",$Index="") { $this->TableName=$Name; $this->Error=""; if(is_array($Fields)) {

foreach($Fields as $k=>$v)

if(!eregi("not null",$v)) $Fields[$k]=$v." not null"; $Fields=array("id"=>"int auto_increment primary key") +$Fields+array(DataField=>"mediumblob");

}

Def0($Index,array());

// Считываем из таблицы поле с ее параметрами $this->Fields=array(DataField=>"mediumblob");

Глава 31. Объектно-ориентированное программирование на PHP

471

$Data=$this->TableGetResult(

mysql_query("select ".DataField." from $Name where id=1") );

//Если таблица существует, то запрос окончится успешно.

//В этом случае нужно проверить, не изменилась ли таблица с момента

//последнего обращения, и если это так, то подкорректировать ее. if(@is_array($Data)) {

if(!is_array($Fields)) {

$this->Error="Couldn't create table: no fields specified"; return;

}

Def0($Data["Fields"],array()); Def0($Data["Index"],array());

//** Возможно, что-то изменилось. Тогда выполняем alter table. //1. Добавились поля?

$Lst=array(); foreach($Fields as $k=>$v) {

if(!isSet($Data["Fields"][$k])) $Lst[]="add $k $v"; else if($Data["Fields"][$k]!=$v) $Lst[]="change $k $k $v";

}

//2. Удалились поля? foreach($Data["Fields"] as $k=>$v) if(!isSet($Fields[$k])) $Lst[]="drop $k"; //3. Добавились индексы?

foreach($Index as $k=>$v) if(!isSet($Data["Index"][$k])) $Lst[]="add index index_$k ($k".($v!=0?" ($v)":"").")"; //4. Удалились индексы?

foreach($Data["Index"] as $k=>$v) if(!isSet($Index[$k])) $Lst[]="drop index index_$k"; if(count($Lst)) {

PrintDump($Lst);

if(!mysql_query("alter table $Name ".implode($Lst,","))) { $this->Error=mysql_error();

return;

}

$Changed=1;

}

$this->JustCreated=0; } else {

472

Часть V. Приемы программирования на PHP

//Необходимо создать таблицу.

//BugFix by DM: При создании новой таблицы необходимо очистить

//переменную Error, иначе в ней остается ошибка от попытки

//чтения полей.

$this->Error=""; $Lst=array();

foreach($Fields as $k=>$v) $Lst[]="$k $v"; foreach($Index as $k=>$v)

$Lst[]="index index_$k ($k".($v!=0?" ($v)":"").")"; if(!mysql_query("create table $Name (".implode($Lst,",").")")) { $this->Error=mysql_error();

return;

}

$this->JustCreated=1;

}

// Сохраняем информацию о таблице, если она поменялась if(!empty($Changed)||$this->JustCreated) { $Data["Fields"]=$Fields;

$Data["Index"]=$Index;

Def0($Data["Info"],array()); // Информации не было — делаем пустой $Data=SqlPack($Data);

if($this->JustCreated) {

$Result=mysql_query("insert into $Name(id,".DataField.") values(1,'$Data')");

} else {

$Result=mysql_query("update $Name set ".DataField. "='$Data' where id=1");

}

if(!$Result) { $this->Error=mysql_error(); return; }

}

$this->Fields=$Fields; $this->Index=$Index;

}

//Записывает в таблицу информацию, общую для всей таблицы. Эта

//информация может быть получена потом только при помощи метода

//GetInfo(), и никак иначе. Например, если таблица используется для

//гостевой книги, мы можем сюда записывать какие-нибудь параметры этой

//книги — скажем, имя и пароль владельца. $Inf может быть чем угодно —

Глава 31. Объектно-ориентированное программирование на PHP

473

// даже массивом.

function TableSetInfo($Inf)

{$this->Error="";

//Читаем информационную запись

$r=mysql_query("select ".DataField." from ". $this->TableName." where id=1");

if(!($Data=$this->GetResult($r))) return;

//Устанавливаем поле Info $Data["Info"]=$Inf; $Data=SqlPack($Data);

//Сохраняем результат

if(!mysql_query("update ".$this->TableName. " set ".DataField."='$Data' where id=1")) { $this->Error=mysql_error(); return; } return 1;

}

function SetInfo($Inf) { return $this->TableSetInfo(&$Inf); }

//Возвращает информацию о таблице, ранее занесенную в нее при помощи

//SetInfo. Если информация не была занесена, возвращает пустой массив. function TableGetInfo()

{ $this->Error="";

//Читаем информационную запись

$r=mysql_query("select * from ".$this->TableName." where id=1");

// Если что-то не в порядке, GetResult установит поле Error у объекта if(!($Data=$this->GetResult($r))) return array(); if(!@is_array($Data["Info"])) $Data["Info"]=array();

return $Data["Info"];

}

function GetInfo() { return $this->TableGetInfo(); }

//Уничтожает таблицу. Осторожно! Таблица удаляется без всяких

//предупреждений!!!

function TableDrop() { $this->Error="";

if(!mysql_query("drop table ".$this->TableName)) { $this->Error=mysql_error();

return 0;

}

return 1;

}

474

Часть V. Приемы программирования на PHP

function Drop() { return $this->TableDrop(); }

//Добавляет запись $Rec (обычно это ассоциативный массив с некоторыми

//установленными полями) в таблицу. Автоматически у нее проставляется

//id, а также проверяется, уникальны ли у записи те поля, которые должны

//быть уникальными (указываются в конструкторе). Возвращает 1 в случае

//успеха, при этом в $Rec содержится окончательно сформированная

//запись.

function TableAdd(&$Rec) { $this->Error="";

if(!$this->PreModify($Rec)) return 0;

//Иначе все в порядке. Добавляем запись. $Rec[DataField]=$this->_PackFields($Rec);

//Составляем список имен полей и их значений $LNames=$LVals=array(); foreach($this->Fields as $name=>$type) { $LNames[]=$name; $LVals[]="'".Apostrophs($Rec[$name])."'";

}

$LNames=implode($LNames,","); $LVals=implode($LVals,","); unSet($Rec[DataField]);

//Добавляем

if(!mysql_query("insert into ".$this->TableName. "($LNames) values($LVals)"))

{ $this->Error=mysql_error(); return 0; } $Rec["id"]=mysql_insert_id(); $this->PostSelect($Rec);

return 1;

}

function Add(&$Rec) { return $this->TableAdd(&$Rec); }

//Удаляет из таблицы записи, удовлетворяющие выражению $Expr.

//Например: $Tbl->Delete("(id=$id) or (id=0)");

function TableDelete($Expr) { $this->Error="";

if(!mysql_query("delete from ".$this->TableName. " where ($Expr) and (id>1)"))

{ $this->Error=mysql_error(); return 0; } return 1;

Глава 31. Объектно-ориентированное программирование на PHP

475

}

function Delete($Expr) { return $this->TableDelete($Expr); }

//Возвращает массив записей (ключ — id, значение — запись). В массив

//будет занесено не более $Num записей. Для каждой записи

//вызывается PostSelect()!

function TableSelect($Expr="",$Num=100000,$Order="id desc")

{$this->Error="";

//Выполнить запрос

$r=$this->SelectQuery($Expr,$Order); if(!$r) return 0; // Цикл по найденным записям

for($i=0,$Found=array(); $i<$Num&&($Rec=$this->GetResult($r)); $i++) $Found[$Rec["id"]]=$Rec;

return $Found;

}

function Select($Expr="",$Num=100000,$Order="id desc") { return $this->TableSelect($Expr,$Num,$Order); }

//Обновляет запись в таблице, при этом запись $Upd изменяется и

//становится фактически такой, как она будет выглядеть после обновления.

//То есть к ней могут добавиться новые поля из таблицы. Если записи с

//таким id нет (когда $id не указан в параметрах, его значение берется

//равным $Upd["id"]), то генерируется ошибка!

//Возможно, в записи $Upd не задан идентификатор id (это бывает, если

//мы только что получили данные из формы). В этом случае можно этот

//идентификатор передать через $id.

//Итак, при обновлении id НЕ МЕНЯЕТСЯ по определению (в отличие от

//ДОБАВЛЕНИЯ, когда id всегда проставляется)!

function TableUpdate(&$Upd,$id=0)

{$this->Error="";

//Если задан $id, то устанавливаем в записи этот идентификатор if($id) $Upd["id"]=$id;

//Загружаем старую запись. Она должна быть одна.

$r=$this->SelectQuery("id=".$Upd["id"]); $Rec=$this->GetResult($r);

//Если не удалось, значит, неверное обновление — записи

//еще не существует

if(!$Rec) { $this->Error="NotExists"; return 0; }

//Иначе все в порядке — добавляем. Сначала обновляем

//поля и упаковываем переменные

476

Часть V. Приемы программирования на PHP

$Rec=$Upd+$Rec; $Upd=$Rec; if(!$this->PreModify($Rec)) return 0; $Rec[DataField]=$this->_PackFields($Rec);

//Затем составляем список полей для обновления $Lst=array();

foreach($this->Fields as $name=>$type) $Lst[]="$name='".Apostrophs($Rec[$name])."'"; $Lst=implode($Lst,",");

//Выполняем запрос

if(!mysql_query("update ".$this->TableName. " set $Lst where id=".$Rec["id"]))

{ $this->Error=mysql_error(); return 0; } $this->PostSelect($Rec);

return 1;

}

function Update(&$Upd,$id=0) { return $this->TableUpdate(&$Upd,$id); }

//Возвращает число записей, удовлетворяющих выражению $Expr.

//Если $Expr не задано, возвращает число ВСЕХ записей. function TableGetCount($Expr="")

{ $this->Error=""; if(!$Expr) $Expr="1=1";

$r=mysql_query("select count(if(($Expr) and (id>1),1,NULL)) from ". $this->TableName);

if(!$r) { $this->Error=mysql_error(); return 0; } $a=mysql_fetch_array($r);

return $a[0];

}

function GetCount($Expr="") { return $this->TableGetCount($Expr); }

//Возвращает СПИСОК всех уникальных значений поля $field

//в таблице, удовлетворяющих тому же условию $Expr.

//ВНИМАНИЕ: эта функция работает лишь тогда, когда поле $field

//присутствовало среди полей $Fields при вызове конструктора.

//В противном случае генерируется ошибка.

//Рекомендуется при создании таблицы для поля $field создать индекс. function TableGetDistinct($field,$Expr="")

{ $this->Error=""; if(!$Expr) $Expr="1=1";

$r=mysql_query("select distinct $field from ".

Глава 31. Объектно-ориентированное программирование на PHP

477

$this->TableName." where ($Expr) and (id>1)");

// distinct НЕ работает вместе с order by! Почему — неясно...

if(!$r) { $this->Error=mysql_error(); return 0; } for($Arr=array(),$i=0,$n=mysql_num_rows($r); $i<$n; $i++) $Arr[]=mysql_result($r,$i,0);

return $Arr;

}

function GetDistinct($field,$Expr="")

{ return $this->TableGetDistinct($field,$Expr); } }; // Конец класса

?>

А вот пример применения этого класса (листинг 31.3). Делает он следующее: открывает таблицу в некоторой базе данных (если таблицы с таким именем не существует, создает ее) и добавляет одну пробную запись.

Листинг 31.3. Пример использования класса MysqlTable

<?

include "librarian.phl"; // подключаем библиотекарь

Uses("MysqlTable"); // подключаем модуль с классом таблицы

//Устанавливаем соединение с базой данных mysql_connect("localhost"); mysql_select_db("test");

//Открываем таблицу

$t=new MysqlTable("test",array("t"=>"int")); // Добавляем запись

$d=array("t"=>time()); $t->Add($d);

//Работаем с блоком информации $Inf=$t->GetInfo(); $Inf["a"]=@$Inf["a"]+1; $Inf["b"]=@$Inf["b"]+10;

echo $Inf["a"]," ",$Inf["b"],"<br>"; $t->SetInfo($Inf);

//Выбираем все записи и выводим их $d=$t->Select();

foreach($d as $id=>$Data) { echo "$id: ".$Data['t']."<br>";