Коментування коду та генерація документації в PHP

Code commenting and PHP documentation generation

Навіщо потрібні коментарі в коді? Як їх писати? Де вони потрібні, а де ні? Як правильно коментувати код? Як створити однаковий стиль документації для всіх членів команди? Які існують інструменти для генерації документації? Я спробую відповісти на всі питання і поділитися з вами своїми думками з цього приводу.

Отже, існує два типи програмної документації. Перший тип - це написання коментарів у самому коді. Другий варіант - це коли використовується сторонній інструмент або репозиторій, наприклад WIKI-engine, де описуються принципи роботи програми, приклади використання, модулі взаємодії, а також надаються блок-схеми та діаграми, загалом все те, що не можна написати в коді.

Варіанти розміщення документації

Почнемо з документації всередині програмного коду. Хоча це не є метою цієї статті. У проектах з відкритим вихідним кодом часто можна помітити, що статті документації зберігаються у тому ж сховищі, що й основний код. Наприклад, у бібліотеці PHP fake fixtures документація знаходиться у файлі README, і якщо ви хочете прочитати її до кінця, вам доведеться трохи прокрутити. У Guzzle, популярному HTTP-клієнті для PHP, інструкції з використання зберігаються в окремій теці docs. Зберігати документацію поруч з кодом - це добре і дуже зручно. Завантаживши пакет постачальника один раз, ви отримуєте код і документацію. Якщо ваша бібліотека невелика, якщо вона стабільна і не передбачає частих змін API в майбутньому, які призведуть до постійного переписування документації, ви можете сміливо розміщувати документацію в репозиторії вашого проекту.

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

В такому випадку краще створити окремий репозиторій для документації, наприклад, як це було зроблено для Symfony. GitHub, GitLab, Bitbucket також надають вбудований інструмент WIKI, який приєднується до проекту і не є окремим репозиторієм. Але ви також можете отримати доступ до неї через Git, а це означає, що ви можете копіювати всю документацію, редагувати її в редакторі, якому ви віддаєте перевагу, групувати зміни в коміти, відправляти їх на сервер і отримувати нові коментарі.

Ось приклад добре організованої WIKI для бібліотеки візуалізації D3.js. Звичайно, можна створити веб-сайт для продукту і розмістити там його документацію. Але якщо ви скористаєтеся одним з описаних вище способів, то зможете генерувати веб-сторінки документації з вашого Git або WIKI-репозиторію - для цього є відповідні інструменти. Якщо ж ви віддаєте перевагу комплексним рішенням, то варто звернути увагу на Confluence від Atlassian. Він має набагато більше можливостей, ніж WIKI-рушій.

Коментування кода всередині коду

Тепер давайте повернемося до документування коду в самому коді. Я пишу цю статтю на основі власного досвіду, але нещодавно я прочитав "Чистий код" Роберта Мартіна, тому я буду цитувати цю книгу, коли це буде доречно. Перше повідомлення від Роберта Мартіна полягає в тому, що коментар - це ознака невдачі. Коментарі пишуться лише для того, щоб вказати на помилку програміста, який не зміг чітко висловити свою думку мовою програмування. Процес аналізу коду - дуже широке поняття, і він виходить далеко за межі цієї статті. Але дозвольте мені поділитися з вами хитрістю для написання дійсно хорошого коду: ви повинні писати його так, щоб його можна було читати, як речення. Об'єктно-орієнтоване програмування набагато простіше, ніж функціональне, широко розповсюджена практика називати класи іменниками, а методи дієсловами робить код більш природним. Наприклад, у нас є кролик, і давайте опишемо деякі з його основних функцій так, ніби вони є інтерфейсом:

interface RabbitInterface
{
    public function run();
    public function jump();
    public function stop();
    public function hide();
}

Ми просто створюємо один об'єкт з класу Rabbit class:

$rabbit = new Rabbit();
$rabbit->run();
$rabbit->stop();

Код легко читається. Метод run змушує кролика бігти, метод stop - інтуїтивно зрозуміла команда, вона зупиняє попередню дію і кролик зупиняється. Тепер давайте навчимо нашого кролика деяким трюкам і змусимо його пробігти фіксовану відстань, яку ми передамо як параметр методу run.

$rabbit->run(100);

І він побіг... Але ми не можемо зрозуміти, що означає число 100. Це число означає хвилини, метри чи фути? Це можна виправити за допомогою коментаря:

// Rabbit have to run 100 metres
$rabbit->run(100);

