Прежде чем приступать к написанию кода, нам нужно решить, что именно такого полезного мы можем создать, какие функции будет выполнять библиотека. Довольно популярным является написание библиотек, упрощающих работу с тем или иным API. Зачастую API представляют из себя большой список различных методов, работающих не только через GET method http-протокола. И это доставляет сложность при работе с ним у программистов: нужно постоянно учитывать все нюансы обращения к методу, его ответа, а еще может присутствовать аутентификация при работе и тд.
Гораздо проще иметь готовый класс или группу классов, через которые можно легко объектно-ориентированно работать, не сильно заботясь обо всех этих нюансах, к тому же современные IDE умеют подсказывать и подсвечивать, когда у вас есть такой класс, а стороннего API они никак не знают и не подскажут. Поэтому написание библиотеки-прослойки между API и использующим его приложением так популярно.
Наша основная цель научиться создавать и публиковать lib для composer, а не реализация крутого и огромного решения, поэтому я выбрал API, которое уже имеет официальную php client library, но все же его вполне можно дополнить, и тем самым упростить использование. Я говорю о Почте от Google: Gmail API. Если перейти на официальную страницу настройки и использования этого API https://developers.google.com/gmail/api/quickstart/php, то мы увидим, что она предполагает все же некоторую подготовительную работу прежде, чем можно будет приступить к непосредственной работе с почтой.
Посмотрим на листинг в пункте "Step 2: Set up the sample", там мы видим подключение composer зависимостей, затем функцию getClient(), которая создает подключение к сервису почты, и только после нее идет пример кода работы с почтовыми лейблами. Как минимум эту функцию мы можем вынести за скобки нашего основного веб-приложения в библиотеку, которую сделать доступной для установки на любой проект, где понадобится работа с Gmail. Конечно, в реальном мире разработки создавать библиотеку только ради упрощения одной функции идея не самая лучшая, но мы учимся, к тому же вполне вероятно, что во время работы мы войдем во вкус и придумаем, что еще можно упростить, ускорить и вынести в библиотеку кроме этой функции настройки и подключения.
Создание библиотеки
Этот пример еще хорош и тем, что мы увидим далее, как настроить дополнительную зависимость нашего решения от сторонних библиотек. Что ж приступим, создаем директорию для будущего расширения и инициализируем composer проект:
mkdir gmail && cd gmail
composer init
Composer предлагает нам ввести данные о создаваемом пакете:
Welcome to the Composer config generator
This command will guide you through creating your composer.json config.
Package name (<vendor>/<name>) [zhukmax/gmail]:
Description []: Package for easy using Gmail Api
Author [ZhukMax <zhukmax@ya.ru>, n to skip]:
Minimum Stability []: dev
Package Type (e.g. library, project, metapackage, composer-plugin) []: library
License []: Apache-2.0
Define your dependencies.
Would you like to define your dependencies (require) interactively [yes]? no
Would you like to define your dev dependencies (require-dev) interactively [yes]? no
{
"name": "zhukmax/gmail",
"description": "Package for easy using Gmail Api",
"type": "library",
"license": "Apache-2.0",
"authors": [
{
"name": "ZhukMax",
"email": "zhukmax@ya.ru"
}
],
"minimum-stability": "dev",
"require": {}
}
Do you confirm generation [yes]?
Include the Composer autoloader with: require 'vendor/autoload.php';
Обратим внимание на название пакета (Package name), оно должно быть уникальным для того, чтобы можно было разместиться в packagist. Так же мы видим, что composer в квадратных скобках [ ] предлагает ответ на свой же вопрос, и если нас он устраивает, то просто нажимаем на клавишу Enter, иначе после двоеточия пишем свой вариант. Для лицензии проекта я выбрал Apache второй версии, открытую лицензию, отлично подходящую для opensource публикуемых программных решений, так же есть крайне популярные MIT и GNU GPL (https://ru.wikipedia.org/wiki/GNU_General_Public_License). В поле Description одним предложением описываем свою библиотеку, а в Author можно перечислить авторов, мой Composer уже знает меня, и поэтому подставил по умолчанию. На вопросы о зависимостях (dependencies) я пока что ответил отрицательно, мы поставим нужные зависимости позже, а также добавим кое-что вручную прям в файл composer.json, в котором и хранятся все настройки проекта.
В конце после вопроса "Do you confirm generation [yes]?" мы нажали Enter, и в корне проекта появился файл composer.json. Откроем его и пропишем зависимость от php, я выбрал версию не ниже 7.1, так как седьмая версия выпущена уже более пяти лет. Но если вы предполагаете, что вашей библиотекой будут пользоваться на проектах с php 5, тогда можно проставить ">=5.6.0". Далее запускаем команду установки зависимости от официальной Gmail API библиотеки:
composer require google/apiclient:^2.0
Это займет некоторое время, добавляем autoload, который позволит Composer'у находить все файлы с кодом у нас в проекте, и в конечном итоге наш composer.json будет выглядеть вот так:
{
"name": "zhukmax/gmail",
"description": "Package for easy using Gmail Api",
"type": "library",
"license": "Apache-2.0",
"authors": [
{
"name": "ZhukMax",
"email": "zhukmax@ya.ru"
}
],
"minimum-stability": "dev",
"require": {
"php": ">=7.1.0",
"google/apiclient": "^2.0",
"ext-json": "*"
},
"autoload": {
"psr-4": {
"Zhukmax\\Gmail\\": "src/"
}
}
}
Заходим на github под своим аккаунтом (если нужно зарегестрируйтесь) и создаем новый репозиторий с названием gmail, так же стоит указать описание, лицензию и другие настройки.
Подключаем к уже созданому локально проекту новый репозиторий:
git init
git remote add origin https://github.com/ZhukMax/gmail.git
git config checkout.defaultRemote origin
git pull origin main
Открываем папку с нашим проектов в любимом редакторе коде, я лично предпочитаю PhpStrorm, но вполне подойдет даже Notepad++. Создаем директорию src, в которой и будет располагаться основной код. Как видно из листинга выше, мы прописали именно эту папку для всех классов в namespace Zhukmax\Gmail.
На шаге два из официальной инструкции к работе с Gmail API (https://developers.google.com/gmail/api/quickstart/php#step_2_set_up_the_sample) нам требуется создать файл, в котором размещаем некоторые конфигурационные данные, функцию авторизации и сам код работы с API. Как раз конфигурацию и авторизацию мы и перенесем в наш пакет, чтобы при использовании можно было не заморачиваться с этим кодом и сразу заниматься тем, что необходимо на проекте. Поэтому создаем класс авторизации в src и добавляем в него функцию авторизации:
touch src/Auth.php
Пока что наш код не будет работать, так как функции авторизации нужны настройки: название приложения, путь к директории для хранения токена, путь к файлу credentials.json, который можно получить в консоли разработчика Google. А еще этот код можно улучшить и упростить работу с ним для конечного разработчика. Для начала создадим публичный конструктор и метод, который будет отдавать готовый к работе сервис, либо выбрасывать исключение (Exception), если произошла какая-то ошибка, а уже созданный сделаем приватным:
<?php
private $name;
private $scope;
private $credentialsPath;
private $tokenPath;
public function __construct(array $params)
{
if (!$params['credentials']) {
throw new Exception("Path to credentials is required");
}
$this->credentialsPath = $params['credentials'];
if (!$params['token']) {
throw new Exception("Path to token is required");
}
$this->tokenPath = $params['token'];
$this->name = $params['name'] ?? 'Gmail API PHP';
$this->scope = $params['scope'] ?? Google_Service_Gmail::GMAIL_READONLY;
}
public function getService()
{
$client = self::getClient();
return new Google_Service_Gmail($client);
}
Также предлагаю сделать небольшую декомпозицию основного метода, взятого нами из официальной документации. Во-первых, стоит вынести код, занимающийся сохранением файла токена в отдельный метод, во-вторых, запрос токена, если предыдущий просрочен или даже не существует:
<?php
private function makeNewToken(Google_Client $client)
{
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
private function saveToken(Google_Client $client)
{
if (!file_exists(dirname($this->tokenPath))) {
mkdir(dirname($this->tokenPath), 0700, true);
}
file_put_contents($this->tokenPath, json_encode($client->getAccessToken()));
}
Конечно, еще хорошо бы покрыть весь наш код юнит-тестами, но это уже другая история.
Публикация библиотеки
Когда код библиотеки готов сделаем коммит и отправим все правки в репозиторий на github (или другой выбранный вами ранее):
git push origin main
Теперь скопируем ссылку на репозиторий:
На сайте packagist.org после авторизации переходим в раздел Submit, вставляем GitHub URL - ссылку на репозиторий и нажимаем Check и затем Submit
Готово, наша библиотка отправлена в публичное пространство и доступна для установок и использования.
Результат
Мы создали новую библиотеку, облегчающую работу с API, отправили ее публичный репозиторий и опубликовали для установки через composer. Теперь мы умеем делиться своими наработками и решениями с сообществом.