• База знань
  • /
  • Блог
  • /
  • Wiki
  • /
  • ONLINE CHAT
+380 (44) 364 05 71

Стаття також доступна російською (перейти до перегляду).

Що таке Composer

Вступ

Управління залежностями в PHP стало набагато простішим з появою Composer. Давайте розглянемо використання деяких найбільш досконалих методів, котрі застосовує вказана технологія.

Що таке Composer

Це менеджер залежностей для PHP, подібний до NPM чи Yarn для Javascript або ж до pip для Python. Це спрощує керування пакетами, від яких залежить додаток. У якості додаткової опції, Composer додає можливість для пакетів реєструвати автозавантажувач. І це поєднання робить Composer однією з найбільш важливих утиліт PHP-середовища.

Найчастіше у якості репозиторію пакетів для Composer використовується сервіс Packagist. Він приймає лише пакети з відкритим вихідним кодом. Для можливості скористатися усіма функціями Composer можна використовувати Private Packagist. Це платна версія сервісу Packagist, створена розробниками Composer, яка дозволяє використовувати пакети із закритим вихідним кодом, що також включає детальний контроль доступу. Нещодавно ми розповідали про те як налагодити роботу з приватними репозиторіями за допомогою Gitlab. Докладніше про роботу з приватними репозиторіями за посиланням.

Тут не буде роз'яснень, стосовно встановлення і налаштування Composer. Розробники сервісу докладно пояснюють це у розділі «Початок роботи» веб-сайту Composer. Для будь-яких прикладів, де буде використовуватися Composer CLI, будемо вважати, що Composer встановлений глобально і викликається за допомогою команди composer.

Окреме використання робочих залежностей та залежностей розробки

 Composer розрізняє два типи залежностей – робочі та розробки. Робочі залежності – це залежності, котрі завжди потрібні коду, незалежно від середовища, у котрому він виконується. Залежності розробки – це залежності, котрі необхідні лише при розробці коду, наприклад, у середовищі тестування PHP Unit, статичних аналізаторах Psalm та аналізаторах стилю PHP Code Sniffer. В них нема необхідності при запуску коду у робочому середовищі. При додаванні залежності при використанні команди composer require [package] вона буде помічена як «робоча залежність». При використанні префіксу -dev пакет буде помічений як «залежність для розробки».

При запуску команди composer install завантажуються і встановлюються для додатку як робочі залежності, так і розробки. Щоб завантажити та встановити лише робочі залежності, необхідно додати префікс --no-dev. Зазвичай, це те, що потрібно у конвеєрі розгортання, а залежності розробки тут не потрібні.

Команда composer install завантажує і встановлює лише залежності розробки для додатку або пакету, але це не стосується залежностей розробки другого рівня. Тому, якщо існує залежність проекту від пакету А, а пакет А має залежність розробки від пакету В, то лише пакет А буде завантажений та встановлений у проекті.

Залежності розробки можуть накопичуватися у списку залежностей і, відповідно, у великій кількості файлів. Всі ці файли автоматично завантажуються автозавантажувачем. Це може зекономити місце, час передачі (під час розгортання) та час виконання, щоб мінімізувати кількість робочих залежностей у додатку. Економія місця та часу передачі можуть бути незначними при невеликих обсягах, але як тільки додаток збільшиться у розмірах або супроводженні, це може призвести до більш значущих цифр. Ще однією перевагою маркування залежностей розробки є те, що вони не будуть включені у конструкції Composer автозавантажувача при запуску за допомогою команди composer install --no-dev.

Автозавантаження

Як вже зазначалось, додаткова функція Composer полягає в тому, що він пропонує утиліти для підключення пакетів до автозавантаження PHP. За допомогою цієї функціональності пакети можуть вказувати, яким чином Composer може зіставляти ім'я класу із ім'ям файлу. Це звільнює розробників від забруднення коду викликами типу require_once та require. Все, що потрібно зробити, це включити vendor/autoload.php.