Якщо кролик починає "бігати" в декількох місцях і рядках вашого коду, кожен з них потребує додаткових коментарів. Коментарі будуть дублюватися, і їх слід підтримувати одразу в декількох місцях. Перше, що ви можете зробити, щоб уникнути коментарів - це замінити число на змінну.

$metres = 100;
$rabbit->run($metres);

У цьому випадку коментар не потрібен, тому що читабельність коду стає трохи кращою і в коді видно, що кролик пробігає 100 метрів. Але найкращим варіантом буде додати контекст до назви методу.

$rabbit->runInMetres(100);

Кролик - іменник, бігти - прислівник, в метрах - контекст, який ми додаємо до методу, щоб він відображав суть. Можна писати методи за такою схемою.

$rabbit->runInSeconds(25);
$rabbit->runTillTime(new \DateTime('tomorrow'));
$rabbit->runTillTheEndOfForest($sherwood);

Вони передадуть суть методу без додаткових коментарів. Просто дайте своїм змінним і методам правильні імена, і ви зменшите кількість непотрібних коментарів у вашому коді. Роберт Мартін дає хорошу пораду з цього приводу:

Не витрачайте свій час на написання коментарів, які пояснюють безлад, який ви створили, - витратьте його на його виправлення.

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

$rabbit->runUntilFindVegetables();
$rabbit->runForwardAndTurnBackIfMeet([$wolf, $hunter]);

Але, це вже занадто:

$rabbit->runForwardUntilFindCarrotOrCabbageAndTurnBackIfMeetWolfOrHunter();

Цей метод важко читати, архітектура неправильна. Його можна рефакторити, наприклад, так:

$conditions = new Condition();
 
$untilCondition    = (new Condition\Until())->findVegetables('carrot', 'cabbage');
$turnBackCondition = (new Condition\TurnBack())->ifMeet('wolf', 'hunter');
 
$conditions->add($untilCondition)->add($turnBackCondition);
$rabbit->run(Direction::FORWARD, $conditions);

Існують також винятки щодо довжини імен методів. Наприклад, коли ви пишете специфікації для phpSpec, ви можете не обмежувати довжину методу, головне, щоб ваш код відображав суть. Ось приклад коду з документації phpSpec:

class MovieSpec extends ObjectBehavior
{
    function it_should_have_john_smith_in_the_cast_with_a_lead_role()
    {
        $this->getCast()->shouldHaveKeyWithValue('leadRole', 'John Smith');
    }
}

У специфікаціях підкреслення використовується в назвах методів, так легше читати довгі речення. Це не відповідає стандарту PSR, де використовується camelCase, але для читабельності тестів підійде.

Відчуття правильної довжини методів прийде з часом і досвідом. Ви також можете подивитися на приклади з популярних фреймворків та бібліотек.

Характеристики Коментарів

Застарілість

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

Надмірність

Це означає, що коментар не потрібен і пишеться там, де все зрозуміло і без коментаря. Ось приклад коду із зайвими коментарями:

// Cut the carrot into 4 pieces
$piecesOfCarrot = $carrot / 4;
// Let the rabbit eat all pieces of carrot one by one
foreach ($piecesOfCarrot as $pieceOfCarrot) {
    $rabbit->eat($pieceOfCarrot); // Rabbit eats the piece of carrot
}

Якщо ми видалимо коментарі, код залишиться чистим:

$piecesOfCarrot = $carrot / 4;
foreach ($piecesOfCarrot as $pieceOfCarrot) {
    $rabbit->eat($pieceOfCarrot);
}

Неповнота

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

public function eat($food)
{
    switch ($food) {
        case 'carrot':
            $this->getCalories(50);
            break;
        case 'cabbage':
            $this->getCalories(100);
            break;
        default:
            // If the rabbit eats unknown food - it dies :(
            break;
    }
}

Що означає коментар "кролик помре"? Зрозуміло, коли це відбувається в реальному житті. А як щодо програми? Що автор хотів зробити після цього? Звільнити пам'ять, зайняту кроликом? Згадати про виключення, а потім дописати його в іншому місці коду? У цьому коді з кроликом нічого не відбувається, кролик просто не отримує нових калорій, коли їсть щось, окрім моркви та капусти. Але для новачка, який буде дописувати код, ідея автора залишиться незрозумілою. Цілком ймовірно, що новачок видалить коментар і зробить його по-своєму.

Ненадійність

