Upload
phpdevby
View
2.363
Download
8
Embed Size (px)
Citation preview
Сайт на Zend Framework в составе промышленной системы
Сергей Морозов, Минск 2012
Сайты бывают разные
Отдельно стоящий сайт
• БД принадлежит только
сайту.
• Неограниченные
привилегии.
• Сайт разграничивает
права пользователей.
База данных
Бизнес-логика
Представление
Внешение ресурсы
Сайт в составе
промышленной системы
• БД обслуживает
несколько приложений
• Повышенные требова-
ния к безопасности
• Максимально
ограниченные права
База данных
Коммутатор
Биллинг
Инженерное ПО
Сервер
клиентских приложений
Сайт
Разграничение прав доступа
средствами БД
• Базы
данных
• Таблицы
• Поля
Чего не хватает?
• Разграничения на
доступ к записям
• Прочих
ограничений,
обусловленных
бизнес-логикой
Ну например?
Если у пользователя есть права на выполнение запроса SELECT id FROM users WHERE login = :login AND password = SOME_SECRET_ALG(:password, :salt) ,
значит он может выполнить и SELECT id, login, password FROM users
Как быть?
• Запретить доступ к таблицам напрямую
• Предоставить абстрактный интерфейс в виде
пакетов хранимых процедур
Как будто ООП
Таблицы
БАЗА ДАННЫХ
Пакет 1
процедура_1_1
процедура_1_2
Пакет 2
процедура_2_1
процедура_2_2
Данные
ПРИЛОЖЕНИЕ
Класс 1
метод_1_1
метод_1_2
Класс 2
метод _2_1
метод _2_2
Преимущества подхода
• Централизованное
разграничение прав
доступа
• Инкапсуляция
• Разделение труда
Недостатки подхода
• Бюрократия
• Сложность
реализации
• Отсутствие чѐтких
границ
Как выглядит код процедуры?
CREATE PROCEDURE AUTHENTICATE(
psUsername IN VARCHAR2,
psPassword IN VARCHAR2,
viAccount_id OUT PLS_INTEGER,
viStatus OUT VARCHAR2
)
BEGIN
SELECT
fiAccount_id,
fiStatus
INTO
viAccount_id,
viStatus
FROM
users
WHERE
fsUsername = psUsername
AND
fsPassword = SOME_SECRET_ALG(psPassword, 'some salt')
END;
Как выглядит вызов
процедуры?
<?php $query = <<<QUERY BEGIN AUTH.AUTHENTICATE(:psUsername, :psPassword, :piAccount_id, :piStatus); END QUERY; $stmt = oci_parse($conn, $query); oci_bind_by_name($stmt, ':psUsername', $username, 32, SQLT_CHR); oci_bind_by_name($stmt, ':psPassword', $password, 32, SQLT_CHR); oci_bind_by_name($stmt, ':piAccount_id', $account_id, -1, SQLT_INT); oci_bind_by_name($stmt, ':piStatus', $status, -1, SQLT_INT); oci_execute($stmt);
Как выглядит код функции?
CREATE TYPE TR_ACCOUNT IS RECORD (
fiUser_id NUMBER(10),
fsCurrency_code VARCHAR2(3),
fxBalance NUMBER(10,2)
);
CREATE FUNCTION GET_ACCOUNT_INFO (
piAccount_id NUMBER
) RETURN TR_ACCOUNT IS
BEGIN
/* ... */
END;
Как выглядит вызов функции?
<?php
$query = <<<QUERY
DECLARE
vrAccount BILLING.ACCOUNT;
BEGIN
vrAccount := BILLING.GET_ACCOUNT_INFO(:piAccount_id);
:piUser_id := vrAccount.fiUser_id;
:psCurrency_code := vrAccount.psCurrency_code;
:pxBalance := vrAccount.pxbalance;
END;
QUERY;
$stmt = oci_parse($conn, $query);
oci_bind_by_name($stmt, ':piAccount_id', $account_id, -1, SQLT_INT);
oci_bind_by_name($stmt, ':piUser_id', $user_id, -1, SQLT_INT);
oci_bind_by_name($stmt, ':psCurrency_code', $currency_code, 3, SQLT_CHR);
oci_bind_by_name($stmt, ':pxBalance', $balance, 22, SQLT_LNG);
oci_execute($stmt);
$result = array(
'account_id' => $account_id,
'user_id' => $user_id,
'currency_code' => $currency_code,
'balance' => $balance,
);
Как выглядит код функции?
CREATE TYPE TN_ACCOUNTS IS TABLE OF TR_ACCOUNT;
CREATE FUNCTION GET_ACCOUNTS(
piUser_id NUMBER DEFAULT NULL
) RETURN TN_ACCOUNTS PIPELINED IS
BEGIN
/* ... */
END;
Как выглядит вызов функции?
<?php $query = <<<QUERY SELECT * FROM TABLE(BILLING.GET_ACCOUNTS(:piUser_id)) END; QUERY; $stmt = oci_parse($conn, $query); oci_bind_by_name($stmt, ':piUser_id', $user_id, -1, SQLT_INT); oci_execute($stmt); oci_fetch_all($stmt, $result);
Необходимо определить: 1. Наименования функций и
процедур 2. Наименования и типы
аргументов 3. Типы возвращаемых
значений 4. Структуру табличных
типов
Рутину нужно
автоматизировать
Как получить описание
интерфейса в Oracle?
SELECT
PACKAGE_NAME, OBJECT_NAME, POSITION,
DATA_LEVEL, ARGUMENT_NAME, DATA_TYPE,
DATA_LENGTH, DEFAULTED, IN_OUT, TYPE_NAME,
TYPE_SUBNAME
FROM
USER_ARGUMENTS
WHERE
PACKAGE_NAME = 'MY_PACKAGE'
ORDER BY
PACKAGE_NAME, OBJECT_NAME, SEQUENCE, POSITION,
DATA_LEVEL;
Обход графа объектов
Package
Procedures
Types
GET_ACCOUNT
ptrResult
piAccount_id
TR_ACCOUNT
S GET_ACCOUNTS
ptnResult
piUser_id
TN_ACCOUNTS
S UPDATE_ACCOUNT
TR_ACCOUNT
TR_ACCOUNT
fiUser_id
fsCurrency_code
fxBalance
TN_ACCOUNTS
Разбор результатов P
AC
KA
GE
_N
AM
E
OB
JE
CT
_N
AM
E
PO
SIT
ION
DA
TA
_L
EV
EL
AR
GU
ME
NT
_N
A
ME
DA
TA
_T
YP
E
DA
TA
_L
EN
GT
H
DE
FA
UL
TE
D
IN_O
UT
TY
PE
_N
AM
E
TY
PE
_S
UB
NA
ME
BILLING CREATE_NUMBER 1 0 ACCOUNT_ID BINARY_INTEGER N IN
BILLING CREATE_NUMBER 2 0 PLAN_ID BINARY_INTEGER N IN
BILLING CREATE_NUMBER 3 0 NUMBER BINARY_INTEGER N IN
BILLING CREATE_NUMBER 4 0 BEGIN_TIME DATE N IN
BILLING CREATE_NUMBER 5 0 PRODUCT_ID NUMBER 22 N OUT
BILLING CREATE_NUMBER 6 0 LOGIN VARCHAR2 N OUT
BILLING CREATE_NUMBER 7 0 PASSWORD VARCHAR2 N OUT
Разбор результатов
<?php class Billing { public function createNumber($account_id, $plan_id, $number, $begin_time, &$product_id, &$login, &$password) { /* implementation goes here */ } }
PA
CK
AG
E_N
AM
E
OB
JE
CT
_N
A
ME
PO
SIT
ION
DA
TA
_L
EV
EL
AR
GU
ME
NT
_
NA
ME
DA
TA
_T
YP
E
DA
TA
_L
EN
G
TH
DE
FA
UL
TE
D
IN_O
UT
TY
PE
_N
AM
E
TY
PE
_S
UB
N
AM
E
BILLING GET_NUMBERS 0 0 TABLE N OUT BILLIN
G
TN_NUMBE
RS
BILLING GET_NUMBERS 1 1 PL/SQL RECORD N OUT BILLIN
G
TR_NUMBE
R
BILLING GET_NUMBERS 1 2 NUMBER_ID NUMBER 22 N OUT
BILLING GET_NUMBERS 2 2 NUMBER VARCHAR2 20 N OUT
BILLING GET_NUMBERS 3 2 BEAUTY_RATING_ID NUMBER 22 N OUT
BILLING GET_NUMBERS 4 2 BEAUTY_RATING VARCHAR2 20 N OUT
BILLING GET_NUMBERS 5 2 CATEGORY_ID NUMBER 22 N OUT
BILLING GET_NUMBERS 1 0 PREFIX VARCHAR2 Y IN
BILLING GET_NUMBERS 2 0 BEAUTY_RATING_ID NUMBER 22 Y IN
BILLING GET_NUMBERS 3 0 CATEGORY_ID NUMBER 22 Y IN
Разбор результатов
Разбор результатов
<?php class Billing { public function getNumbers($prefix,
$beauty_rating_id = null, $category_id = null) { /* implementation goes here */ } }
• Один класс для пакета — «Фасад»
• По одному классу для каждой процедуры
• Ленивая загрузка (Zend_Loader_PluginLoader).
• Дополнительные плагины
Что получается в итоге?
Когда уже будет про Zend
Framework?
• Zend_Tool_*
• My_Tool_Provider_DbIntf
• Zend_Application_Resource_Multidb
• My_Application_Resource_DbIntf
• Zend_CodeGenerator_*
Но не всѐ так просто
• Необязательные аргументы В зависимости от количества переданных
аргументов нужно формировать
соответствующий ему код SQL-запроса.
• Передача переменных по ссылке Перегрузка методов в PHP не позволяет
передавать переменные по ссылке, поэтому
для таких методов перегрузку использовать
нельзя.
Повторное использование
запросов
<?php
function get_users(Zend_Db_Select $select = null) { if (null === $select) { $select = Zend_Db_Select(); } return $select ->from('users') ->query() ->fetchAll(); }
• Обработка исключительных ситуаций:
• Код ошибки
• Маркер: поле, индекс, триггер и т. д.
• Форматирование и разбор дат
• Репликация master-slave
Что ещѐ можно
автоматизировать?
Переход на PostgreSQL
• По-другому получаем описание
интерфейса
• Другой синтаксис вызова процедур
• Описание табличных типов в конечном
итоге не нужно
Численные показатели
• Сроки
– Две недели на прототип идеи
– Полтора года активной разработки
– Две недели на переход на PostgreSQL* * только вызовы процедур
• Код
– Написано 621 NCLOC
– Сгенерировано 7415 NCLOC* * для 150-ти процедур
Конец