Skip to content

PHPDoc PSR-5

=============

Оглавление

1. Введение

Основная цель этого PSR — предоставить полное и формальное определение стандарта PHPDoc. Этот PSR отличается от своего предшественника, де-факто стандарта PHPDoc, связанного с phpDocumentor 1.x, чтобы обеспечить поддержку новых функций языка PHP и устранить некоторые недостатки своего предшественника.

Этот документ НЕ ДОЛЖЕН:

  • Описывать стандарт реализации аннотаций через PHPDoc. Хотя он предлагает универсальность, которая позволяет создать последующий PSR на основе текущих практик. Дополнительную информацию по этой теме см. в главе 5.3.
  • Описывать лучшие практики или рекомендации для стандартов кодирования по применению стандарта PHPDoc. Этот документ ограничен формальной спецификацией синтаксиса и намерений.

2. Условные обозначения, используемые в этом документе

Слова «НЕОБХОДИМО» / «ДОЛЖНО» ("MUST"), «НЕДОПУСТИМО» ("MUST NOT"), «ТРЕБУЕТСЯ» ("REQUIRED"), «НУЖНО» ("SHALL"), «НЕ ПОЗВОЛЯЕТСЯ» ("SHALL NOT"), «СЛЕДУЕТ» ("SHOULD"), «НЕ СЛЕДУЕТ» ("SHOULD NOT"), «РЕКОМЕНДУЕТСЯ» ("RECOMMENDED"), «МОЖЕТ» / «ВОЗМОЖНО» ("MAY") и «НЕОБЯЗАТЕЛЬНО» ("OPTIONAL") в этом документе следует понимать так, как это описано в RFC-2119 (и его переводе).

3. Определения

  • «PHPDoc» — это раздел документации, в котором содержится информация об аспектах «Структурного элемента».

Важно отметить, что PHPDoc и DocBlock — это два отдельных объекта. DocBlock представляет собой комбинацию DocComment, который является типом комментария, и объекта PHPDoc. Это объект PHPDoc, который содержит синтаксис, описанный в этой спецификации (например, описание и теги).

  • «Структурный элемент» — это набор программных конструкций, которым МОЖЕТ предшествовать DocBlock. Коллекция содержит следующие конструкции:

  • require(_once)

  • include(_once)
  • class
  • interface
  • trait
  • function (including methods)
  • property
  • constant
  • variables, both local and global scope.

РЕКОМЕНДУЕТСЯ ставить перед «Структурным элементом» DocBlock там, где он определен, а не при каждом использовании. Общепринятой практикой является размещение DocBlock перед структурным элементом, но он также МОЖЕТ быть разделен неопределенным количеством пустых строк.

Пример:

  /** @var int $int Это счетчик. */
  $int = 0;

  // здесь не должно быть DocBlock
  $int++;

или

  /**
   * Этот класс выступает в качестве примера того, где разместить DocBlock.
   */
  class Foo
  {
      /** @var string|null $title содержит заголовок для Foo */
      protected $title = null;

      /**
       * Задает однострочный заголовок.
       *
       * @param string $title Текст для заголовка.
       *
       * @return void
       */
      public function setTitle($title)
      {
          // здесь не должно быть DocBlock
          $this->title = $title;
      }
  }

НЕ РЕКОМЕНДУЕТСЯ использовать составные определения для Констант или Свойства, так как обработка DocBlocks в этих ситуациях может привести к неожиданным результатам. Если используется составной оператор, каждый элемент ДОЛЖЕН иметь предшествующий DocBlock.

Например:

    class Foo
    {
      protected
        /**
         * @var string Должен содержать название
         */
        $name,
        /**
         * @var string Должен содержать описание
         */
        $description;
    }

Примером использования, выходящим за рамки настоящего стандарта, является явное документирование переменной в foreach; некоторые IDE используют эту информацию, чтобы помочь своим функциям автозаполнения.