Всі люди схильні до помилок. Але програмісти роблять їх не лише в коді, а й у блоках коментарів. Через неуважність, втому або незнання іноземної мови, в коментарях буває безлад і вони збивають з пантелику інших. На жаль, таке трапляється. Єдина порада, яку я можу вам дати - відповідально ставитись до своїх коментарів. Якщо ви вирішили щось написати, ви повинні написати це правильно. Ви повинні бути перфекціоністом, коли пишете коментарі.

Неочевидність

Коли в певному фрагменті коду використовуються невідомі або неочевидні терміни.

// Uses coefficient of rabbit growing per day, which depends on several factors
$rabbit->growInSize();

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

Отже, ми не повинні писати коментарі взагалі?

Ми повинні їх писати, але ми повинні брати на себе відповідальність за них. Є критичні моменти, коли вони необхідні.

Інформаційна цінність

У деяких місцях коментарі необхідні. Коли потрібно пояснити алгоритм або коли групі програмістів довелося використовувати "хаки" в коді і залишити коментар про це. Описати причину, чому це було зроблено, про що коментар і коли його потрібно виправити. А ще намагайтеся вибирати правильні імена для своїх змінних і методів.

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

// Find all rabbits in locations which
// end on: shire, field, wood
// starts on: yellow, green
// and are not case sensitive
// e.g. Blackshire, Greenfield, Sherwood, SHERWOOD, wood, Yellowstone
$locationsRegExp = '/\b(yellow|green)\w*|\w*(shire|field|wood)\b/i';
$rabbits = $search->findRabbitsInLocations(locationsRegExp);

Наміри

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

Сповіщення та попередження

Бувають випадки, коли ви не можете використати певну функцію, наприклад

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

Якщо ви стикалися з такою ситуацією, коментарі будуть дуже корисними.

Посилення

// Set default encoding for MB functions manually to prevent cases when it is missed in config
mb_internal_encoding('UTF-8');

Ще одна порада від Діна Мартіна:

Не коментуйте поганий код - перепишіть його.

Коли ви натрапляєте на незрозумілий код і витрачаєте багато часу на те, щоб розібратися в ньому, а потім залишаєте кілька коментарів для наступного розробника, ви повинні розуміти, що код не стане кращим. У такій ситуації, якщо ви розумієте код, спробуйте провести рефакторинг, щоб зробити його більш читабельним. Девіз бойскаута: "Залиш табір (код) чистішим, ніж ти його знайшов".

Створення документації за допомогою Doc.blocks

Існує окремий різновид PHP-коментарів, який має свій власний стандарт - він називається DocBlock). Для роботи з докблоками існує інструмент phpDocumentor (також відомий як phpDoc). Він може зчитувати докблоки з коду і створювати на їх основі документацію. DocBlock - це комбінація DocComment і розміщених у ньому описів, створених на основі стандарту PHPDoc. Існує підтримка багаторядкових коментарів на мові C (DocComment):

/*
 * It is
 * a C-style comment in PHP
 */

DocBlock відрізняється додатковим знаком "зірочка" /** на початку коментаря.

/**
 * It is
 * a PHP docblock
 */

DocBlock може мати лише один рядок, але він повинен починатися з /**.

/** It is also a docblock */

Стандарт PHP Doc для документування PHP-коду базується на javaDoc для Java. Важливим компонентом Docbloc є теги та анотації, які роблять коментарі семантичними. Тег або анотація починається з @, наприклад.

/**
 * Login via email and password
 *
 * @param Request $request Request
 *
 * @return Response
 *
 * @throws BadRequestHttpException
 * @throws UnauthorizedHttpException
 *
 * @Rest\Post("/login")
 */
public function loginAction(Request $request)
{
}

У наведеному вище прикладі @param, @return і @throws є тегами PHPDoc, і вони будуть розбиратися за допомогою phpDocrumentor. @Rest\Post("/login") - це анотація до FOSRestBundle. Різниця між анотаціями і тегами полягає в тому, що теги лише документують PHP-код, а анотації додають або змінюють код. Відмінність PHP анотацій від Java анотацій полягає в тому, що Java анотації є частиною Java, в той час як PHP анотації є коментарями і для їх використання необхідно використовувати рефлексію. Можливо, в майбутньому анотації стануть частиною PHP, але наразі для їх читання потрібно використовувати цей парсер. Варто також зауважити, що якщо ми змінимо початок докблоку з /** на /*, то це вже не буде докблок, навіть якщо він містить теги або анотації, і синтаксичний аналізатор його проігнорує.

Докблоки настільки широко використовуються у спільноті PHP-програмістів, що PSR-5 (PHP Standard Recommendation) підготовлено на основі докблоків. Коли я писав цю статтю, він був у чорновому варіанті.

У PHP за допомогою докблок можна документувати наступні елементи:

  • функції;
  • константи;
  • класи;
  • інтерфейси;
  • риси;
  • константи класів;
  • властивості;
  • методи.

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

/**
 * Rabbit Class
 *
 * @version 0.1.0
 */