Як і у випадку із залежностями, Composer пропонує два типи автозавантажувачів – робочі директиви автозавантаження і директиви автозавантаження розробки. Пакети можуть це використовувати, щоб зменшити свій розмір і розмір автозавантажувача, однак додатки також можуть виграти від цього. Якщо припустити, що проект добре протестований, кількість файлів, котрі використовуються лише для тестування може складати значну частину кодової бази, подібно до будь-яких утиліт розробки, таким, як інструменти командної строки. Під час роботи вони не потрібні. Відмітивши автозавантажувач для цих файлів, як автозавантажувач розробки, вони не будуть враховуватися при запуску команди composer install --no-dev, що може підвищити продуктивність додатку. Відмінність між робочим автозавантаженням і автозавантаження розробки зазначається у файлі composer.json:

Файл автозавантаження autoload.php

Composer пропонує декотрі додаткові інструменти для підвищення продуктивності функцій і класів автозавантаження за допомогою оптимізації автозавантажувача.

Обмеження версії

До появи Composer розробник додатків завантажував визначену версію залежності і поміщував її у систему управління версіями. Composer дає змогу відійти від конкретної версії залежності та бере на себе завдання вирішити, яку саме версію залежності встановити. Щоб визначити це, Composer в значній мірі спирається на концепцію семантичного управління версіями. Він не тільки робить це для додатку, але і розширює цю функціональність для залежностей. Це дозволяє пакетам мати свої власні залежності, котрі, у свою чергу, теж можуть мати свої залежності і так далі. Це призводить до зменшення розміру пакетів, котрі можуть зосередитися на одній функції і тим самим забезпечити її централізацію.

У порівнянні з NPM, Composer використовує лише одну версію пакету для кожного додатку. Це означає, що потрібно встановити менше пакетів, хоча це створює додатковий ризик конфлікту версій. Це є причиною того, щоб не використовувати конкретну версію залежності, а робити запит на діапазон версій. Зазвичай, це робиться за допомогою оператора каретки типу ^1.0. Це означає, що приймається будь-яка версія у діапазоні >= 1.0.0 та < 2.0.0. Враховуючи семантичне управління версіями, це означає, що будуть гарантовані необхідні функції і не вносяться зміни, котрі б порушували зворотну сумісність.

Вказавши діапазон замість конкретної версії, можна попередити конфлікти версій у випадку, якщо інша залежність використовує той самий пакет. Для прикладу, розглянемо розповсюджену ситуацію.

Вміст файлу composer.json додатку виглядає наступним чином:

Вміст файлу composser.json

Файл composer.json для vendor/package:

Файл composer.json для vendor/package

При встановленні залежностей Composer виявить, що наявні кілька запитів для other_vendor/package, але, оскільки вони мають обмеження діапазону, він може вирішити, що йому не варто встановлювати версію 1.1.5, а замість цього краще встановити версію 1.2.8.

Composer має кілька операторів обмежень і, навіть, дозволяє їх комбінувати. Це означає, що обмеження типу "php": "^7.2", котре часто використовується, не буде включати PHP 8.

Щоб вказати, що підтримуються як PHP 7.2 і вищі, так і PHP 8, можна об'єднати два оператори: "php": "^7.2 | | ^8,0".

Іншим варіантом було б використати підхід Symfony та обрати оператор "php": >= 7.2". Але ми не рекомендуємо застосовувати цей підхід, оскільки він передбачає, що код також буде працювати із PHP 9 і вище. Але невідомо, які зміни, котрі порушують зворотну сумісність будуть внесені в PHP 9. І тому, на наш погляд, краще використовувати оператор типу "php": "^7.2 || ^8.0".

Кореневі залежності

Дивлячись на вміст файлу composer.json, можна виявити відмінності між залежностями, котрі вимагаються безпосередньо із додатку («кореневий пакет» у термінах Composer) і залежностями, котрі необхідні опосередковано, як залежність від кореневої залежності.

Для прикладу, візьмемо базовий файл composer.json:

Базовbй файл composer.json

