Розробка opensource- та приватних Composer-пакетів: як це робити і навіщо
У цій статті продемонструємо розробку PHP-пакету, розберемося, для чого це робити та як автоматизувати рутинні дії для його підтримки. Стаття буде цікава програмістам будь-якого рівня, які планують створити свій opensource-пакет, або тим, хто, розробляючи приватний пакет, хоче знайти шлях інкапсулювання складної логіки поза межами основного репозиторію.
Composer
Майже кожен PHP-розробник знає про Composer. Це менеджер пакетів, який революціонував PHP і дав дуже потужний поштовх для розвитку цієї мови.
Тепер для того, щоб використати у своєму проєкті напрацювання інших девів, розробникам не треба завантажувати PHP-файли, копіювати код і робити інші, дивні для сучасної розробки, налаштування. Достатньо просто знайти потрібний пакет на packagist.org і виконати команду composer require author/package-name
або composer install
, якщо потрібно встановити пакети з попередньо сконфігурованого файлу composer.json
.
Самі пакети створила велика спільнота розробників, кожен з яких робить свій внесок у opensource-спільноту і розвиток мови.
Для чого розробляти пакети
Давайте розберемося, для чого розробники створюють composer-пакети — яка в них мотивація?
Ось декілька головних чинників для opensource-пакетів:
- відчуття внеску в спільноту, тому що таким чином розвивається технологія;
- покращення свого резюме, тому що opensource-проєкти — це дуже хороше доповнення до портфоліо;
- удосконалення якості свого коду, бо знаючи, що код потенційно може подивитися велика кількість людей, програміст автоматично намагається написати якісний і продуманий код;
- just for fun, просто тому що це весело. Також це перевірка пул реквестів, розвиток чогось свого, фідбек від колег.
Пакети можуть бути розроблені тільки для внутрішнього використання. Таким чином розробники відмежовують логіку в пакеті від логіки основного проєкту.
Кожен пакет починається з composer init
. Щоб створити пакет, потрібно виконати команду composer init
у директорії пакету.
Composer запропонує вам декілька стандартних запитань. Поки що можемо просто пропустити їх.
Composer також запропонує вам інтерактивно обрати залежності — вибираємо no, бо пакет, який створюємо, не матиме залежностей на цьому етапі.
У результаті, створиться файл composer.json — файл з конфігурацією.
composer.json { "name": "igorrebega/uawords", "authors": [ { "name": "Igor Rebega", "email": " Данный адрес e-mail защищен от спам-ботов, Вам необходимо включить Javascript для его просмотра. " } ], "require": {} }
Autoloading
Щоб Composer міг працювати з namespace й правильно підключати файли, нам потрібне автозавантаження за форматом PSR-4. Будемо складати наш код в папку /src
.
Відредагуємо composer.json
таким чином, додавши секцію autoload
:
{ "name": "igorrebega/uawords", "authors": [ { "name": "Igor Rebega", "email": " Данный адрес e-mail защищен от спам-ботов, Вам необходимо включить Javascript для его просмотра. " } ], "require": {}, "autoload": { "psr-4": { "IRebega\\UaWords\\": "src/" } } }
Виконаємо команду composer dump
, яка створить файл для автозавантаження.
На цьому етапі ми готові робити наш перший клас:
src/WordFactory.php <?php namespace IRebega\UaWords; class WordFactory { public function hello() { return 'Привіт!’; } }
Для тестування створимо файл index.php
:
index.php <?php include 'vendor/autoload.php'; echo (new \IRebega\UaWords\WordFactory())->hello() . PHP_EOL;
І запустимо скрипт за допомогою команди php index.php
.
Як бачите, автозавантаження працює.
Тестування
Якщо ви хочете, щоб вашим пакетом користувалися інші розробники, вам потрібні тести.
Для цього використаємо фреймворк PHPUnit. З урахуванням того, що він нам потрібен тільки для розробки, додамо його як dev-залежність, виконавши команду composer require --dev phpunit/phpunit
(це змінить composer.json-файл).
Тепер давайте створимо папку для тестів і назвемо її tests
. Також додамо автозавантаження для неї (секція autoload-dev):
composer.json
Перший тест
Код тесту:
tests/WordFactoryTest.php <?php namespace IRebega\UaWords\Tests; use IRebega\UaWords\WordFactory; use PHPUnit\Framework\TestCase; class WordFactoryTest extends TestCase { public function test_it_is_not_null() { $this->assertNotNull((new WordFactory())->hello()); } }
Щоб його запустити, нам потрібен файл з налаштуваннями phpunit — phpunit.xml
:
<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php" backupGlobals="false" backupStaticAttributes="false" colors="true" verbose="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false"> <testsuites> <testsuite name="bvblogic test suite"> <directory>tests</directory> </testsuite> </testsuites> <filter> <whitelist> <directory suffix=".php">src/</directory> </whitelist> </filter> </phpunit>
Можете використовувати цей код як шаблон своїх пакетів. У більшості випадків вам потрібно буде змінити тільки testsuite name
параметр.
Детальніше можна прочитати тут.
Тепер давайте запустимо наш тест командою ./vendor/bin/phpunit
:
Вітаю! Наш перший тест пройшов успішно.
Використовуємо пакет локально
Тепер давайте навчимося використовувати наш пакет локально, щоб мати змогу тестувати його в реальних проєктах.
Для демонстрації створюємо новий проєкт й ініціалізуємо його за допомогою composer init
.
Далі спробуємо виконати команду composer require igorrebega/uawords
.
Пакет не буде знайдено, тому що composer за замовчуванням використовує сервіс Packagist для пошуку пакетів.
Але ми можемо вказати йому свій локальний репозиторій. Для цього додаємо в composer.json тестового проєкту такий код:
"minimum-stability": "dev", "repositories": [ { "type": "path", "url": "../uawords" } ]
Де url — це відносний або абсолютний шлях до папки з пакетом. А minimum-stability вказаний для того, щоб мати змогу використовувати dev-версії пакетів.
Знову виконаємо composer require igorrebega/uawords
:
Далі створимо файл index.php
:
index.php <?php require 'vendor/autoload.php'; echo (new \IRebega\UaWords\WordFactory)->hello() . PHP_EOL;
Запустимо його командою php index.php
і отримаємо «Привіт!» у консолі.
Додаємо пакет у Git
Виконуємо команду git init
. Додаємо файл .gitignore
з таким вмістом:
.gitignore vendor composer.lock
Додаємо всі інші файли в Git і пушимо на GitHub.
Додаємо пакет у Packagist
Переходимо за цією адресою, реєструємося і вказуємо посилання на GitHub. Далі підтверджуємо створення пакету.
Як ви тепер можете бачити, у нас тільки одна версія — dev-master. Тобто все, що ви пушите у гілку master, автоматично позначається як найновіша версія.
Додаємо версії
Для версій ми будемо використовувати Semantic Versioning. Докладніше можна прочитати тут — semver.org.
Найголовніше, розуміти те, що версія складатиметься з трьох чисел, розділених крапкою.
- MAJOR — ті зміни, які можуть зламати код користувача, якщо він використає вищу версію пакету, ніж у нього був.
- MINOR — удосконалення, які додають функціонал, але не ламають код, який використовує старий функціонал.
- PATCH — зазвичай для багфіксів.
Отже, заходимо в GitHub, потім releases:
Натискаємо create a new release, вводимо інформацію про реліз і тиснемо Publish release.
Тепер переходимо на сторінку свого пакету в Packagist, тиснемо оновити і маємо побачити, що версія змінилася на 1.0.0. Саме вона буде використана за замовчуванням:
Рекомендуємо декілька змін заливати в одну версію, щоб ваш пакет був стабільний і ви мали досить часу протестувати новий код.
Travis CI
Тепер зробімо так, щоб тести запускалися автоматично. Для цього використаємо сервіс Travis CI.
Реєструємося, синхронізуємося з GitHub і вибираємо наш репозиторій:
Додаємо до проєкту файл з налаштуваннями travis:
.travis.yml language: php php: - 7.2 - 7.3 - 7.4 env: matrix: - COMPOSER_FLAGS="--prefer-lowest" before_script: - travis_retry composer update ${COMPOSER_FLAGS} script: - vendor/bin/phpunit
Таким чином ми вказуємо сервісу, що потрібно запускати тести на декількох версіях PHP і використовувати найнижчі версії розширень. У такий спосіб ми можемо впевнитися, що тести працюють навіть зі старими версіями залежностей.
Після того як ви запушите, Travis автоматично виконає тести й сповістить вас про результат.
StyleCI
Тепер давайте подбаємо про єдиний формат написання коду. У цьому нам допоможе сервіс StyleCI.
Після реєстрації переходьте на сторінку Repos і включіть StyleCI для свого репозиторію.
Також пропонуємо перейти до Repositories setting та обрати бажаний рівень автоматизації. Для opensourсe-проєктів часто підходить Automatically send and merge fix pull requests
:
Він буде автоматично надсилати і мержити пул реквести із змінами code style.
Тепер нам потрібно додати файл конфігурації в проєкт:
.styleci.yml preset: laravel
Таким чином ми будемо використовувати code style фреймворку Laravel. Після пушу ви маєте побачити, як StyleCi автоматично пофіксить усі проблеми з code style.
Ліцензія
Якщо ви не вкажете ліцензію для вашого проєкту, то легально не дозволяєте нікому його використовувати. Більшість проєктів для opensource мають MIT-ліцензію, яка дозволяє використовувати пакет у будь-яких цілях, але знімає з автора юридичну відповідальність за його роботу.
Тут можна прочитати більше про MIT і скопіювати текст ліцензії.
Пропонуємо так і зробити, створивши файл LICENCE.md, змінивши [year] [fullname] секції та вставивши текст ліцензії туди.
Далі ви маєте побачити на GitHub іконку MIT-ліцензії:
Readme
Якщо ви хочете, щоб про ваш пакет дізналися більше, вам обов’язково треба створити readme-файл.
Пропонуємо скористатися сервісом Makereadme.
Далі додаємо завантажений файл README.md до репозиторію.
Додаткові ресурси
Як бачите, процес створення пакету справді займає чимало часу. Щоб вирішити цю проблему, було створено багато сервісів, які можуть згенерувати вам порожній проєкт, який ви потім можете використовувати. Наприклад, Laravel Package Boilerplate.
Приватні пакети
Нещодавно Packagist запустив комерційну версію, яка дозволяє створювати приватні пакети — Packagist.
Тому, якщо ваш проєкт не opensource, і ви не маєте змоги створити публічний пакет для нього, цей сервіс може стати вам у пригоді.
Ми в команді часто користуємося приватними пакетами. Наприклад, коли необхідно розробити АПІ-інтеграцію зі специфічним ресурсом, то простіше код інтеграції винести в окремий пакет і просто підключати той пакет до нашого основного репозиторію.
Наступні кроки
Сподіваюся, ця стаття надала вам потрібні для створення пакетів інструменти. А якщо треба додаткові приклади, то рекомендуємо вам переглянути найпопулярніші PHP-пакети на GitHub.