Этот стандарт не распространяется на этот конкретный случай, поскольку оператор foreach считается оператором «потока управления», а не «структурным элементом».

  /** @var \Sqlite3 $sqlite */
  foreach ($connections as $sqlite) {
      // здесь не должно быть докблока
      $sqlite->open('/my/database/path');
      <...>
  }
  • «DocComment» — это особый тип комментария, который ДОЛЖЕН

  • начинаться с последовательности символов /**, за которой следует пробел

  • заканчиваться на */ и
  • иметь ноль или более строк между ними.

В случае, когда DocComment занимает несколько строк, каждая строка ДОЛЖНА начинаться со звездочки (*), которая ДОЛЖНА быть выровнена с первой звездочкой открывающего предложения.

Пример одной строки:

  /** <...> */

Многострочный пример:

    /**
     * <...>
     */
  • «DocBlock» — это «DocComment», содержащий единую структуру «PHPDoc» и представляющий базовое представление в исходном коде.

  • «Тег» — это отдельная часть метаинформации, касающаяся «Структурного элемента» или его компонента.

  • «Тип» — это определение того, какой тип данных связан с элементом. Это обычно используется при определении точных значений аргументов, констант, свойств и многого другого.

См. Приложение A для более подробной информации о типах.

  • «FQSEN» — это аббревиатура от Fully Qualified Structural Element Name. Эта нотация расширяет полное имя класса и добавляет нотацию для идентификации членов класса/интерфейса/признака и повторного применения принципов FQCN к интерфейсам, признакам, функциям и глобальным константам.

Для каждого типа «Структурного элемента» могут использоваться следующие обозначения:

  • Namespace: \My\Space
  • Function: \My\Space\myFunction()
  • Constant: \My\Space\MY_CONSTANT
  • Class: \My\Space\MyClass
  • Interface: \My\Space\MyInterface
  • Trait: \My\Space\MyTrait
  • Method: \My\Space\MyClass::myMethod()
  • Property: \My\Space\MyClass::$my_property
  • Class Constant: \My\Space\MyClass::MY_CONSTANT