Запуск команди composer install для вказаного файлу покаже, що завантажений буде не тільки пакет laminas/laminas-diactoros, але і laminas/laminas-zendframework-bridge, psr/http-factory та psr-http-message. У цьому прикладі laminas/laminas-diactoros є кореневою вимогою.

Різниця між кореневою і похідною вимогою полягає у кількості прямих залежностей у проекті. У цілому, менше кореневих залежностей означає меншу кількість пакетів і, відповідно, менше виникаючих конфліктів при їх оновленні. Менше пакетів, тому що нова версія пакету може мати інші залежності, котрі автоматично будуть підтримуватися Composer. Менше конфліктів, тому що дерево залежностей має менше обмежень.

Але що робить пакет таким, щоб він відповідав кореневій вимозі? Емпіричне правило полягає в тому, що коли клас або інтерфейс із пакету використовується у доменному коді, це повинно бути кореневою вимогою. Тепер знову поглянемо на наш приклад:

Базовий файл composser.json

Дивлячись на приклад, де ми покладаємося лише на laminas/laminas-diactoros, Diactoros пропонує реалізацію інтерфейсів, вказаних у PSR-7 и PSR-15. Якщо у коді ми посилаємося лише на об'єкти з laminas/laminas-diactoros, це буде допустимою конфігурацією Composer. Але, якщо ми будемо посилатися на інтерфейси, котрі є визначеними у PSR-7 та PSR-15, то це було б причиною зробити psr-http-message та psr/http-factory кореневими вимогами.

Для прикладу:

Приклад посилань на інтерфейси psr

Це емпіричне правило означає, що пакет, котрий виконує дії на основі об'єктів PSR-7, повинен тільки відмітити psr-http-message як залежність, це робить його незалежним від фактичної реалізації інтерфейсу. У цьому, по суті, й полягає ідея PSR.

Вплив середовища

Додатки або пакети створюються для визначеної версії PHP. Це гарантує правильне функціонування коду лише для цієї версії. Можливо також встановлення розширення PHP. Саме тому рекомендується вказувати, які версії PHP необхідні або підтримуються, а також які необхідні розширення.

Коли пакет розробляється, версії і розширення PHP можна використовувати так, начебто це звичайні пакети:

Вказівка весії PHP та розширень, які підтримуються.

Оскільки розширення прив'язане до PHP, вказівка версії не вимагається і тому ми можемо для cURL розширення використовувати знак «*».

Цей підхід також можна використовувати при створенні додатку і, навіть, вказати ці обмеження у якості конфігурації платформи:

Примусове використання версії PHP 7.4.4

Перевага тут в тому, що Composer буде використовувати версію PHP 7.4.4 у якості версії PHP, навіть, якщо у системі, де виконуються команди Composer встановлена інша версія. Із перспективою використання рішень віртуалізації, таких як Docker та Vagrant, це дозволить користувачам запускати команди Composer локально та запускати додаток у віртуалізованому середовищі. Це також виключає можливість впливу на робоче середовище встановлення одним із користувачів версій залежностей, котрі не підтримуються робочим середовищем.

Робота із системою контролю версій

Зазвичай, у проекті, Composer буде мати три об'єкти файлової системи – два файли із ім'ям composer.json та composer.lock, а також каталог із ім'ям vendor*. Однак, не всі з них повинні бути включені в систему управління версіями.

У файлі composer.json знаходяться всі пакети, котрі вимагаються із обмеженнями версій – додаткову інформацію див. у розділі «Обмеження версій». Цей файл завжди необхідно додавати в систему контролю версій. Без нього користувач, котрий хоче використовувати код, навіть, не уявляє, які пакети вимагаються.

У файлі composer.lock містяться точні версії всіх пакетів, включаючи контрольні суми. Якщо цей файл присутній при запуску команди composer install, він встановить такі ж самі версії, котрі вказані у composer.lock. Завдяки цій функції, гарантується, що залежності у всіх середовищах однакові. При створенні додатку ми обов'язково додамо цей файл в систему контролю версій, однак при створенні бібліотеки ми цього не зробимо. Це пов'язано з тим, що додатки (або інші пакети), котрі використовують пакет, не будуть враховувати цей файл, а локальне середовище розробки враховує. Це може призвести до розробки або тестування застарілих версій залежностей, у той час, як споживачі пакету дійсно використовують останні версії.