class Rabbit implements RabbitInterface
{
    const STATUS_RUNNING = 'running';
 
    /**
     * @var string $status Status
     */
    private $status;
 
    /**
     * Set `running` status for the rabbit
     *
     * @return $this
     */
    public function run()
    {
        $this->status = self::STATUS_RUNNING;
 
        return $this;
    }
}

У PHPDoc є багато тегів, але не всі теги можуть бути застосовані до всіх елементів структури. Нижче наведено список тегів, які вже існують, а також їх використання та пояснення.

  • @api (метод) визначає стабільні загальнодоступні методи, які не змінять свою семантику до наступного великого релізу.
  • @author (у будь-якому місці) визначає ім'я або електронну адресу автора, який написав наступний код.
  • @copyright (у будь-якому місці) використовується для зазначення ваших авторських прав на код.
  • @deprecated (у будь-якому місці) - корисний тег, який означає, що цей елемент зникне у наступних версіях. Зазвичай замість нього додається коментар з кодом, який слід використовувати. Також більшість IDE підсвічують місця, де використовуються застарілі методи. Коли потрібно буде почистити застарілий код для нового релізу, його буде легко знайти за цим тегом.
  • @example (в будь-якому місці) використовується для вставки посилання на файл або веб-сторінку, де показано приклад використання коду. Наразі phpDocumentor стверджує, що цей тег підтримується не повністю.
  • @filesource ( (файл) - тег, який можна розміщувати тільки на самому початку php-файлу, оскільки цей тег можна застосувати тільки до файлу і включити весь код в документацію, що генерується.
  • @global (змінна) - на даний момент цей тег не підтримується, можливо він буде реалізований в наступних версіях при оновленні та доопрацюванні.
  • @ignore (довільне місце) - докблок з цим тегом не буде оброблятися при генерації документації, навіть якщо в ньому є інші теги.
  • @internal (будь-де) - часто використовується з тегом @api, щоб показати, що код використовується внутрішньою логікою цієї частини програми. Елемент з цим тегом не буде включено до документації.
  • @license (файл, клас) - показує тип ліцензії написаного коду.
  • @link (будь-яке місце) використовується для додавання посилань, але згідно з документацією цей тег підтримується не повністю.
  • @method (клас) застосовується до класу і описує методи, що обробляються функцією __call().
  • @package (файл, клас) розділяє код на логічні підгрупи. Коли ви розміщуєте класи в одному просторі імен, ви вказуєте на їх функціональну схожість. Якщо класи належать до різних просторів імен, але мають однакову логічну характеристику, їх можна згрупувати за допомогою цього тегу (наприклад, це стосується класів, які працюють з кошиком покупця, але належать до різних просторів імен). Але краще уникати такої ситуації. Наприклад, стиль коду Symfony не використовує цей тег.
  • @param (метод, функція) описує вхідні параметри функції. Варто зауважити, що якщо ви описуєте вхідні параметри для певної функції за допомогою докблоків, ви повинні описати всі параметри, а не тільки один або два.
  • @property(class) - так само як і @method цей тег розміщується в докблоці класу, але його функція полягає в описі властивостей, доступ до яких здійснюється за допомогою магічних функцій __get() і __set().
  • @property-read, @property-write (class) схожі на попередній тег, але обробляють лише один магічний метод __get() або __set().
  • @return (метод, функція) використовується для опису значення, що повертається функцією. Ви можете вказати його тип і PhpStorm сам його визначить і дасть вам різні підказки, але про це поговоримо пізніше.
  • @see (будь-де) - за допомогою цього тегу можна вставляти посилання на зовнішні ресурси (так само, як і за допомогою @link), але він також дозволяє ставити відносні посилання на класи і методи.
  • @since (будь-де) - можна вказати версію, в якій з'явився даний фрагмент коду.
  • @source (будь-яке місце, крім початку) - за допомогою цього тегу можна розміщувати фрагменти вихідного коду в документації (задається початок і кінець рядка коду)
  • @throws (функція-метод) - використовується для вказівки винятків, які може викликати ця функція.
  • @todo (будь-яке місце) - найоптимістичніший тег, який використовується програмістами як нагадування про те, що потрібно зробити в певному фрагменті коду. IDE вміють виявляти цей тег і групувати всі частини коду в окремому вікні, що дуже зручно для подальшого пошуку. Це робочий стандарт і використовується дуже часто.
  • @uses (в будь-якому місці) використовується для відображення зв'язку між різними ділянками коду. Він схожий на @see. Різниця в тому, що @see створює односпрямоване посилання і після переходу на нову сторінку документації у вас не буде зворотного посилання, тоді як @uses дає вам посилання для зворотної навігації.
  • @var (змінна) використовується для вказівки та опису змінних, подібних до тих, що використовуються всередині функцій та для властивостей класу. Слід відрізняти цей тег від @param. Тег @param використовується тільки в докблоках для функцій і описує вхідні параметри, а @var використовується для опису змінних.
  • @version (в будь-якому місці) позначає поточну версію програми, в якій з'являється даний клас, метод і т.д.

Застарілі теги, які, швидше за все, не будуть підтримуватися в майбутньому:

  • @category (файл, клас) використовувався для групування пакунків.
  • @subpackage (файл, клас) використовувався для позначення окремих груп у пакунку.

Не всі теги однаково популярні. Найчастіше використовуються @var, @param, @return, @todo, @throws. Інші менш популярні. А таких тегів як @property і @method я ніколи не зустрічав, тому що працювати з магією небезпечно!

Зручність використання докблоків в IDE

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

Візьмемо найпопулярнішу IDE для PHP - PhpStorm. І подивимося на попередній приклад пошуку кроликів:

$rabbits = $search->findRabbitsInLocations('/Sherwood/');
foreach ($rabbits as $rabbit) {
    $rabbit->doSomething();
}

Що означають змінні $rabbits і $rabbit? PhpStorm нічого про це не знає. PHP слабо типізований і тип результату функції не є строго визначеним в описі (привіт, PHP 7, де це буде реалізовано). Тому ви повинні вказати вашій IDE, як працювати з певними частинами коду за допомогою док-блоків. Існує кілька варіантів. Можна зробити так:

/** @var Rabbit $rabbit */
foreach ($rabbits as $rabbit) {
    $rabbit->doSomething();
}

Or add tag @return in the method findRabbitsInLocations:

/**
 * @return Rabbit[]
 */
public function findRabbitsInLocations($locations)
{
    // some operations here...
    return [];
}

Зверніть увагу, що ми вказуємо саме Rabbit[], а не Rabbit. Дужки дають зрозуміти, що повертається масив об'єктів класу. Якщо ми приберемо дужки, то це буде означати, що метод повертає один екземпляр класу Rabbit. Також можна написати так @return null|Rabbit[], вертикальна паличка означає АБО, в цьому випадку ми вказуємо, що метод поверне масив кроликів або null.

Незалежно від того, який спосіб вказівки типу ви вибрали, тепер PHPStorm покаже вам кілька підказок і порад після того, як ви введете $rabbit-> і почекаєте деякий час.

Коментування коду і генерація PHP-документації

Це відбувається тому, що PHPStorm знає, що у змінній $rabbits повертається масив об'єктів Rabbit. Далі в циклі foreach змінна $rabbit отримує один елемент масиву, який є прикладом класу Rabbit, і PHPStorm показує вам всі доступні загальнодоступні методи цього класу. Таким чином, ви можете використовувати класи з загальнодоступними методами, написані вашими колегами, не відриваючи рук від клавіатури.

PHPStorm надасть вам підказки, і якщо метод має зрозумілу назву, ви зможете використовувати його навіть без читання вихідного коду та документації.

Ще одна корисна фіча, доступна вам при використанні докблоків з PHPStorm, є попередження про неправильні параметри доступу. Закінчимо написання докблоку для одного з методів класу Rabbit:

/**
 * Run in metres
 *
 * @param int $metres Metres
 */
public function runInMetres($metres)
{
    // some operations here...
}

Тут ми вказуємо, що для доступу потрібно використовувати ціле число (в PHP 7 можна буде задати число синтаксично). Що станеться, якщо передати в цей метод масив?

Коментування коду і створення документації PHP

PHPStorm підсвічує це і дає підказку, що тут виключається int, коли ви використовуєте масив. Дуже зручно, чи не так? Підказки також будуть показані для невідповідних класів та інтерфейсів. Якщо ваш метод підтримує декілька типів вхідних аргументів, розділіть їх за допомогою |. У цьому прикладі, якщо метод runInMetres() може працювати з масивами, ви можете написати «@param int|array $metres Metres» и PhpStorm перестануть сваритися.

PhpStorm також вміє генерувати докблоки. Помістіть курсор у рядок над оголошенням функції, класу або змінної, введіть /** і натисніть Enter. IDE згенерує шаблон докблоку, який ви можете трохи змінити за бажанням. Ви також можете запустити генерацію докблоків за допомогою комбінації клавіш Alt + Insert.

Як дотримуватися стилів коментування

Добре, якщо всі члени команди дотримуються правил коментування PHPDoc. Але на практиці це буває рідко. Стандарту дотримуються лише перфекціоністи, а також ті, хто давно користується докблоками і це стало звичкою. Деякі програмісти-початківці хочуть використовувати док-блоки, але іноді забувають їх використовувати або не до кінця розбираються в тегах. Звісно, є й міцні горішки, які не користуються док-блоками, навіть якщо команда ними користується.

Щоб зменшити дискомфорт, вам слід увімкнути перевірку докблок в PhpStorm для кожного члена команди: Settings > Editor > Inspections > PHPDoc і відзначити всі галочки:

Code commenting and PHP documentation generation

Так само слід використовувати PHP CodeSniffer (phpcs) з відповідним вам код стайлом. Не впевнений, як щодо всіх код стайлів, але для Symfony докблоки є обов'язковими. Тому phpcs у "Штормі" видаватиме вам попередження на льоту. Налаштування робляться в тому ж місці, а ось ще є додаткова інструкція.

Звичайно, ви не можете змусити всіх слідувати правилам. Для найледачіших я хотів би ще раз надати пораду з "Чистого коду" (мова йде про форматування коду, але наслідки ті ж самі):“Правила повинні дотримуватися всіма членами групи. Це означає, що кожен член групи повинен бути достатньо розумним, щоб розуміти, що не має значення, як будуть розставлені дужки, якщо тільки всі не домовилися розставляти їх однаково.”

Генерація документації за допомогою PhpDocumentor

Тепер, коли всі дотримуються правил і ваш код сповнений докблок, ви можете генерувати документацію. Я багато пишу про документацію phpDocumentor, покажу лише найнеобхідніші команди, інші найкращі практики документування php коду ви можете знайти на офіційному сайті.

Отже, вам потрібно встановити phpDocumentor. Глобально його можна встановити таким чином:

$ wget http://www.phpdoc.org/phpDocumentor.phar
$ chmod +x phpDocumentor.phar
$ sudo mv phpDocumentor.phar /usr/local/bin/phpdoc
$ phpdoc --version

Або додайте його як вимогу до composer.json вашого проекту.

$ composer require --dev "phpdocumentor/phpdocumentor:2.*"

І тепер, коли ви перебуваєте в каталозі проекту, повному докблок, просто запустіть його з консолі.

$ phpdoc -d src/

Як я вже згадував, це найнеобхідніший набір опцій для генерації документації, опція -d src/ показує шлях до файлів, з якими ви хочете працювати. Згенерована інформація буде розташована у папці з назвою output. Звичайно, ця утиліта має різні специфікації і безліч можливостей.

Генерація документації за допомогою Sami

Ще одним інструментом документування PHP на основі докблок є утиліта під назвою Sami. Можливо, вона не така популярна, але я вирішив згадати про неї, тому що документація Symfony генерується за допомогою Sami. І Фаб'єн Потенсьє, який створив цю утиліту, згадує про неї у своєму блозі.

Генератор документації Sami відрізняється від phpDocumentor тим, що його конфігурація зберігається у файлах PHP. Загалом, він має більший потенціал для налаштування, ніж phpDocumentor, але вам доведеться налаштовувати все це вручну, тобто писати код. Ви навіть можете перевизначити різні фрагменти коду, які відповідають за генерацію документації. Всі необхідні шаблони можна знайти на TWIG, до того ж їх легко перевизначити. Для більш детальної інформації про Sami перейдіть на сторінку GitHub.

Висновок

Це лише невеликий огляд проблеми документування коду на PHP та його коментування. Тут ви знайдете всього потроху, щоб заохотити бажання зануритися в цю тему глибше. Якщо ви початківець у PHP, раджу прочитати детальну документацію про phpDocumentor. Якщо ви досвідчений розробник, ви можете поділитися власним досвідом, написавши коментар нижче. ^_^