PHP offers three different APIs to connect to MySQL. Below we show the APIs provided by the mysql, mysqli, and PDO extensions. Each code snippet creates a connection to a MySQL server running on "example.com" using the username "user" and the password "password". And a query is run to greet the user.
Example #1 Comparing the three MySQL APIs
// mysqli
$mysqli
= new
mysqli
("example.com"
,
"user"
,
"password"
,
"database"
);
$result
=
$mysqli
->
query
();
$row
=
$result
->
fetch_assoc
();
// PDO
$pdo
= new
PDO
("mysql:host=example.com;dbname=database"
,
"user"
,
"password"
);
$statement
=
$pdo
->
query
("SELECT "Hello, dear MySQL user!" AS _message FROM DUAL"
);
$row
=
$statement
->
fetch
(PDO
::
FETCH_ASSOC
);
echo
htmlentities
($row
[
"_message"
]);
// mysql
$c
=
mysql_connect
("example.com"
,
"user"
,
"password"
);
mysql_select_db
("database"
);
$result
=
mysql_query
("SELECT "Hello, dear MySQL user!" AS _message FROM DUAL"
);
$row
=
mysql_fetch_assoc
($result
);
echo
htmlentities
($row
[
"_message"
]);
?>
Recommended API
It is recommended to use either the mysqli or PDO_MySQL extensions. It is not recommended to use the old mysql extension for new development, as it was deprecated in PHP 5.5.0 and was removed in PHP 7. A detailed feature comparison matrix is provided below. The overall performance of all three extensions is considered to be about the same. Although the performance of the extension contributes only a fraction of the total run time of a PHP web request. Often, the impact is as low as 0.1%.
Feature comparison
ext/mysqli | PDO_MySQL | ext/mysql | |
---|---|---|---|
PHP version introduced | 5.0 | 5.1 | 2.0 |
Included with PHP 5.x | Yes | Yes | Yes |
Included with PHP 7.x | Yes | Yes | No |
Development status | Active | Active | Maintenance only in 5.x; removed in 7.x |
Lifecycle | Active | Active | Deprecated in 5.x; removed in 7.x |
Recommended for new projects | Yes | Yes | No |
OOP Interface | Yes | Yes | No |
Procedural Interface | Yes | No | Yes |
API supports non-blocking, asynchronous queries with mysqlnd | Yes | No | No |
Persistent Connections | Yes | Yes | Yes |
API supports Charsets | Yes | Yes | Yes |
API supports server-side Prepared Statements | Yes | Yes | No |
API supports client-side Prepared Statements | No | Yes | No |
API supports Stored Procedures | Yes | Yes | No |
API supports Multiple Statements | Yes | Most | No |
API supports Transactions | Yes | Yes | No |
Transactions can be controlled with SQL | Yes | Yes | Yes |
Supports all MySQL 5.1+ functionality | Yes | Most | No |
У меня часто спрашивают, как работать с API . Наиболее, популярной задачей, связанной с API , является - работа с API ВКонтакте . В данной статье я покажу, как работать с API ВКонтакте , а, главное, покажу Вам, что все API работают по единому принципу .
Вот порядок действий , который Вам необходимо сделать, чтобы начать работать с любым API :
- Найти документацию по нужному API .
- Посмотреть примеры подключения к API . Тут могут быть разные варианты. Например, на одном сервисе потребуют секретный ключ, выдаваемый после регистрации. На втором сервисе, все запросы не требуют секретных ключей. На третьем сервисе, есть и с секретным ключом, и без варианты работы. Иногда, секретный ключ называют токеном .
- Скопировать простой готовый код с сайта документации и проверить его работу. Если не работает, то ищите ошибку у себя, так как едва ли она будет у сервиса.
- Найти в документации метод , который решает Вашу задачу.
- Используя примеры и описание метода, отправить правильный запрос к API .
- В зависимости от формата ответа, разобрать его на "составные части ", а дальше уже делать то, что требуется - вывести пользователю, сохранить в файл, отправить в базу данных и тому подобное.
А в качестве примера следования данной инструкции мы разберём API ВКонтакте . Итак:
- Ссылка на документацию .
- В данном случае есть методы, которые общедоступны, а потому не требуют токена, а есть методы, требующие его.
- В данном случае, примеров на конкретном языке я не нашёл. Возможно, они есть, но явно не на виду. Обычно же, у API для каждого метода есть примеры на разных языках.
- Мы хотим вывести 5 последних записей со стены конкретного пользователя, а затем вывести их у себя на сайте . Нужный нам метод .
Теперь мы должны отправить запрос к API , используя описание метода. Делать мы это будем через PHP :
$wall = file_get_contents("https://api.vk.com/method/wall.get?v=5.3&filter=others&domain=myrusakov&count=5");
print_r($wall);
?>
В данном случае, я использую API 5.3 (v=5.3 ), вывожу все записи независимо от автора (filter=others ) со своей страницы (domain=myrusakov ) в количестве 5 штук (count=5 ). Думаю, что здесь всё очень прозрачно.
Мы получили ответ в формате JSON , и теперь нам нужно перейти к последнему пункту - разобрать ответ на "составные части ". Дальше мы с Вами выведем записи со стены в более-менее читабельном виде на странице нашего сайта. Результирующий PHP-код :
$wall = file_get_contents("http://api.vk.com/method/wall.get?v=5.3&filter=others&domain=myrusakov&count=5"); // Отправляем запрос
$wall = json_decode($wall); // Преобразуем JSON-строку в массив
$wall = $wall->response->items; // Получаем массив комментариев
for ($i = 0; $i < count($wall); $i++) {
echo "
".($i + 1).". ".$wall[$i]->text."
".date("Y-m-d H:i:s", $wall[$i]->date)."
}
?>
Как видите, всё очень просто. Самая сложная часть - разобрать на составные элементы. Я предпочитаю выводить результат от API через print_r , а уже затем писать обработку этого. Хотя можно посмотреть пример ответа в описании метода, а дальше уже написать парсер.
Разрабатывая проект, я столкнулся с необходимостью организации клиент-серверного взаимодействия приложений на платформах iOS и Android с моим сайтом на котором хранилась вся информация - собственно БД на mysql, картинки, файлы и другой контент.
Задачи которые нужно было решать - достаточно простые:
регистрация/авторизация пользователя;
отправка/получение неких данных (например список товаров).
И тут-то мне захотелось написать свой API для взаимодействия с серверной стороной - большей своей частью для практического интереса.
Входные данные
В своем распоряжении я имел:
Сервер - Apache, PHP 5.0, MySQL 5.0
Клиент - Android, iOS устройства, любой браузер
Я решил, что для запросов к серверу и ответов от него буду использовать JSON формат данных - за его простоту и нативную поддержку в PHP и Android. Здесь меня огорчила iOS - у нее нет нативной поддержки JSON (тут пришлось использовать стороннюю разработку).
Так же было принято решение, что запросы можно будет отсылать как через GET так и через POST запросы (здесь помог $_REQUEST в PHP). Такое решение позволило проводить тестирование API через GET запросы в любом доступном браузере.
Внешний вид запросов решено было сделать таким:
http://[адрес сервера]/[путь к папке api]/?[название_api].[название_метода]=
Путь к папке api - каталог на который нужно делать запросы, в корне которого лежит файл index.php - он и отвечает за вызов функций и обработку ошибок
Название api - для удобства я решил разделить API группы - пользователь, база данных, конент и тд. В таком случае каждый api получил свое название
Название метода - имя метода который нужно вызвать в указанном api
JSON - строковое представление JSON объекта для параметров метода
Скелет API
Скелет API на серверной стороне состоит из нескольких базовых классов:
index.php - индексный файл каталога в Apache на него приходятся все вызовы API, он осуществляет парсинг параметров и вызов API методов
MySQLiWorker - класс-одиночка для работы с базой MySQL через MySQLi
apiBaseCalss.php - родительский класс для всех API в системе - каждый API должен быть наследован от этого класса для корректной работы
apiEngine.php - основной класс системы - осуществляет разбор переданных параметров (после их предварительного парсинга в index.php) подключение нужного класса api (через require_once метод), вызов в нем нужного метода и возврат результата в JSON формате
apiConstants.php - класс с константами для api вызовов и передачи ошибок
apitest.php - тестовый api для тестирования новых методов перед их включением в продакшн версию
Весь механизм выглядит следующим образом:
Мы делаем запрос на сервер - к примеру www.example.com/api/?apitest.helloWorld= {}
На серверной стороне файл index.php - производит парсинг переданных параметров. Index.php берет всегда только первый элемент из списка переданных параметров $_REQUEST - это значит что конструкция вида www.example.com/api/?apitest.helloWorld= {}&apitest.helloWorld2 - произведет вызов только метода helloWorld в apitest. Вызова же метода helloWorld2 непроизойдет
Теперь подробней о каждом
Я попробовал достаточно документировать файлы, чтобы не занимать много место под текст. Однако в тех файлах где нет комментариев, я все ж приведу описание.
Index.php
Как уже говорил раньше это входной индексный файл для Apache а значит все вызовы вида www.example.com/api будет принимать он.
0){ require_once "apiEngine.php"; foreach ($_REQUEST as $apiFunctionName => $apiFunctionParams) { $APIEngine=new APIEngine($apiFunctionName,$apiFunctionParams); echo $APIEngine->callApiFunction(); break; } }else{ $jsonError->error="No function called"; echo json_encode($jsonError); } ?>
Первым делом устанавливаем тип контента - text/html (потом можно сменить в самих методах) и кодировку - UTF-8.
Дальше проверяем, что у нас что-то запрашивают. Если нет то выводим JSON c ошибкой.
Если есть параметры запроса, то подключаем файл движка API - apiEngine.php и создаем класс движка с переданными параметрами и делаем вызов api метода.
Выходим из цикла так как мы решили что будем обрабатывать только один вызов.
apiEngine.php
Вторым по важности является класс apiEngine - он представляет собой движок для вызова api и их методов.
apiFunctionParams = stripcslashes($apiFunctionParams);
//Парсим на массив из двух элементов - название API, - название метода в API
$this->apiFunctionName = explode("_", $apiFunctionName);
}
//Создаем JSON ответа
function createDefaultJson() {
$retObject = json_decode("{}");
$response = APIConstants::$RESPONSE;
$retObject->$response = json_decode("{}");
return $retObject;
}
//Вызов функции по переданным параметрам в конструкторе
function callApiFunction() {
$resultFunctionCall = $this->createDefaultJson();//Создаем JSON ответа
$apiName = strtolower($this->apiFunctionName);//название API проиводим к нижнему регистру
if (file_exists($apiName . ".php")) {
$apiClass = APIEngine::getApiEngineByName($apiName);//Получаем объект API
$apiReflection = new ReflectionClass($apiName);//Через рефлексию получем информацию о классе объекта
try {
$functionName = $this->apiFunctionName;//Название метода для вызова
$apiReflection->getMethod($functionName);//Провераем наличие метода
$response = APIConstants::$RESPONSE;
$jsonParams = json_decode($this->apiFunctionParams);//Декодируем параметры запроса в JSON объект
if ($jsonParams) {
if (isset($jsonParams->responseBinary)){//Для возможности возврата не JSON, а бинарных данных таких как zip, png и др. контетнта
return $apiClass->$functionName($jsonParams);//Вызываем метод в API
}else{
$resultFunctionCall->$response = $apiClass->$functionName($jsonParams);//Вызыаем метод в API который вернет JSON обект
}
} else {
//Если ошибка декодирования JSON параметров запроса
$resultFunctionCall->errno = APIConstants::$ERROR_ENGINE_PARAMS;
$resultFunctionCall->error = "Error given params";
}
} catch (Exception $ex) {
//Непредвиденное исключение
$resultFunctionCall->error = $ex->getMessage();
}
} else {
//Если запрашиваемый API не найден
$resultFunctionCall->errno = APIConstants::$ERROR_ENGINE_PARAMS;
$resultFunctionCall->error = "File not found";
$resultFunctionCall->REQUEST = $_REQUEST;
}
return json_encode($resultFunctionCall);
}
}
?>
apiConstants.php
Данный класс используется только для хранения констант.
MySQLiWorker.php
Класс-одиночка для работы с базой. В прочем это обычный одиночка - таких примеров в сети очень много.
dbName = $dbName; self::$instance->dbHost = $dbHost; self::$instance->dbUser = $dbUser; self::$instance->dbPassword = $dbPassword; self::$instance->openConnection(); } return self::$instance; } //Определяем типы параметров запроса к базе и возвращаем строку для привязки через ->bind function prepareParams($params) { $retSTMTString = ""; foreach ($params as $value) { if (is_int($value) || is_double($value)) { $retSTMTString.="d"; } if (is_string($value)) { $retSTMTString.="s"; } } return $retSTMTString; } //Соединяемся с базой public function openConnection() { if (is_null($this->connectLink)) { $this->connectLink = new mysqli($this->dbHost, $this->dbUser, $this->dbPassword, $this->dbName); $this->connectLink->query("SET NAMES utf8"); if (mysqli_connect_errno()) { printf("Подключение невозможно: %s\n", mysqli_connect_error()); $this->connectLink = null; } else { mysqli_report(MYSQLI_REPORT_ERROR); } } return $this->connectLink; } //Закрываем соединение с базой public function closeConnection() { if (!is_null($this->connectLink)) { $this->connectLink->close(); } } //Преобразуем ответ в ассоциативный массив public function stmt_bind_assoc(&$stmt, &$out) { $data = mysqli_stmt_result_metadata($stmt); $fields = array(); $out = array(); $fields = $stmt; $count = 1; $currentTable = ""; while ($field = mysqli_fetch_field($data)) { if (strlen($currentTable) == 0) { $currentTable = $field->table; } $fields[$count] = &$out[$field->name]; $count++; } call_user_func_array("mysqli_stmt_bind_result", $fields); } } ?>
apiBaseClass.php
Ну вот мы подошли к одному из самых важных классов системы - базовый класс для всех API в системе.
mySQLWorker = MySQLiWorker::getInstance($dbName,$dbHost,$dbUser,$dbPassword); } } function __destruct() { if (isset($this->mySQLWorker)){ //Если было установленно соединение с базой, $this->mySQLWorker->closeConnection(); //то закрываем его когда наш класс больше не нужен } } //Создаем дефолтный JSON для ответов function createDefaultJson() { $retObject = json_decode("{}"); return $retObject; } //Заполняем JSON объект по ответу из MySQLiWorker function fillJSON(&$jsonObject, &$stmt, &$mySQLWorker) { $row = array(); $mySQLWorker->stmt_bind_assoc($stmt, $row); while ($stmt->fetch()) { foreach ($row as $key => $value) { $key = strtolower($key); $jsonObject->$key = $value; } break; } return $jsonObject; } } ?>
Как видно данный класс содержит в себе несколько «утилитных» методов, таких как:
конструктор в котором осуществляется соединение с базой, если текущее API собирается работать с базой;
деструктор - следит за освобождением ресурсов - разрыв установленного соединения с базой
createDefaultJson - создает дефолтный JSON для ответа метода
fillJSON - если подразумевается что запрос вернет только одну запись, то данный метод заполнит JSON для ответа данными из первой строки ответа от БД
Создадим свой API
Вот собственно и весь костяк этого API. Теперь рассмотрим как же это все использовать на примере создания первого API под названием apitest. И напишем в нем пару простых функций:
одну без параметров
одну с параметрами и их же она нам и вернет, чтобы было видно, что она их прочитала
одну которая вернет нам бинарные данные
И так создаем класс apitest.php следующего содержания
createDefaultJson(); $retJSON->withoutParams = "It\"s method called without parameters"; return $retJSON; } //http://www.example.com/api/?apitest.helloAPIWithParams={"TestParamOne":"Text of first parameter"} function helloAPIWithParams($apiMethodParams) { $retJSON = $this->createDefaultJson(); if (isset($apiMethodParams->TestParamOne)){ //Все ок параметры верные, их и вернем $retJSON->retParameter=$apiMethodParams->TestParamOne; }else{ $retJSON->errorno= APIConstants::$ERROR_PARAMS; } return $retJSON; } //http://www.example.com/api/?apitest..jpg"); } } ?>
Для удобства тестирования методов, я дописываю к ним адрес по которому я могу сделать быстрый запрос для тестирования.
И так у нас три метода
Function helloAPI() { $retJSON = $this->createDefaultJson(); $retJSON->withoutParams = "It\"s method called without parameters"; return $retJSON; }
Это простой метод без параметров. Его адрес для GET вызова www.example.com/api/?apitest.helloAPI= {}
Результатом выполнения будет вот такая страница (в браузере)
Этот метод принимает в параметры. Обязательным является TestParamOne, для него и сделаем проверку. Его его не передать, то будет выдан JSON с ошибкой
Function helloAPIWithParams($apiMethodParams) {
$retJSON = $this->createDefaultJson();
if (isset($apiMethodParams->TestParamOne)){
//Все ок параметры верные, их и вернем
$retJSON->retParameter=$apiMethodParams->TestParamOne;
}else{
$retJSON->errorno= APIConstants::$ERROR_PARAMS;
}
return $retJSON;
}
Результат выполнения
helloAPIResponseBinary
И последний метод helloAPIResponseBinary - вернет бинарные данные - картинку хабра о несуществующей странице (в качестве примера)
function helloAPIResponseBinary($apiMethodParams){
header("Content-type: image/jpeg");
echo file_get_contents("http://сайт/i/error-404-monster.jpg");
}
Как видно - здесь есть подмена заголовка для вывода графического контента.
Результат будет такой