В каталозі vendor міститься код пакетів. Вміст цього каталогу не варто додавати в систему управління версіями. Цей каталог заповнюється при запуску процесу інсталяції сервісу Composer.

Можна змінити ім'я та розташування каталогу за допомогою параметру конфігурації vendor-dir, однак для цієї статті використовується значення, встановлене за замовченням.

Внесення змін у залежності

Припустимо, що ми хочемо оновити залежності нашого додатку. У цьому випадку ми запустимо кілька команд оновлення сервісу Composer і побачимо зміни у файлі composer.lock. Ми створимо комміт. При наступному оновленні з'явиться ще один комміт. Потім, через кілька днів ми знову продовжимо з того місця, на котрому зупинилися. При цьому ми змушені будемо працювати із перевантаженим репозиторієм, і тому, перш ніж продовжити, необхідно виконати перебазування, щоб переконатися що внесені зміни сумісні із останньою версією коду. Маємо конфлікт злиття! Конфлікт злиття можна вирішити у composer.json, але у composer.lock це зробити складніше. Спосіб вирішення конфліктів злиття у файлі composer.lock полягає в тому, щоб прийняти вхідні зміни – це останні зміни – і запустити команди Composer, щоб знову внести зміни у першу чергу. Це гарантує, що внесені зміни будуть застосовані до самої останньої версії кодової бази. Потім необхідно зафіксувати новий composer.lock та продовжити перебазування. Якщо виконати ще якусь роботу із Composer у наступному комміті, то необхідно буде повторити ці кроки ще раз. Для того, щоб цього уникнути, необхідно:

  • Вносити зміни у залежностях у одному комміті;
  • Використовувати для запуску список команд Composer;
  • Двічі застосовувати ці команди, якщо у composer.lock виникає конфлікт злиття.

Висновок

Composer став життєво важливою частиною розробки PHP. Як і у випадку із будь-яким менеджером пакетів, тут можуть знадобитися додаткові пояснення. Нижче наведемо декотрі рекомендації, що до його використання:

  • Виділити залежності робочого середовища та залежності розробки, щоб зменшити розмір додатку;
  • Розділити автозавантажувач на робочий та розробки, це зменшить його розмір та підвищить продуктивність;
  • Не треба покладатися на фіксовані версії залежностей, а лише на їх діапазон, це зробить оновлення пакетів менш хворобливим;
  • Використовувати лише прямі залежності, котрі фактично викликаються у середині додатку, це спростить оновлення або перехід на альтернативне рішення;
  • Завжди поміщати composer.json в систему управління версіями, але composer.lock в систему управління версіями для додатків, щоб користувачі пакету не змогли оновлювати інші пакети;
  • Треба намагатися мати максимум один фіксований запит чи запит на злиття, котрий вносить зміни в composer.json та composer.lock, це зведе до мінімуму конфлікти злиття у цих файлах.

Віртуальний хостинг FREEhost.UA надає можливість роботи з Composer за допомогою SSH-доступу. Це вкрай необхідно під час сучасної розробки сайтів. Запрошуємо Вас безкоштовно протестувати роботу нашого хостингу.

Підписуйтесь на наш телеграм–канал https://t.me/freehostua, щоб бути в курсі нових корисних матеріалів.

Дивіться наш канал Youtube на https://www.youtube.com/freehostua.

Ми у чомусь помилилися, чи щось пропустили?

Напишіть Про це у коментарях, ми з задоволенням відповімо та обговорюємо Ваші зауваження та пропозиції.

Дата: 13.12.2022
Автор: Євген
Голосування

Авторам статті важлива Ваша думка. Будемо раді його обговорити з Вами:

comments powered by Disqus
navigate
go
exit
Дякуємо, що обираєте FREEhost.UA