function magicBuildPage($request) { $orderConnection = mappedConnection('orders'); $usersConnection = mappedConnection('users'); $query = $request['query']; // пагинация по айдишникам, что бы не было сдвигов/повторов при добавлении/удаленнии элементов в ленте $minId = array_key_exists('min_id', $query) ? (int) $query['min_id'] : null; $maxId = array_key_exists('max_id', $query) ? (int) $query['max_id'] : null; $pageSize = 10; // селектим на 1 элемент больше, // что бы понять, достигнут ли конец и не селектить count $pageSeizeInc = $pageSize + 1; $orders = loadOrders($orderConnection, $pageSeizeInc, $minId, $maxId); $isLastPage = count($orders) < $pageSeizeInc; // удаляем лишний элемент if (!$isLastPage) { array_pop($orders); } $userIds = []; foreach ($orders as $order) { if (($id = $order['created_by']) && !in_array($id, $userIds, true)) { $userIds[] = $id; } } $users = loadUsersByIds($usersConnection, $userIds); $vars = ['users' => $users, 'orders' => $orders, 'is_last_page' => $isLastPage]; if (array_key_exists('HTTP_ACCEPT', $request['server']) && strpos(strtolower($request['server']['HTTP_ACCEPT']), 'application/json') !== false) { return createResponse(json_encode($vars), 200, ['content-type' => 'application/json']); } else { $vars['form'] = createCreateForm(); return createResponse(render('orders/list.html.php', $vars)); } }
function getLastId() { $connection = mappedConnection('queue'); $sql = 'SELECT auto_increment FROM information_schema.tables WHERE table_name = \'queue\';'; $query = mysqli_query($connection, $sql); $result = mysqli_fetch_array($query, MYSQLI_NUM)[0]; mysqli_free_result($query); return $result - 1; }
function queueNotify($type, $data) { $connection = mappedConnection('queue'); $sql = 'INSERT INTO queue (event) VALUES (?);'; $statement = mysqli_prepare($connection, $sql); $value = serialize(['type' => $type, 'data' => $data]); mysqli_stmt_bind_param($statement, 's', $value); mysqli_stmt_execute($statement); mysqli_stmt_free_result($statement); }
<?php /** * Скрипт для обработки оторваных транзакций. * @todo механизм локов, что бы скрипт не мешал работе аппликейшна. */ define('SRC_DIR', __DIR__ . '/../src'); define('APP_DIR', __DIR__ . '/../app'); define('WEB_DIR', __DIR__ . '/../web'); define('TML_DIR', SRC_DIR . '/templates'); require_once SRC_DIR . '/database.php'; $userConnection = mappedConnection('users'); $orderConnection = mappedConnection('orders'); // если коннекты совпадают, то ничего делать не нужно, у нас общая транзакция. if ($userConnection === $orderConnection) { exit; } // получаем список текущих транзакций. // если не получилось, значит с одной из баз что-то не так, прерываем работу. $userResult = mysqli_query($userConnection, 'xa recover;'); if ($userResult) { $userRecover = mysqli_fetch_all($userResult); mysqli_free_result($userResult); } else { exit; } if ($orderResult = mysqli_query($orderConnection, 'xa recover;')) { $orderRecover = mysqli_fetch_all($orderResult); mysqli_free_result($orderResult); } else { exit;
<?php include_once __DIR__ . '/pay_form.php'; include_once SRC_DIR . '/forms.php'; include_once SRC_DIR . '/database.php'; function addCash($connection, $userId, $sum) { $sql = 'UPDATE users SET cash = cash + ' . $sum . ' WHERE id = ' . $userId . ';'; mysqli_query($connection, $sql); return (bool) mysqli_affected_rows($connection); } return function (array $request) { $form = createPayForm(); formHandleRequest($form, $request); if (formIsValid($form)) { $vars = extractValues($form); $sum = (int) ((double) $vars['pay'] * 100); $user = currentUser(); $userId = (int) $user['id']; $connection = mappedConnection('users'); addCash($connection, $userId, $sum); return createResponse(); } return createResponse('', 400); };
$sql = 'SELECT id, name, email, hashed_password FROM users WHERE email = ? LIMIT 1'; $statement = mysqli_prepare($connection, $sql); mysqli_stmt_bind_param($statement, 's', $email); mysqli_stmt_execute($statement); mysqli_stmt_bind_result($statement, $id, $name, $email, $hp); mysqli_stmt_fetch($statement); mysqli_stmt_close($statement); if (isset($id, $name, $email, $hp)) { return ['id' => $id, 'name' => $name, 'email' => $email, 'hashed_password' => $hp]; } return null; } return function (array $request) { if (currentUser()) { return createRedirectResponse('/index.php/'); } $form = createCreateForm(); formHandleRequest($form, $request); if (formIsValid($form)) { $auth = extractValues($form); $user = findUserByEmail(mappedConnection('users'), $auth['email']); if ($user && password_verify($auth['password'], $user['hashed_password'])) { authorize($user); return createRedirectResponse('/'); } else { $form['valid'] = false; $form['fields']['password']['errors'][] = 'Неверный Пароль'; } } return createResponse(render('session/new.html.php', ['form' => $form])); };
function deleteOrder($id, $ownerId) { $orderConnection = mappedConnection('orders'); $userConnection = mappedConnection('users'); $price = findOrderPriceById($orderConnection, $id, $ownerId); if (!$price) { return false; } $sqlOrders = 'UPDATE orders SET deleted = TRUE WHERE id = ' . (int) $id . ' AND done_by IS NULL AND deleted IS FALSE;'; $sqlUsers = 'UPDATE users SET cash = cash + ' . $price . ' WHERE id = ' . $ownerId . ';'; if ($orderConnection === $userConnection) { mysqli_begin_transaction($orderConnection); mysqli_query($orderConnection, $sqlOrders); if ($result = (bool) mysqli_affected_rows($orderConnection)) { mysqli_query($orderConnection, $sqlUsers); mysqli_commit($orderConnection); return true; } mysqli_rollback($orderConnection); return false; } else { $result = false; $uuid = uniqid('', false); $transactionOrder = '\'' . $uuid . '\', \'oi\''; $transactionUser = '******'' . $uuid . '\', \'ui\''; mysqli_query($userConnection, 'DO GET_LOCK(\'' . $uuid . '-u\', 1);'); mysqli_query($orderConnection, 'DO GET_LOCK(\'' . $uuid . '-o\', 1);'); // стартуем транзакцию на базе с заказами mysqli_query($orderConnection, 'XA START ' . $transactionOrder . ';'); // удаляем заказ mysqli_query($orderConnection, $sqlOrders); $completed = mysqli_affected_rows($orderConnection); mysqli_query($orderConnection, 'XA END ' . $transactionOrder . ';'); // если получилось удалить if ($completed) { mysqli_query($userConnection, 'XA START ' . $transactionUser . ';'); mysqli_query($userConnection, $sqlUsers); mysqli_query($userConnection, 'XA END ' . $transactionUser . ';'); // подготавиливаем обе транзакции if (mysqli_query($orderConnection, 'XA PREPARE ' . $transactionOrder . ';')) { if (mysqli_query($userConnection, 'XA PREPARE ' . $transactionUser . ';')) { // пытаемся закоммитить удаление заказа if (mysqli_query($orderConnection, 'XA COMMIT ' . $transactionOrder . ';')) { // плюсуем деньги, если в этот момет БД упала, то коммитим при поднятии. mysqli_query($userConnection, 'XA COMMIT ' . $transactionUser . ';'); $result = true; } else { // если что-то пошло не так, откатываем mysqli_query($userConnection, 'XA ROLLBACK ' . $transactionUser . ';'); } } else { mysqli_query($orderConnection, 'XA ROLLBACK ' . $transactionOrder . ';'); } } else { mysqli_query($userConnection, 'XA ROLLBACK ' . $transactionUser . ';'); } } else { // коммитим, изменений все равно нет. mysqli_query($orderConnection, 'XA COMMIT ' . $transactionOrder . ' ONE PHASE;'); } mysqli_query($userConnection, 'DO RELEASE_LOCK(\'' . $uuid . '-u\');'); mysqli_query($orderConnection, 'DO RELEASE_LOCK(\'' . $uuid . '-o\');'); return $result; } }
function completeOrder($id, $userId) { $id = (int) $id; $userId = (int) $userId; $userConnection = mappedConnection('users'); $orderConnection = mappedConnection('orders'); $price = findOrderPriceById($orderConnection, $id, $userId); if (!$price) { return false; } $commission = calculateCommission($price); $reward = $price - $commission; $sqlOrders = 'UPDATE orders SET done_by = ' . $userId . ', commission = ' . $commission . ' WHERE id = ' . $id . ' AND done_by IS NULL AND orders.deleted IS FALSE;'; $sqlUsers = 'UPDATE users SET cash = cash + ' . $reward . ' WHERE id = ' . $userId . ';'; if ($userConnection === $orderConnection) { mysqli_begin_transaction($orderConnection); mysqli_query($orderConnection, $sqlOrders); if ($result = (bool) mysqli_affected_rows($orderConnection)) { mysqli_query($orderConnection, $sqlUsers); mysqli_commit($orderConnection); return true; } mysqli_rollback($orderConnection); return false; } else { $uuid = uniqid('', false); // Лочим uuid, что бы скрипт поднятия оторваных транзакций не мешал ;) mysqli_query($userConnection, 'DO GET_LOCK(\'' . $uuid . '-u\', 1);'); mysqli_query($orderConnection, 'DO GET_LOCK(\'' . $uuid . '-o\', 1);'); $transactionOrder = '\'' . $uuid . '\', \'oc\''; $transactionUser = '******'' . $uuid . '\', \'uc\''; // стартуем транзакцию на базе с заказами mysqli_query($orderConnection, 'XA START ' . $transactionOrder . ';'); // помечаем заказ выполненым mysqli_query($orderConnection, $sqlOrders); $completed = mysqli_affected_rows($orderConnection); mysqli_query($orderConnection, 'XA END ' . $transactionOrder . ';'); $result = false; // если получилось пометить как выполненое if ($completed) { mysqli_query($userConnection, 'XA START ' . $transactionUser . ';'); mysqli_query($userConnection, $sqlUsers); mysqli_query($userConnection, 'XA END ' . $transactionUser . ';'); // подготавиливаем обе транзакции if (mysqli_query($orderConnection, 'XA PREPARE ' . $transactionOrder . ';')) { if (mysqli_query($userConnection, 'XA PREPARE ' . $transactionUser . ';')) { // пытаемся закоммитить изменения заказа if (mysqli_query($orderConnection, 'XA COMMIT ' . $transactionOrder . ';')) { // плюсуем деньги, если в этот момет БД упала, то коммитим при поднятии. mysqli_query($userConnection, 'XA COMMIT ' . $transactionUser . ';'); $result = true; } else { // если что-то пошло не так, пробуем откатить mysqli_query($userConnection, 'XA ROLLBACK ' . $transactionUser . ';'); } } else { mysqli_query($orderConnection, 'XA ROLLBACK ' . $transactionOrder . ';'); } } else { mysqli_query($userConnection, 'XA ROLLBACK ' . $transactionUser . ';'); } } else { // коммитим, изменений все равно нет. mysqli_query($orderConnection, 'XA COMMIT ' . $transactionOrder . ' ONE PHASE;'); } mysqli_query($userConnection, 'DO RELEASE_LOCK(\'' . $uuid . '-u\');'); mysqli_query($orderConnection, 'DO RELEASE_LOCK(\'' . $uuid . '-o\');'); return $result; } }
/** * @param string $name * @param string $description * @param int $price * @param int $ownerId * @return bool|int */ function createOrder($name, $description, $price, $ownerId) { $sql = 'INSERT INTO orders (title, description, created_by, price) VALUES (?, ?, ?, ?)'; $sqlUsers = 'UPDATE users SET cash = cash - ' . $price . ' WHERE id = ' . $ownerId . ' AND cash >= ' . $price . ';'; $orderConnection = mappedConnection('orders'); $userConnection = mappedConnection('users'); if ($orderConnection === $userConnection) { mysqli_begin_transaction($orderConnection); if (mysqli_query($userConnection, $sqlUsers) && mysqli_affected_rows($userConnection)) { $statement = mysqli_prepare($orderConnection, $sql); mysqli_stmt_bind_param($statement, 'ssdd', $name, $description, $ownerId, $price); mysqli_stmt_execute($statement); mysqli_commit($orderConnection); return mysqli_stmt_insert_id($statement); } else { mysqli_rollback($userConnection); return false; } } else { $result = false; $uuid = uniqid('', false); $transactionOrder = '\'' . $uuid . '\', \'oi\''; $transactionUser = '******'' . $uuid . '\', \'ui\''; mysqli_query($userConnection, 'DO GET_LOCK(\'' . $uuid . '-u\', 1);'); mysqli_query($orderConnection, 'DO GET_LOCK(\'' . $uuid . '-o\', 1);'); // стартуем транзацкцию на базе с юзерами. mysqli_query($userConnection, 'XA START ' . $transactionUser . ';'); // списываем деньги, если они есть mysqli_query($userConnection, $sqlUsers); $done = (bool) mysqli_affected_rows($userConnection); mysqli_query($userConnection, 'XA END ' . $transactionUser . ';'); // если списать деньги удалось, то пробуем опубликовать заказ if ($done) { // стартуем транзакцию на базе с заказами mysqli_query($orderConnection, 'XA START ' . $transactionOrder . ';'); // сейвим $statement = mysqli_prepare($orderConnection, $sql); mysqli_stmt_bind_param($statement, 'ssdd', $name, $description, $ownerId, $price); mysqli_stmt_execute($statement); $result = mysqli_stmt_insert_id($statement); mysqli_query($orderConnection, 'XA END ' . $transactionOrder . ';'); // подготавиливаем обе транзакции if (mysqli_query($userConnection, 'XA PREPARE ' . $transactionUser . ';')) { if (mysqli_query($orderConnection, 'XA PREPARE ' . $transactionOrder . ';')) { // пытаемся закоммитить списание денег if (mysqli_query($userConnection, 'XA COMMIT ' . $transactionUser . ';')) { // публикуем заказ, если в этот момет БД упала, то коммитим при поднятии. mysqli_query($orderConnection, 'XA COMMIT ' . $transactionOrder . ';'); } else { // если что-то пошло не так, откатываем mysqli_query($orderConnection, 'XA ROLLBACK ' . $transactionOrder . ';'); $result = false; } } else { mysqli_query($userConnection, 'XA ROLLBACK ' . $transactionUser . ';'); $result = false; } } else { mysqli_query($orderConnection, 'XA ROLLBACK ' . $transactionOrder . ';'); $result = false; } } else { // коммитим, изменений все равно нет. mysqli_query($userConnection, 'XA COMMIT ' . $transactionUser . ' ONE PHASE;'); } mysqli_query($userConnection, 'DO RELEASE_LOCK(\'' . $uuid . '-u\');'); mysqli_query($orderConnection, 'DO RELEASE_LOCK(\'' . $uuid . '-o\');'); return $result; } }
function mysql_session_destroy($sessionId) { $connection = mappedConnection('sessions'); $statement = mysqli_prepare($connection, 'DELETE FROM sessions WHERE id = ?;'); mysqli_stmt_bind_param($statement, 's', $sessionId); mysqli_stmt_execute($statement); mysqli_stmt_close($statement); return true; }