FQSEN имеет следующее определение ABNF:

  FQSEN    = fqnn / fqcn / constant / method / property  / function
  fqnn     = "\" [name] *("\" [name])
  fqcn     = fqnn "\" name
  constant = (fqnn "\" / fqcn "::") name
  method   = fqcn "::" name "()"
  property = fqcn "::$" name
  function = fqnn "\" name "()"
  name     = (ALPHA / "_") *(ALPHA / DIGIT / "_")

Примечание от переводчика. Технические спецификации Интернета часто должны определять формальный синтаксис. С годами появилась модифицированная версия формы Бэкуса-Наура. (BNF), называемый расширенным BNF (ABNF), поскольку эта спецификация была популярна среди многих спецификаций Интернета. Текущая спецификация документирует ABNF. Она сочетает в себе компактность и простоту с разумными ограничениями. Различия между стандартным BNF и ABNF включают в себя правила именования, повторения, альтернативы, порядок- независимость и диапазон значений. Эта спецификация также предоставляет дополнительные определения правил и кодировок для основного лексического анализатора типа, общего для нескольких интернет-спецификаций.

4. Основные принципы

  • PHPDoc всегда ДОЛЖЕН содержаться в "DocComment"; комбинация этих двух элементов называется «DocBlock».

  • DocBlock ДОЛЖЕН непосредственно предшествовать «Структурному элементу».

5. Формат PHPDoc

Формат PHPDoc имеет следующий ABNF определение:

PHPDoc             = [summary] [description] [tags]
summary            = *CHAR (2*CRLF)
description        = 1*(CHAR / inline-tag) 1*CRLF ; any amount of characters
                                                 ; with inline tags inside
tags               = *(tag 1*CRLF)
inline-tag         = "{" tag "}"
tag                = "@" tag-name [":" tag-specialization] [tag-details]
tag-name           = (ALPHA / "\") *(ALPHA / DIGIT / "\" / "-" / "_")
tag-specialization = 1*(ALPHA / DIGIT / "-")
tag-details        = *SP (SP tag-description / tag-signature )
tag-description    = 1*(CHAR / CRLF)
tag-signature      = "(" *tag-argument ")"
tag-argument       = *SP 1*CHAR [","] *SP

Примеры использования включены в главу 5.4.

5.1. Резюме

Резюме ДОЛЖНО содержать краткое описание «Структурного элемента», определяющего цель. РЕКОМЕНДУЕТСЯ, чтобы Резюме занимало одну или максимум две строки, но не более того.

Резюме ДОЛЖНО заканчиваться двумя последовательными разрывами строк, если только оно не является единственным содержимым в PHPDoc.

Если предоставлено описание, то ему ДОЛЖНО предшествовать резюме. В противном случае Описание будет считаться Сводкой до тех пор, пока не будет достигнут конец Сводки.

Поскольку Резюме можно сравнить с названием главы, полезно использовать как можно меньше форматирования. Таким образом, вопреки описанию (см. следующую главу), не делается никаких рекомендаций по поддержке языка разметки. Это явно оставлено на усмотрение реализующего приложения, хочет ли оно поддерживать это или нет.

5.2. Описание

Описание является НЕОБЯЗАТЕЛЬНЫМ, но ДОЛЖНО быть включено, когда «Структурный элемент», которому предшествует этот DocBlock, содержит больше операций или более сложных операций, чем может быть описано в одном резюме.

Любому приложению, анализирующему описание, РЕКОМЕНДУЕТСЯ поддерживать язык разметки Markdown для этого поля, чтобы автор мог обеспечить форматирование и четкий способ представления примеров кода.

Обычно описание используется (среди прочего):

  • Чтобы предоставить более подробную информацию, чем сводка, о том, что делает этот метод.
  • Чтобы указать, из каких дочерних элементов состоит входной или выходной массив или объект.
  • Предоставить набор общих вариантов использования или сценариев, в которых может применяться «Структурный элемент».

5.3. Теги

Теги позволяют авторам предоставлять краткие метаданные о последующем «Структурном элементе». Каждый тег начинается с новой строки, за ним следует знак @ и имя тега, за которым следует пробел и метаданные (включая описание).

Если предоставлены метаданные, они МОГУТ охватывать несколько строк и МОГЛИ следовать строгому формату и, таким образом, предоставлять параметры в соответствии с типом тега. Тип тега может быть получен из его имени.

Например:

@param string $argument1 Это параметр.

Вышеупомянутый тег состоит из имени («param») и метаданных («строка $argument1. Это параметр».), где метаданные разделены на «Тип» («string»), имя переменной (' $argument') и описание ("Это параметр").

Описание тега ДОЛЖНО поддерживать Markdown в качестве языка форматирования. Из-за характера уценки допустимо начинать описание тега с той же или последующей строки и интерпретировать его таким же образом.

Таким образом, следующие теги семантически идентичны:

/**
 * @var string Это описание.
 * @var string Это 
 *  описание.
 * @var string
 *    Это описание.
 */

Вариантом этого является то, что вместо описания используется подпись тега; в большинстве случаев тег фактически будет «Аннотацией». Сигнатура тега может предоставить аннотации параметры, касающиеся ее работы.

Если присутствует подпись тега, то НЕ ДОЛЖНО присутствовать описание в том же теге.

Метаданные, предоставляемые тегами, могут привести к изменению фактического поведения следующего «структурного элемента» во время выполнения, и в этом случае вместо «тега» обычно используется термин «аннотация».

5.3.1. Название тэга

Имена тегов указывают, какой тип информации представлен этим тегом, или, в случае аннотаций, какое поведение должно быть введено в последующий «Структурный элемент».

В поддержку аннотаций допускается введение набора тегов, разработанных специально для отдельного приложения или подмножества приложений (и, следовательно, не охватываемых данной спецификацией).

Эти теги или аннотации ДОЛЖНЫ предоставлять пространство имен либо

  • префикс имени тега с пространством имен в стиле PHP или
  • префикс имени тега с одним именем поставщика, за которым следует дефис.

Пример имени тега с префиксом пространства имен в стиле php (косая черта перед префиксом НЕОБЯЗАТЕЛЬНА):

@\Doctrine\Orm\Mapping\Entity()

Примечание: Стандарт PHPDoc НЕ делает предположений о значении тега, если это не указано в этом документе или последующих дополнениях или расширениях.

Это означает, что вы МОЖЕТЕ использовать псевдонимы пространства имен, если предоставляется префиксный элемент пространства имен. Таким образом, следующее также является законным:

@Mapping\Entity()

Ваша собственная библиотека или приложение могут проверять псевдонимы пространств имен и создавать из них FQCN; это не влияет на настоящий стандарт.

Важно: Инструменты, использующие стандарт PHPDoc, МОГУТ интерпретировать пространства имен, которые зарегистрированы в этом приложении, и применять настраиваемое поведение.

Пример имени тега с префиксом имени поставщика и дефисом:

@phpdoc-event transformer.transform.pre

Имена тегов без префикса поставщика или пространства имен ДОЛЖНЫ быть описаны в Каталоге тегов PSR и/или любом официальном приложении.

5.3.2. Специализация тегов

Чтобы предоставить метод, с помощью которого можно придать нюансы тегам, определенным в этом стандарте, но без расширения базового набора, после имени тега МОЖЕТ быть предоставлена специализация тега путем добавления двоеточия, за которым следует строка, обеспечивающая более подробное описание. тега. Список поддерживаемых специализаций тегов не сохраняется в Каталог тегов PSR, так как он может меняться со временем. Метадокумент Tag Catalog PSR может содержать ряд рекомендаций для каждого имени тега, но проекты могут свободно выбирать свою собственную специализацию тегов, если это применимо.

Важно: Инструменты, использующие стандарт PHPDoc, МОГУТ интерпретировать специализации тегов, которые зарегистрированы/поняты этим приложением, и применять настраиваемое поведение, но ожидается, что они реализуют только предшествующее имя тега, как определено в Каталоге тегов PSR.

Например:

@see:unit-test \Mapping\EntityTest::testGetId

Вышеприведенный тег состоит из имени («see») и специализации тега («unit-test») и, таким образом, определяет отношение к модульному тесту для текущего метода.

5.3.3. Подпись тега

Подписи тегов обычно используются для аннотаций для предоставления дополнительных метаданных, характерных для текущего тега.

Предоставленные метаданные могут влиять на поведение аннотации-владельца и, таким образом, влиять на поведение последующего «Структурного элемента».

Содержимое подписи должно определяться типом тега (как описано в имени тега) и выходит за рамки данной спецификации. Однако за подписью тега НЕ ДОЛЖНО следовать описание или другая форма метаданных.

5.4. Примеры

Следующие примеры служат для иллюстрации основного использования DocBlocks; рекомендуется прочитать список тегов в Каталог тегов PSR.

Полный пример может выглядеть так:

/**
 * Это резюме.
 *
 * Это Описание. Оно может занимать несколько строк или 
 * содержать примеры «кода» с использованием языка разметки _Markdown_.
 *
 * @see Markdown
 *
 * @param int        $parameter1 Описание параметра.
 * @param \Exception $e          Описание другого параметра.
 *
 * @\Doctrine\Orm\Mapper\Entity()
 *
 * @return string
 */
function test($parameter1, $e)
{
    ...
}

Также допускается опускать Описание:

/**
 * Это резюме.
 *
 * @see Markdown
 *
 * @param int        $parameter1 Описание параметра.
 * @param \Exception $parameter2 Описание другого параметра.
 *
 * @\Doctrine\Orm\Mapper\Entity()
 *
 * @return string
 */
function test($parameter1, $parameter2)
{
}

Или даже опустить раздел тегов (хотя это не рекомендуется, так как вам не хватает информации о параметрах и возвращаемом значении):

/**
 * Это резюме.
 */
function test($parameter1, $parameter2)
{
}

DocBlock также может занимать одну строку:

/** @var \ArrayObject $array */
public $array = null;

Приложение А. Типы

ABNF

Тип имеет следующее определение ABNF:

type-expression  = type *("|" type) *("&" type)
type             = class-name / keyword / array
array            = (type / array-expression) "[]"
array-expression = "(" type-expression ")"
class-name       = ["\"] label *("\" label)
label            = (ALPHA / %x7F-FF) *(ALPHA / DIGIT / %x7F-FF)
keyword          = "array" / "bool" / "callable" / "false" / "float" / "int" / "iterable" / "mixed" / "null" / "object" /
keyword          = "resource" / "self" / "static" / "string" / "true" / "void" / "$this"

Подробности

Когда используется «Тип», пользователь будет ожидать значение или набор значений, как подробно описано ниже.

Когда «Тип» состоит из нескольких типов, они ДОЛЖНЫ быть разделены либо знаком вертикальной черты (|) для типа объединения, либо амперсандом (&) для типа пересечения. Любой интерпретатор, поддерживающий эту спецификацию, ДОЛЖЕН распознать это и разделить «Тип» перед оценкой.

Пример типа союза:

@return int|null

Пример типа пересечения:

@var \MyClass&\PHPUnit\Framework\MockObject\MockObject $myMockObject

Массивы

Значение, представленное «Типом», может быть массивом. Тип ДОЛЖЕН быть определен в соответствии с форматом одной из следующих опций:

  1. неопределен: определение содержимого представляемого массива не дается. Пример: @return array

  2. указанный, содержащий один тип: определение типа информирует читателя о типе каждого значения массива. В этом случае для каждого значения в данном массиве ожидается только один тип.

Пример: @return int[]

Обратите внимание, что mixed также является одним типом, и с помощью этого ключевого слова можно указать, что каждое значение массива содержит любой возможный тип.

  1. указано как содержащее несколько типов: определение типа информирует читателя о типе каждого значения массива. Каждое значение может быть любого из заданных типов. Пример: @return (int|string)[]

Допустимое имя класса

Допустимое имя класса отображается в зависимости от контекста, в котором упоминается этот тип. Таким образом, это может быть либо полное имя класса (FQCN), либо локальное имя, если оно присутствует в пространстве имен.

Элемент, к которому применяется этот тип, является либо экземпляром этого класса, либо экземпляром класса, который является (под) дочерним по отношению к данному классу.

Из-за вышеуказанного для приложений, которые собирают и формируют эту информацию, РЕКОМЕНДУЕТСЯ отображать список дочерних классов с каждым представлением класса. Это сделало бы очевидным для пользователя, какие классы приемлемы в качестве типа.

Ключевое слово

Ключевое слово определяет назначение этого типа. Не каждый элемент определяется классом, но все же заслуживает классификации, чтобы помочь разработчику понять код, охватываемый DocBlock.

Примечание:

Большинство из этих ключевых слов разрешены в качестве имен классов в PHP, и их трудно отличить от реальных классов. Таким образом, ключевые слова ДОЛЖНЫ быть строчными, так как большинство имен классов начинаются с первого символа в верхнем регистре, и вы НЕ ДОЛЖНЫ использовать классы с такими именами в своем коде.

Есть и другие причины не называть классы именами этих ключевых слов, но это выходит за рамки данной спецификации.

В этом PSR распознаются следующие ключевые слова:

  1. bool: элемент, к которому применяется этот тип, имеет только состояние TRUE или FALSE.

  2. int: элемент, к которому применяется этот тип, является целым числом.

  3. float: элемент, к которому применяется этот тип, является непрерывным или действительным числом.

  4. string: элемент, к которому применяется этот тип, представляет собой строку двоичных символов.

  5. object: элемент, к которому применяется этот тип, является экземпляром неопределенного класса.

  6. array: элемент, к которому применяется этот тип, представляет собой массив значений.

  7. iterable: элемент, к которому применяется этот тип, является массивом или объектом Traversable согласно Итерирумым типам.

  8. resource: элемент, к которому применяется этот тип, является ресурсом согласно Ресурсному типу.

  9. mixed: элемент, к которому применяется этот тип, может быть любого типа, указанного здесь. Во время компиляции неизвестно, какой тип будет использоваться.

  10. void: этот тип обычно используется только при определении возвращаемого типа метода или функции, указывая, что «ничего не возвращается», и поэтому пользователь не должен полагаться на какое-либо возвращаемое значение.

    Пример 1:

    /**
     * @return void
     */
    function outputHello()
    {
        echo 'Hello world';
    }

В приведенном выше примере оператор возврата не указан, и поэтому возвращаемое значение не определено.

Пример 2:

    /**
     * @param bool $quiet когда истинное «Hello world» выводится.
     *
     * @return void
     */
    function outputHello($quiet)
    {
        if ($quiet) {
            return;
        }
        echo 'Hello world';
    }

В этом примере функция содержит оператор return без заданного значения. Поскольку фактическое значение не указано, это также квалифицируется как тип void.

  1. null: элемент, к которому применяется этот тип, имеет значение NULL или, с технической точки зрения, не существует.

    Большая разница по сравнению с void заключается в том, что этот тип используется в любой ситуации, когда описываемый элемент может в любой момент времени содержать явное значение NULL.

Пример 1:

    /**
     * @return null
     */
    function foo()
    {
        echo 'Hello world';
        return null;
    }

Этот тип обычно используется в сочетании с другим типом, чтобы указать, что, возможно, ничего не возвращается.

Пример 2:

    /**
     * @param bool $create_new Когда true возвращает новый stdClass.
     *
     * @return stdClass|null
     */
    function foo($create_new)
    {
        if ($create_new) {
            return new stdClass();
        }
        return null;
    }
  1. callable: элемент, к которому применяется этот тип, является указателем на вызов функции. Это может быть любой тип callable в соответствии с Функциями обратного вызова (callback-функции).

  2. false или true: элемент, к которому применяется этот тип, будет иметь значение TRUE или FALSE. Никакое другое значение не будет возвращено из этого элемента.

  3. self: элемент, к которому применяется этот тип, относится к тому же классу, в котором изначально содержится задокументированный элемент.

Пример:

Метод c содержится в классе A. DocBlock заявляет, что его возвращаемое значение имеет тип self. Таким образом, метод c возвращает экземпляр класса A.

Это может привести к запутанным ситуациям, когда речь идет о наследовании.

Пример (предыдущая примерная ситуация остается в силе):

Класс B расширяет класс A и не переопределяет метод c. Таким образом, можно вызвать метод c из класса B.

В этой ситуации может возникнуть двусмысленность, поскольку self может быть интерпретирован как класс A или B. В этих случаях self ДОЛЖЕН интерпретироваться как экземпляр класса, в котором записан DocBlock, содержащий тип self.

В приведенных выше примерах self всегда ДОЛЖЕН относиться к классу A, так как он определен с помощью метода c в классе A.

Из-за вышеуказанного характера РЕКОМЕНДУЕТСЯ для приложений, которые собирают и формируют эту информацию, отображать список дочерних классов с каждым представлением класса. Это сделало бы очевидным для пользователя, какие классы приемлемы в качестве типа.

  1. static: элемент, к которому применяется этот тип, относится к тому же классу, в котором содержится задокументированный элемент, или, если встречается в подклассе, относится к типу этого подкласса, а не к исходному классу.

    Это ключевое слово ведет себя так же, как Позднее статическое связывание (не статический метод, свойство или модификатор переменной), как определено в PHP.

  2. $this: элемент, к которому применяется этот тип, является точно таким же экземпляром, как текущий класс в данном контексте. Таким образом, этот тип является более строгой версией static, потому что возвращаемый экземпляр должен быть не только того же класса, но и того же экземпляра.

    Этот тип часто используется в качестве возвращаемого значения для методов, реализующих шаблон проектирования Fluent Interface.