| ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Доступ к базам данных средствами PHP PHPLib - основная библиотека PHP
Уровень абстрактного представления базы данных
Аутентификация
Заключение
База данных является для разработчика программного
обеспечения для Web привычным инструментом. Программист должен знать язык
SQL по крайней мере не хуже, чем PHP. В большинстве Web-приложений используются
реляционные базы данных. В то время как новички пытаются по возможности
избежать операций, связанных с запросами SQL и реляционными системами
управления базами данных (РСУБД), опытный разработчик ценит те возможности,
которые они предоставляют. Любая мало-мальски нетривиальная задача, например
организация параллельного доступа, поиск и сортировка, обработка отношений
между различными множествами данных, мгновенно становится серьезной проблемой
при использовании методики хранения данных в файлах или массивах. Базы
данных специально предназначены для эффективной организации и поиска информации,
и нет никакой необходимости имитировать этот механизм в виде "псевдобаз".
PHPLib - основная библиотека PHP Как уже отмечалось, библиотека PHPLib может значительно
облегчить те задачи, с которыми программист сталкивается каждый день.
Мы еще раз поднимем вопрос об основных понятиях, связанных с разработкой
Web-приложений (управление сеансами работы пользователей, аутентификация,
разделение кода и разметки). Библиотека PHPLib содержит целый ряд объектов,
которые позволяют решать подобные задачи. Историческая справка Работу над библиотекой PHPLib начали (в 1998
г.) Борис Эрдман (Boris Erdmann) и Кристиан Кёнтопп (Christian Koehntopp).
Работая над неким большим проектом, они поняли, что вынуждены постоянно
вновь и вновь кодировать одни и те же процедуры, и решили оформить соответствующие
функции в виде библиотеки. Однако предложенное ими программное решение
оказалось не слишком удачным. Преимущества и недостатки Мы не устаем повторять, что оружие для боя всегда
надо выбирать самым тщательным образом. Библиотека PHPLib предназначена
для использования в проектах, работа над которыми занимает больше двух
дней. Первый опыт применения этой библиотеки, разумеется, будет сопряжен
для вас с дополнительным объемом работы - необходимо прочитать документацию,
уяснить сущность основных концепций, еще раз удостовериться, что вы все
правильно поняли. Файлы local.inc и prepend.php3 В дистрибутиве PHPLib имеются два файла: local.inc
и prepend.php3, - содержимое которых вам, вероятно, придется изменить.
require($_PHPLIB["libdir"] . "db_mysql.inc"); /*измените эту строку, чтобы она соответствовала вашей базе данных */ Если вы используете какие-то другие классы из
библиотеки PHPlib, например Template, рекомендуется включать их
в файл prepend.php3. Настройка PHPLib В большинстве случаев базовые классы библиотеки
PHPLib не используются непосредственно, но на их основе вы можете определять
собственные производные классы, отвечающие вашим потребностям. Наряду
с этим в библиотеке PHPLib существуют классы, для которых определена реализация
по умолчанию. С некоторыми из них мы будем сталкиваться в примерах. Реализация
по умолчанию всегда базируется на определенных допущениях, касающихся
характеристик вашей системы. В частности, предполагается, что вы используете
базы данных в формате MySQL. Если это не соответствует действительности,
вам необходимо внести соответствующие изменения в файл local.inc;
мы рекомендуем создать новый объект как расширение DB_Sql. Последовав
нашему совету, вы избежите необходимости вновь и вновь задавать значения
соответствующих параметров настройки для каждого создаваемого приложения:
вы четко установите их в своем классе раз и навсегда. Уровень абстрактного представления базы данных Уровень абстракции, или абстрактного представления, базы данных - это интерфейс, который предоставляет ряд функций для работы с различными базами данных независимо от способа их реализации. Изменив внутренний интерфейс уровня абстракции базы данных, можно легко перейти, например, с формата MySQL на формат Oracle. В языке Perl таким уровнем является DBI (DataBase Interface), а в библиотеке PHPLib - уровень абстрактного представления базы данных, организованный в классе DB_Sql. Переносимость Для профессионального программиста, создающего
Web-приложения, возможность абстрактного представления базы данных может
быть очень полезной и важной. Основой каждого приложения является некая
модель данных - множество структур данных, ориентированных на решение
наиболее общих задач. Модель данных во многих случаях реализуется непосредственно
в базе данных. Пакет PHP поддерживает множество различных баз данных;
каждая из них располагает своим интерфейсом прикладных программ. На основе
такого программного интерфейса невозможно обеспечить разработку программ,
независимых от конкретной базы данных и операционной системы. Если вы
не будете использовать некий уровень абстракции, примером которого может
служить библиотека PHPLib, то при переводе приложения с MySQL на Oracle
вам придется проделать тяжелую работу. В табл. 6.1 представлены различия
между программными интерфейсами баз данных в различных системах. Таблица 6.1. Интерфейсы прикладных программ, реализованные в СУБД MySQL и Oracle
Если вашей целью является создание переносимого
кода, вы должны за километр обходить все специфические черты конкретных
баз данных. Библиотека PHPLib может несколько упростить вашу задачу: в
ней, к примеру, предусмотрены механизмы встроенной последовательной обработки
записей и блокировки таблиц, которые не зависят от базы данных. Недавно
разработчики библиотеки PHPLib добавили к ней класс Query, который
предназначен для абстрагирования простых запросов (вставки, обновления,
инструкций WHERE и некоторых других; такие запросы обычно составляют
примерно 80 % общего числа запросов, адресованных базе данных), что позволяет
сделать их независимыми от базы. В настоящее время данный класс работает
только с СУБД MySQL и Oracle, начиная с версии 7. Режим отладки В классе DB_Sql предусмотрен отладочный режим, который позволяет просматривать запросы, адресуемые базе данных. Для включения отладочного режима вставьте в программу инструкцию $db->Debug = true;, которая должна выполняться после создания экземпляра класса. Мы обычно определяем глобальную константу DEBUG, которую присваиваем классу DB_Sql, как показано ниже. define("DEBUG", true); $db->Debug = DEBUG; Если включен отладочный режим, то класс DB_Sql при вызове тех или иных функций будет автоматически выводить определенные значения и много дополнительной информации. Это может оказать вам существенную помощь, если вы ищете ошибку или вам необходимо проверить правильность запросов SQL, которые генерирует ваш сценарий. Обработка ошибок Библиотека PHPLib обрабатывает все ошибки, которые
могут возникнуть при работе с функциями, имеющими отношение к работе с
базами данных. К тому же код на основе DB_Sql обычно более компактен,
так как вам не надо беспокоиться об обработке ошибок вручную. class test_db extends BD_Sql { function haltmsg($msg) { print("Ошибка базы данных: $msg За вывод сообщений об ошибках отвечает функция haltmsg(); фактически принятие решения о том, что следует сделать: прекратить выполнение сценария или попытаться преодолеть последствия ошибки, - эта функция оставляет на усмотрение библиотеки PHPLib. Данная функция выполняется только в том случае, когда переменная $Halt_On_Error имеет значения yes или report. Переменная $msg, передаваемая в качестве аргумента функции haltmsg(), содержит словесное описание ошибки. Следует также заметить, что вы можете использовать инструкции $this->Error и $this->Errno для получения сообщений об ошибках, генерируемых ядром базы данных. Пример использования класса DB_Sql Использование класса DB_Sql лучше всего проиллюстрировать небольшим примером. Исходный код, приведенный в листинге 6.1, обращается к базе данных и выводит на экран все строки соответствующей таблицы. В этом примере используется класс Example_Db; он определяется в файле local.inc, который расширяет класс DB_Sql и показывает, каким образом программист может создавать собственные классы. Для простоты мы воспользуемся примером, позаимствованным из дистрибутива библиотеки PHPLib. Листинг 6.1. Первый пример использования DB_Sql //создать экземпляр класса DB_Sql $db = new Example_DB; //подключиться к реляционной СУБД $db->connect("test", "localhost", "root", ""); //создать инструкцию SQL $sql = "SELECT * FROM test"; //выполнить запрос $db->query($sql); //просмотреть результат while($db->next_record()) { //просмотреть хэш-таблицу $db->Records while(list($key, $value) = each($db->Record)) { //выводить только нечисловые индексы print(is_string($key) ? "$key: $value В первой строке создается новый экземпляр класса
DB_Sql. По умолчанию этот класс определяется в файле db_mysql.inc
(загружаемом в сценарии prepend.php3); в качестве СУБД используется
MySQL. $db = new Example_Db; $db->Database = 'test'; $db->Host = 'localhost'; $db->User = 'root'; $db->Password = ''; При вызове функции $db->query() библиотека
PHPLib заметит, что соединение еще не установлено, и откроет его автоматически,
используя заданные значения перечисленных переменных класса. Листинг 6.2. Образец реализации традиционного подхода //подключиться к реляционной СУБД $link =mysql_connect('localhost', 'root', '') or die(mysql_error()); //выбрать базу данных $db = mysql_select_db('test') or die(mysql_error()); //создать оператор SQL $sql = "SELECT * FROM test" //выполнить запрос $res = mysql_query($sql) or die(mysql_error()); //просмотреть множество результатов while($row = mysql_fetch_array($res)) { //просмотреть хэш-таблицу $db->Records while(list($key, $value) = each($row)) { " : ""); } print(" "); } Обратите внимание на один нюанс. В приложении вы можете работать только с одной базой данных. В системе PHP не очень удачно реализован доступ из одного сценария к различным базам данных, особенно к базам данных в формате MySQL. Интерпретатор PHP предполагает, что может повторно использовать ранее установленные соединения, применив то же самое имя и пароль. Взгляните на следующий пример. $res_one = mysql_connect("localhost", "root", "") or die(mysql_error()); $res_two = mysql_connect("localhost", "root", "") or die(mysql_error()); Вы могли бы подумать, что здесь два разных идентификатора соединения? Это не так, поскольку при втором вызове функции mysql_connect() интерпретатор PHP использует открытое соединение повторно. Если вы выведете значения идентификаторов соединений $res_one и $res_two, то увидите один и тот же идентификатор ресурса для обеих переменных. В результате использование функции mysql_select_db() для одного соединения меняет контекст для другого соединения. Аналогичная ситуация возникает и при использовании одной базы данных для объекта DB_Sql, а другой - для данных сеанса. К сожалению, приемлемого решения этой проблемы в настоящее время не существует. Сеанс работы пользователя Библиотека PHPLib предоставляет возможности,
по меньшей мере, не уступающие возможностям встроенной библиотеки управления
сеансами PHP. Даже имена функций в этих библиотеках часто совпадают. Но
хотя во многих отношениях механизмы встроенного управления сеансами PHP
и библиотеки PHPLib похожи, есть и различия. Одной из полезных дополнительных
возможностей библиотеки PHPLib является режим автоматического переключения
методов повторной генерации идентификатора сеанса, который рассматривается
в следующем разделе.
Запасной режим повторной генерации идентификатора сеанса По умолчанию все средства управления сеансами работают
через файлы персональных Интернет-настроек (cookies). Как подчеркивалось
в главе 4, этот метод является предпочтительным (если он поддерживается
клиентом); кроме того, это самый простой способ повторной генерации идентификатора
сеанса. Но вы можете вместо него использовать методику, основанную на запросах
GET/POST, изменив значение всего одной переменной - $mode.
Переменная $mode определяет метод, который следует использовать в
качестве основного метода повторной генерации идентификатора сеанса. Эта
переменная может принимать значения cookie (установленное по умолчанию)
и get.
Буферизация страниц Класс Session предоставляет возможность управлять механизмом буферизации страниц. Переменная этого класса $allow_cache принимает значения no, private и public. Правила умолчания в данном случае зависят от конкретной версии библиотеки PHPLib. Скажем, в версии 7.2 значение этой переменной по умолчанию равно private, а в последующих версиях - no. Методика кэширования страниц, предлагаемая библиотекой PHPLib, очень похожа на механизм буферизации, предоставляемый функциями сеанса в PHP 4.0 Сериализация В системе PHP 3.0 сериализовать объекты сложно.
Функция serialize() неправильно сохраняет методы класса, а вручную
это сделать невозможно. В механизме поддержки объектов в программе PHP
3.0 отсутствовало одно важное средство - интроспекция. Нельзя было получить
имя класса и имя его родительского класса. Поэтому при разработке библиотеки
PHPLib надо было использовать обходные маневры. В классах библиотеки были
предусмотрены два дополнительных поля - $classname и $persistent_slots,
- которые соответственно содержали имя класса и переменных класса, подлежащих
сериализации. Зная имя класса, библиотека PHPLib имела возможность генерировать
код PHP, создающий экземпляр этого класса ($class = new class),
и сохранить этот код в банке данных сеанса. При повторном обращении к
данным сеанса указанная инструкция выполнялась с помощью функции eval().
Помните пример с самомодифицирующимся счетчиком из главы 2? В библиотека
PHPLib используется тот же самый принцип. Работа с сеансами Применение объекта сеанса библиотеки PHPLib, как правило, нисколько не сложнее, чем использование библиотеки управления сеансами пакета PHP 4.0. Это демонстрирует образец сценария, приведенный в листинге 6.3. В нем выполняются те же действия, что и в примере, который мы рассматривали в главе 4. Листинг 6.3. Простейший пример использования класса Session библиотеки PHPLib //явным образом создать экземпляр объекта $sess = new Example_Session; //начать сеанс $sess->start(); //зарегистрировать переменную сеанса $sess->register("counter"); //инициализировать счетчик if(!isset($counter)) { $counter = 0; } //вывести идентификатор сеанса и значение счетчик printf("Идентификатор сенса: %s Единственным существенным различием между этим
примером и примером из главы 4 является то, что в библиотеке PHPLib используется
объектно-ориентированный подход.
Чтобы изменить размер частей, на которые класс разбивает данные сеанса, можно использовать переменную $split_length, по умолчанию ее значение равно 4096 (4 Кбайт).
Переменные этого класса отличаются от переменных класса CT_Sql.
Если вы заглянете в файл local.inc, то увидите ряд определений классов. Три из них имеют непосредственное отношение к нашему примеру. class DB_Example extends DB_Sql { var $Host = "localhost"; var $Database= "phplib"; var $User = "tobias"; var $Password = "justdoit"; } class Example_CT_Sql extends CT_Sql{ var $database_class = "DB_Example"; ##к какой базе данных ##подключаться var $database_table = "active_sessions"; ##здесь следует ##искать наши данные } class Example_Session extends Sesion{ var $classname = "Example_Session"; var cookiename = ""; ##по умолчанию будет использоваться имя ##класса var $magic = "Hocuspocus"; ##исходное значение ##идентификатора var $mode = "cookie"; ##повторная генерация идентификатора ## сеанса производится через файл ##персональных настроек var $fallback_mode = "get"; var $lifetime = 0; ##0-работа с персональными ##настройками, иначе - время (в минутах), ##по истечении которого информация о сеансе ##считается устаревшей var $that_class = "Example_CT_Sql"; ##имя контейнера ##для хранения данных var $gc_probability = 5; } Как нетрудно видеть, между классами возникают определенные отношения: в классе Example_Session переменная $that_class получает имя класса Example_CT_Sql, а переменная $database класса Example_CT_Sql указывает на класс DB_Sql. Рис. 6.1 иллюстрирует эти отношения. Рис. 6.1. Модель отношений между классами DB_Sql, CT_Sql и Session В нашем примере использовались не сами базовые классы, но их расширенные версии, определенные в local.inc. Соответствующие зависимости представлены на рис. 6.2. Рис. 6.2. Зависимости между классами и их отношения в нашем примере Функция page_open() Предположим, вы разрабатываете большое приложение, в котором используются механизмы управления сеансами и абстрактного представления базы данных, а также функции аутентификации и определения прав доступа библиотеки PHPLib. Для этого вам требуется создать экземпляры объектов сеанса, аутентификации и разрешения на доступ. Ваш локальный файл local.inc будет выглядеть примерно так: $sess = new Session_Example; $sess->start(); $auth = new Auth_Example; $auth->start(); $perm = new Perm_Example; $user = new User_Example; $user->start(); Классы не являются независимыми друг от друга,
поэтому их нужно инициализировать в правильном порядке; нельзя создать
экземпляр объекта User_Example, если нет экземпляров объектов сеанса
и аутентификации. И это еще не все. Способ завершения программы зависит
от того, какой класс вы открыли первым; порядок, в котором вызываются
программы освобождения ресурсов, занятых классами, также имеет значение.
page_open(array("sess" => "Session_Example", "auth" => "Auth_Example", "perm" => "Perm_Example")); [?] page_close(); В этом примере функция page_open() создает
экземпляры классов Session_ Example, Auth_Example и Perm_Example
с именами $sess, $auth и $perm соответственно. Теперь
эти экземпляры можно использовать напрямую, например, при обращении к
функции $sess->register(). Функции purl(), url() и pself() Если в качестве основного режима повторной генерации
идентификатора сеанса ваше приложение использует cookie, а в качестве
запасного режима - get, надо ко всем ссылкам добавить идентификатор
сеанса. Если вы хотите использовать другие способы распространения идентификатора
сеанса, описанные в главе 4, необходимо расширить библиотеку PHPLib, чтобы
она могла работать с другими значениями переменной $mode. Аутентификация В соответствующем разделе главы 4 мы отмечали,
что методика базовой аутентификации HTTP имеет ряд недостатков, которые
отсутствуют в механизме аутентификации, предусмотренном в системе PHP.
В следующем разделе этот вопрос рассматривается более подробно.
Преимущества аутентификации средствами PHP
В данном разделе мы продолжим разговор об аутентификации
с того места, где остановились в главе 4. Мы уже рассмотрели недостатки
метода базовой аутентификации HTTP (HTTP Basic Authentication) и отметили
тот факт, что аутентификация на основе PHP этих недостатков лишена. Библиотека
PHPLib содержит сложные классы для обработки данных аутентификации пользователей
и управления правами доступа.
Пример использования класса Auth В примере, приведенном в листинге 6.4, вы встретитесь с заданным по умолчанию в библиотеке PHPLib видом экрана регистрации, или входа пользователя в систему. Именно так будет выглядеть экран регистрации при первом обращении к нему. Войдите в систему с заданными в библиотеке PHPLib по умолчанию именем пользователя и паролем (kris/test). По завершении аутентификации вы увидите идентификатор сеанса, ваше пользовательское имя и права доступа. Листинг 6.4. Простейший пример работы с экраном аутентификации page_open(array("sess" => "Session_Example", "auth" => "Auth_Example")); printf("Идентификатор сеанса: %s \n", $sess->id); printf("Идентификатор пользователя: %s Все страницы, использующие механизм аутентификации, предусмотренный в библиотеке PHPLib, придерживаются этой общей структуры. Сначала вызывается функция page_open(); остальная часть сценария будет выполняться только после входа пользователя в систему и завершения его аутентификации. Вы вполне можете быть уверены в том, что ни один пользователь, если он не прошел регистрацию, не увидит ничего, расположенного в тексте сценария после вызова функции page_open(). Написав всего одну строку кода, вы обеспечиваете в своем сценарии полную аутентификацию пользователя. После того как вы решили, какие классы вы хотите включить в приложение, библиотеку PHPLib использовать действительно просто. До сих пор во всех примерах мы применяли готовые классы, поставляемые в составе библиотеки PHPLib. Однако вам придется создавать свои собственные классы (производные от базовых), соответствующие вашим задачам. Для этого необходимо лучше разобраться в принципах работы библиотеки PHPLib. Внутренние переменные класса Auth Если вы пользуетесь ядром СУБД MySQL, таблица пользователей будет строиться по приведенной ниже схеме. CREATE TABLE auth_user{ user_id varchar(32) NOT NULL, username varchar(32) NOT NULL, password varchar(32) NOT NULL, perms varchar(255), PRIMARY KEY (user_id), UNIQUE k_username (username) }; Первичным ключом является поле user_id,
потому что внутренние процедуры библиотеки PHPLib работают именно с идентификационным
номером пользователя, а не с парой значений "имя пользователя - пароль"
(username/password). Пользовательский идентификатор (который
в библиотеке PHPLib называется uid) представляет собой уникальную
строку, аналогичную идентификатору сеанса, которая создается функциями
uniqid() и md5(), как показано ниже. Листинг 6.5. Расширение базового класса Auth require("EasyTemplate.inc.php3); class My_Auth extends Auth { var $classname = "My_Auth"; var $database_class = "DB_Example"; var $database_table = "auth_user"; function auth_lpginform() { //создать экземпляр шаблона $tpl = new EasyTemplate("loginform.inc.html"); //задано ли имя пользователя? Если да, это значит, //что первая попытка аутентификации не удалась if(isset($this->auth["uname"])) { $tpl->assign("USERNAME", $this->auth["uname"]); $tpl->assign("MESSAGE", "Либо имя, либо пароль заданы неверно. <br> Попробуйте еще раз!"); } else { $tpl->assign("USERNAME", ""); $tpl->assign("MESSAGE", "Пожалуйста, введите свое имя и пароль:"); } $tpl->assign("ACTION", $this->url()); //вывести результаты синтаксического анализа шаблона $tpl->easy_print(); } function auth_validatelogin() { //глобальные переменные формы global $username, $password; //если существует значение $username, запомнить его if(isset($username)) { $this->auth["uname"] = $username; } //установить значение $uid равным false по умолчанию $uid = false; //выбрать строки, соответствующие введенным //имени пользователя и паролю $query = " SELECT * FROM $this->database_table WHERE username = '$username' AND password = '$password' "; //выполнить запрос $this->db->query($query); //если возвращена одна строка, пользователь //считается прошедшим аутентификацию if($this->db->num_rows() == 1) { $this->db->next_record(); //задать $uid и массив $this->auth $uid = $this->db->Record["user_id"]; $this->auth["uid"] = $uid; $this->auth["uname"] = $this->db->Record["username"]; } return($uid); } } Две переменные класса Auth: $database_class
и $database_table - используются классом как внутренние для хранения
информации о сеансе и данных аутентификации. Они не влияют на процесс
аутентификации. Процедура регистрации обеспечивается двумя методами класса,
которые необходимо определить, - auth_ loginform() и auth_validatelogin().
Управление уровнями прав доступа Типичное приложение обычно имеет два уровня прав доступа: пользователя и администратора. Однако в некоторых приложениях необходим более сложный механизм управления доступом. Например, текстовые базы данных требуют множества уровней прав доступа.
Поскольку вы знаете текущего зарегистрированного
пользователя и можете идентифицировать его по уникальной строке ($uid),
то несложно написать функции, которые будут включать пользователя в определенную
группу и предоставлять ему те или иные права доступа в зависимости от
того, что это за группа. Библиотека PHPLib располагает встроенными функциями
для выполнения этих действий. Листинг 6.6. Работа с классом Perm page_open(array("sess" => " Example_Session", "auth" => "Example_Auth ", "perm" => "Example_Perm")); if(isset($mode) && $mode == "reload") { $auth->unauth(); print("Вы успешно вышли из системы. Поразрядные операции Поразрядные вычисления часто вызывают большие
затруднения у программистов-новичков, и даже опытные разработчики периодически
испытывают трудности при работе с ними. Установка значения бита Чтобы установить определенный бит, используется
операция включающей дизъюнкции (значение | значение). Допустим,
текущее значение флага $value равно 3 (это означает, что установлен
нулевой и первый биты), а вы хотите дополнительно установить второй бит
(не забывайте, что нумерация начинается с нуля). Тогда можно воспользоваться
операцией включающей дизъюнкции текущего значения флага с константой 4
(2 во второй степени): Переключение бита Переключение бита - установка его значения равным
1, если он был равен 0, и наоборот, - осуществляется операцией исключающей
дизъюнкции (value ^ value). Если значение равно 3, а мы
хотим переключить первый бит (который равен 1, поскольку в двоичной
системе счисления число 3 выглядит как 0000011), то следует написать:
Сброс бита Сброс бита осуществляется очень просто: сначала
нужно убедиться, что он включен, а затем обратить его. Это требует двух
поразрядных операций (value & ~value). Чтобы сбросить первый бит
в значении 3, можно использовать следующие строки: Проверка значения бита Чтобы проверить, установлен ли бит, используется
операция конъюнкции. Эта операция сравнивает два значения и возвращает
1 для тех позиций, где биты в обоих числах оказались равны 1.
Скажем, чтобы проверить, установлен ли первый бит в константе 3,
можно написать: Поразрядный сдвиг Для сдвига битов вправо или влево можно использовать
операторы сдвига >> и << (например, значение << количество_позиций).
Так, сдвиг значения 1 влево на одну позицию даст в результате двоичное
значение 10: Приоритет операторов Помните, что приоритет поразрядных операторов ниже, чем приоритет арифметических операторов. Операторы в выражении 1 + 2 | 3 будут выполняться в следующем порядке: (1 + 2) | 3. Кроме того, необходимо помнить, что поразрядные операторы имеют более низкий приоритет, чем операторы сравнения. Будьте внимательны и избегайте условий вроде if(2 & 3 != 0). Вместо проверки на то, установлен ли второй бит в значении 3 (а он установлен), в данном выражении сначала проверяется значение условия 3 != 0, а это, конечно, правда (true). В результате мы получаем выражение 2 & 1, возвращающее 0, а это совсем не то, что мы хотели. Пример Вернемся к примеру с увлечениями пользователя, который мы использовали выше. Предположим, что в программе определены четыре возможных увлечения: чтение, программирование, сочинительство и путешествия. В начале каждому хобби мы поставим в соответствие свой бит:
В двоичном виде эти значения представлены следующим образом: Чтение: 0 0 0 0 0 0 1 Программирование: 0 0 0 0 0 1 0 Сочинительство: 0 0 0 0 1 0 0 Путешествия: 0 0 0 1 0 0 0 Определить виды увлечений в тексте сценария можно обычным поразрядным сдвигом: define("HOBBY_READING":, 1 << 0); define("HOBBY_PROGRAMMING", 1 << 1); define("HOBBY_WRITING", 1 << 2); define("HOBBY_HIKING", 1 << 3); Если пользователь выбирает в качестве хобби сочинительство
и программирование, можно создать комбинацию битов при помощи оператора
| (включающая дизъюнкция): var $permissions = array( "user" =>1 "author" =>2 "editor" =>4 "supervisor" =>8 "admin" =>16 ); Права доступа переводятся в двоичные значения
внутренними функциями класса; при соответствующих вычислениях используются
конъюнкция и дизъюнкция. Значения в определенном выше ассоциативном массиве
определяют свою комбинацию битов для каждого уровня прав доступа. Хотя
все это выглядит достаточно сложно, использование комбинации битов очень
эффективно для представления подобных данных. В частности, этот механизм
дает возможность работать с уровнями, наследующими права доступа более
низких уровней. Если вы хорошо продумали соответствующие комбинации битов,
уровень admin может автоматически получить все привилегии уровня
user.
Суть этих вычислений заключается в том, что библиотека
PHPLib проверяет, установлен ли в комбинации разрядов, заданной в качестве
аргумента функции $perm-> have_perms(), бит, соответствующий правам
доступа данного пользователя. Такой механизм дает возможность определять
сложные комбинации прав доступа.
Чтобы вычислить комбинацию битов для каждой группы
пользователей, следует начать с нижнего уровня, author, с комбинации
1 (означающей, что установлен самый правый, нулевой бит). Если
бы мы хотели, чтобы редактор наследовал уровень автора, мы бы использовали
его комбинацию разрядов и установили следующий бит (1 | 2). Однако
в данном примере мы хотим разделить права этих двух групп, поэтому мы
задаем уровень editor как комбинацию разрядов, дающую значение
2 (двоичное 10).
Следующий, более высокий уровень (второй бит,
то есть третий справа) - это editor_in_chief, наследующий уровням
editor и author. Это означает, что в комбинации разрядов
уровня главного редактора должны быть установлены нулевой и первый биты,
а мы дополнительно устанавливаем второй бит (2 во второй степени, или
4): 1 | 2 | 4. В результате получаем 7.
Осталось рассмотреть уровень администратора, который также наследует всем низшим уровням. Следует использовать выражение 7 | 8 (константа 8, или 2 в третьей степени, используется для установления третьего бита). Результат равен 15. Проверяем:
Таким образом, наши права доступа определяются следующим образом: define("PHPLIB_PERM_AUTHOR", 1 | 0); define("PHPLIB_PERM_EDITOR", 1 | 0); define("PHPLIB_PERM_EDITOR_IN_CHIEF" 1 | 2 | 4); define("PHPLIB_PERM_ADMIN", 1 | 2 | 4 | 8); var $permissions = array( "author" => PHPLIB_PERM_AUTHOR, "editor" => PHPLIB_PERM_EDITOR, "editor_in_chief" =>PHPLIB_PERM_EDITOR_IN_CHIEF, "admin" => PHPLIB_PERM_ADMIN); Заключение В этой статье вы познакомились с основными классами
и способами применения библиотеки PHPLib. Она является мощным инструментом,
обеспечивающим решение многих проблем, которые неизбежно возникают при
создании Web-приложений. Этой библиотекой легко пользоваться, если вы
позаботитесь о том, чтобы предварительно создать на ее основе продуманный
механизм, отвечающий вашим потребностям. Библиотека PHPLib - прекрасный
каркас для реализации процедур управления всеми аспектами сеансов работы
пользователей и аутентификации. |
|
| ||||||||||||||||
|