Doxygen — это система генерации документации. В Друпале, документирование функций, классов, констант и так далее, делается с помощью специально оформленных комментариев. Документация извлекается прямо из исходного кода, что делает более удобным хранение документации вместе с исходным кодом. На сайте Doxygen находится замечательное руководство по этой системе. Следующие рекомендации имеют отношению к работе с Doxygen в Друпале.
Эти стандартны применяются как к внутристрочным, так и к вынесенным комментариям:
Приветствуется использование внутристрочных и невынесенных комментариев общего характера. Общее правило их использования: если вы смотрите на код и думаете про себя «Ого! Я даже не хочу попробовать это описать», — вам нужно обязательно это описать, пока вы не забыли как это работает.
Комментарий должен идти перед строкой или блоком кода к которому он относиться и не должен отделяться от него пустой строкой, например:
// Unselect all other contact categories.
db_query('UPDATE {contact} SET selected = 0');
В случае, если комментирование требуется для каждой строки кода и используются внутристрочные комментарии, для их выравнивания могут использоваться пробелы — это улучшает чтение.
Используйте комментирование в стиле C (/* */) или в стиле C++ (//). Комментирование в стиле Perl/shell (#) не приветствуется.
Чтобы документировать блок определённого кода, например, функции, класса, метода, константу и так далее, используется следующий синтаксис:
/**
* Текст документации.
*/
Doxygen поймёт любые комментарии расположенные в таком блоке. Ваш стиль должен содержать по возможности команды стандартные для Doxygen, таким образом, мы сохраняем источник разборчивым. Любые упоминания функций или названий файлов в документации автоматически создадут ссылки на тот код, на который они ссылаются. Блок с документацией должен идти перед описываемым кодом, без пустых строк между блоком и кодом.
/**
* Краткое описание; одно предложение на строке (не более 80 знаков).
*
* Более подробное описание.
*
* Пустые строки формируют абзацы. В конце строк не должно быть пробелов.
*
* @param $first
* "@param" это директива Doxygen, описывающая параметр функции. Как и другие
* директивы, она состоит из термина/краткого описания на одной строке и
* описания (этот текст) с отступом в 2 пробела на следующей строке. Весь текст
* строки не должен быть больше 80 знаков без перехода. Эта директива не
* поддерживает пустые строки; если перед этим текстом будет пустая строка, то
* он будет добавлен в основное описание выше.
* @param $second
* Между несколькими директивами одного типа не должно быть пустых строк.
* @param $third
* (дополнительно) TRUE если Third должен быть. Стандартно FALSE. Только
* дополнительные параметры явно фиксируются. Описание должно объяснять
* стандартное значение, если оно опущено.
*
* @return
* "@return" это директива Doxygen, описывающая возвращаемое функцией значение.
*/
function mymodule_foo($first, $second, $third = FALSE) {
}
* @param $variables
* Ассоциативный массив содержащий:
* - tags: массив меток для управления пейджером:
* - first: строка использующаяся для первого элемента пейджера.
* - last: строка использующаяся для последнего элемента пейджера.
* - element: (дополнительно) целое число, чтобы отличать несколько пейджеров
* на странице. Стандартное значение = 0 (ноль).
* - style: целое число, являющееся одной из следующих констант:
* - PAGER_FULL: (стандартно) полный пейджер.
* - PAGER_MINI: мини-пейджер.
* Дальнейшее описание по-прежнему будет принадлежать данному параметру, но
* оно не будет являться частью списка.
*
* Это уже не принадлежит параметру.
В Doxygen, списки могут находиться где угодно. Парсер документации требует строгого следования синтаксису, чтобы текст мог быть корректно выведен в HTML-формате:
/**
* (rest of function/file/etc. header)
*
* @see foo_bar()
* @see ajax.inc
* @see MyModuleClass
* @see MyClass::myMethod()
* @see groupname
* @see http://drupal.org/node/1354
*/
The @see directive may be used to link to (existing) functions, files, classes, methods, constants, groups/topics, URLs, etc. @see directives should always be placed on their own line, and generally at the bottom of the documentation header. Use the same format in @see that you would for automatic links (see below, or see examples above). In addition, you can link to a group/topic using the internal name of the group (what follows the @defgroup in its definition).
/**
* (rest of function/file/etc. header)
*
* See also @link group_name Topic name goes here, @endlink and the
* @link http://example.com web page explaining it further. @endlink
*/
The @link...@endlink directive may be used to output a HTML link in line with the text. Some notes:
* This function invokes hook_foo() on all implementing modules.
* It calls MyClass::methodName(), which includes foo.module as
* a side effect.
Any function, file, constant, class, etc. in the documentation will automatically be linked to the page where that item is documented (assuming that item has a doxygen header). For functions and methods, you must include () after the function/method name to get the link. For files, just put the file name in (not the path to the file). Groups will not be automatically linked; use @see or @link.
* Пример использования:
* @code
* mymodule_print('Hello World!');
* @endcode
* Текст следующий за блоком кода.
Примеры кода могут быть встроены в документацию Doxygen с использованием директив @code и @endcode. Любой код между этими директивами будет выводиться отформатированным.
/**
* ...
* Предыдущий текст (оставьте пустую строку перед директивой @todo).
*
* @todo Удалить это в Д8.
* @todo Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
* nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed
* diam voluptua.
*/
To document known issues and development tasks in code, @todo statements may be used. Notes:
// Здесь какой-то другой комментарий.
// @todo Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
// nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
// sed diam voluptua.
// Явно удаляем все комментарии.
comment_delete($cid);
@todo statements in inline comments follow the same rules as above, especially regarding indentation of subsequent comments, but you don't need to leave a blank line between other comments and the @todo line.
Каждый файл должен начинаться с комментария, который описывает то, что он делает. Пример:
/**
* @file
* The theme system, which controls the output of Drupal.
*
* The theme system allows for nearly all output of the Drupal system to be
* customized by user themes.
*/
Note that a blank line should follow a @file documentation block.
Строка, которая идёт сразу за директивой @file, это описание, которое показывается на странице со списком всех файлов. Если строка начинается с глагола, то он должен быть в настоящем времени, например, «Обрабатывает загрузку файлов». Описание, которое не нужно добавлять на страницу со списком всех файлов, должно идти после пустой строки.
Вообще, директива @file не должна содержать подробного описания, которое лучше поместить в директиву @defgroup. Таким образом, разработчики смогут просматривать документацию верхнего уровня, пользуясь группами (смотрите список тем/групп API ядра на странице http://api.drupal.org/api/drupal/groups).
Для файлов .install используйте шаблон:
/**
* @file
* Install, update, and uninstall functions for the XXX module.
*/
Для файлов содержащих определение только одного класса, используйте шаблон:
/**
* @file
* Definition of NameOfTheClass.
*/
Все функции и методы должны быть документированы. Блок комментирования функции должен ей непосредственно предшествовать.
/**
* Verifies the syntax of the given e-mail address.
*
* Empty e-mail addresses are allowed. See RFC 2822 for details.
*
* @param $mail
* A string containing an email address.
*
* @return
* TRUE if the address is in a valid format, and FALSE if it isn't.
*/
function valid_email_address($mail) {
Note that some types of functions have special forms of documentation that do not follow this exact format -- see sections below for details (e.g., hook declarations, form generating functions, etc.).
* @param ...
* Description of the variable arguments goes here.
If there is no return value, omit the @return directive entirely.
/**
* Converts an associative array to an anonymous object.
*/
function mymodule_array2object($array) {
If the abbreviated syntax is used, the parameters and return value must be described within the one-line summary.
The data type of a parameter or return value MUST be specified in @param or @return directives, if it is not obvious or of a specific class or interface type.
The data type MAY be specified (as in: recommended) for other @param or @return directives, including those with primitive data types, arrays, and generic objects (stdClass).
* @param int $number
* Description of this parameter, which takes an integer.
* @param float $number
* Description of this parameter, which takes a floating-point number.
* @param string $description
* Description of this parameter, which takes a string.
* @param bool $flagged
* Description of this parameter, which takes a Boolean TRUE/FALSE value.
* @param array $args
* Description of this parameter, which takes a PHP array value.
* @param object $account
* Description of this parameter, which takes a PHP stdClass value.
* @param DatabaseStatementInterface $query
* Description of this parameter, which takes an object of a class that
* implements the DatabaseStatementInterface interface.
* @param string|bool $string_or_boolean
* Description of this parameter, which takes a string or a Boolean TRUE/FALSE
* value.
* @param string|null $string_or_null
* (optional) Description of this optional parameter, which takes a string or
* can be NULL.
*
* @return string
* Description of the return value, which is a string.
Отметьте:
* Callback for uasort() within system_themes_page().
This line should indicate which PHP or Drupal function directly takes this callback as an argument, and also which Drupal API function calls that function. If multiple API functions call it, format as a list.
Note that there are also specific standards for some functions that might be considered callbacks, such as hook implementations, menu callbacks, and form callbacks.
/**
* Отвечает за удаление документа.
*
* Этот хук вызывается функцией node_delete_multiple() после удаления
* документа из таблицы {node}, после вызова определённого типа hook_delete()
* и до вызова field_attach_delete().
*
* @param $node
* Удаляемый документ.
*
* @ingroup node_api_hooks
* @ingroup hooks
*/
function hook_node_delete($node) {
db_delete('mytable')
->condition('nid', $node->nid)
->execute();
}
Документирование определения хука следует тем же правилам, что и документирование обычных функций (смотрите выше) за исключением того, что в первой строке используется глагол в повелительном наклонении. Кроме того, приводится пример содержания функции, обычно из реализации хука в модуле ядра. Также полезно упомянуть откуда вызывается хук.
If the implementation of a hook is rather standard and does not require more explanation than the hook reference provides, a shorthand documentation form may be used in place of the full function documentation block described above:
/**
* Implements hook_help().
*/
function blog_help($section) {
// ...
}
This generates a link to the hook reference, reminds the developer that this is a hook implementation, and avoids having to document parameters and return values that are the same for every implementation of the hook.
Optionally, you can add more information in a separate paragraph to describe the particular quirks of your hook implementation. But do not include documentation of the parameters and return values -- this should be in the hook definition documentation, not in the hook implementation documentation.
In the case of hooks that have variables in the names, such as hook_form_FORM_ID_alter(), a slightly expanded syntax should be used:
/**
* Implements hook_form_FORM_ID_alter() for node_type_form().
*/
function mymodule_form_node_type_form_alter(&$form, &$form_state) {
// ...
}
This generates a link to the hook reference, as well as to the particular form that is being altered. Again, optionally you can add more information in a separate paragraph to describe the particular quirks of your hook implementation.
Theme preprocess functions work the same as hook_form_FORM_ID_alter(). Example:
/**
* Implements hook_preprocess_HOOK() for theme_panels_pane().
*/
function mymodule_preprocess_panels_pane(&$variables) {}
Note that the "for ..." part could reference either a theme function like theme_panels_pane(), or a theme template file like search-result.tpl.php.
For hook_update_N() implementations, there is a different standard, because the documentation's first line is displayed to tell people what will happen (or did happen) when update.php is run. So they are documented as follows:
/**
* Add several fields to the {node_revision} table.
*
* Longer description can go here, if necessary.
*/
function module_name_update_7002() {
}
Note that the first line should start with an imperative verb, so it will make sense to people running update.php.
/**
* Form constructor for the user login form.
*
* @param $message
* The message to display.
*
* @see user_login_form_validate()
* @see user_login_form_submit()
* @ingroup forms
*/
function user_login_form($form, &$form_state, $message = '')
/**
* Form validation handler for user_login_form().
*
* @see user_login_form_submit()
*/
function user_login_form_validate($form, &$form_state) {
...
}
/**
* Form submission handler for user_login_form().
*
* @see user_login_form_validate()
*/
function user_login_form_submit($form, &$form_state) {
...
}
Отметьте:
If the form generates a page (i.e., in a hook_menu() implementation, the page callback is 'drupal_get_form' and the page argument is this form), include an @see reference to the hook_menu() implementation that uses it.
The Render API uses various callbacks functions, which you can assign to a render element when setting up a render array (or a form). Document them as follows:
/**
* Render API callback: Validates the maximum upload size field.
*
* Ensures that a size has been entered and that it can be parsed by
* parse_size().
*
* This function is assigned as an #element_validate callback in
* file_field_instance_settings_form().
*/
function _file_generic_settings_max_filesize($element, &$form_state) {
Отметьте:
Этот блок добавлен для Друпала 8.
/**
* Returns HTML for a foo.
*
* @param $variables
* An associative array containing:
* - foo: The foo object that is being formatted.
* - show_bar: TRUE to show the bar component, FALSE to omit it.
*
* @ingroup themeable
*/
function theme_foo($variables) {
...
}
In order to provide a quick reference for themers, we tag all themeable functions so that Doxygen can group them together. A themeable function is defined as any function meant to be called via theme(), and you can indicate that a function is themeable by adding an @ingroup themeable directive. It is also beneficial to use @see directives to link the theme function with its preprocessing functions.
In the rare case that your theme function returns some other type of formatted output other than HTML, you should add a @return directive with a description of what the function generates and returns.
See also the hook implementations section for how to document preprocess functions.
If a template file (themeablename.tpl.php) and a preprocess function is used instead of a theming function, both the template file and the preprocessing function need to be documented. The template file should be documented with a @file directive, should have @ingroup themeable in the file documentation header, and should contain a list of the variables that the template_preprocess_HOOK has prepared for it. If any of these variables contain data that is unsafe to output for XSS reasons, that should be documented; otherwise it can be assumed that variables available have already been appropriately filtered. Anything not listed should not be assumed to be safe to output. It should also contain a @see directive to link back to the preprocessing function.
/**
* @file
* Displays a list of forums.
*
* Available variables:
* - $forums: An array of forums to display. Each $forum in $forums contains:
* - $forum->is_container: Is TRUE if the forum can contain other forums. Is
* FALSE if the forum can contain only topics.
* - $forum->depth: How deep the forum is in the current hierarchy.
* - $forum->name: The name of the forum.
* - $forum->link: The URL to link to this forum.
* - $forum->description: The description of this forum.
* - $forum->new_topics: True if the forum contains unread posts.
* - $forum->new_url: A URL to the forum's unread posts.
* - $forum->new_text: Text for the above URL which tells how many new posts.
* - $forum->old_topics: A count of posts that have already been read.
* - $forum->num_posts: The total number of posts in the forum.
* - $forum->last_reply: Text representing the last time a forum was posted
* or commented in.
*
* @see template_preprocess_forum_list()
*
* @ingroup themeable
*/
See also the hook implementations section for how to document preprocess functions.
(This standard was adopted for Drupal version 8.x.) The page, access, title, and other callbacks that are registered in hook_menu() are documented as follows:
/**
* Page callback: Displays a list of content.
*
* Longer description can go here.
*
* @param $foo
* Description of a parameter for this page.
*
* @return
* A render array for a page containing a list of content.
*
* @see node_menu()
*/
Отметьте:
/**
* Denotes that a block is not enabled in any region and should not be shown.
*/
define('BLOCK_REGION_NONE', -1);
/**
* The base URL of the drupal installation.
*
* @see conf_init()
*/
global $base_url;
Constants, global variables, and member variables of classes should have documentation blocks. As usual, the documentation block should start with a one-line description, and any additional needed documentation should be separated from the summary line by a blank line (though usually only one line is needed).
Each class and interface should have a doxygen documentation block, and each member variable, constant, and function/method within the class or interface should also have its own documentation block. Example:
<?php
/**
* Defines a common interface for a prepared statement.
*/
interface DatabaseStatementInterface extends Traversable {
/**
* Executes a prepared statement.
*
* @param array $args
* Array of values to substitute into the query.
* @param array $options
* Array of options for this query.
*
* @return
* TRUE on success, FALSE on failure.
*/
public function execute($args = array(), $options = array());
}
/**
* Represents a prepared statement.
*
* Default implementation of DatabaseStatementInterface.
*/
class DatabaseStatementBase extends PDOStatement implements DatabaseStatementInterface {
/**
* The database connection object for this statement's DatabaseConnection.
*
* @var DatabaseConnection
*/
public $dbh;
/**
* Constructs a DatabaseStatementBase object.
*
* @param DatabaseConnection $dbh
* Database connection object.
*/
protected function __construct($dbh) {
// Function body here.
}
/**
* Implements DatabaseStatementInterface::execute().
*
* Optional explanation of the specifics of this implementation goes here.
*/
public function execute($args = array(), $options = array()) {
// Function body here.
}
/**
* Returns the foo information.
*
* @return object
* The foo information as an object.
*
* @throws MyFooUndefinedException
*/
public function getFoo() {
// Function body here.
}
/**
* Overrides PDOStatement::fetchAssoc().
*
* Optional explanation of the specifics of this override goes here.
*/
public function fetchAssoc() {
// Call PDOStatement::fetch to fetch the row.
return $this->fetch(PDO::FETCH_ASSOC);
}
}
?>
Отметьте:
You can make "Topics" pages by defining a "group", also known as a "module" in Doxygen terminology. We use topic/group pages to group functions that form an API that is meant to be used by multiple modules. Do not define a topic/group for functions that are internal to a particular file or module. See http://api.drupal.org/api/drupal/groups for a list of existing API topics/groups in Drupal Core.
Here is the basic syntax for defining a group:
/**
* @defgroup group_identifier Name of the group for Topics page
* @{
* One line, one sentence description of the topic/group.
*
* One or more additional paragraphs of information, which will appear
* on the topic page.
* @}
*/
The @defgroup line gives the name of the group that will be used in the URL, and then the name that will be used as the page title and in the Topics listing. Any text within the @defgroup's own doxygen header appears as text on the topic page.
If you put the group definition immediately before one or more functions that belong in the group, you can automatically add functions to the group by using this syntax:
/**
* @defgroup group_identifier Name of the group for Topics page
* @{
* One line, one sentence description of the topic/group.
*
* One or more additional paragraphs of information, which will appear
* on the topic page.
*/
// Functions here are automatically added to the group.
/**
* @} End of "defgroup group_identifier".
*/
If you have functions that need to be added to a group whose definition is not right above the functions, there are two ways to do it:
to the documentation block for the function.
/**
* @addtogroup group_identifier
* @{
*/
before the functions, and
/**
* @} End of "addtogroup group_identifier".
*/
after the group of functions.
Отметьте:
Drupal's Doxygen processing module, API module, currently only supports a small subset of all Doxygen commands and makes some assumptions about the formatting of the source. Code to be processed by api.module is advised to stick to these conventions.
The API module currently supports only one of Doxygen's three grouping mechanisms: Modules (@defgroup, @ingroup, @addtogroup, @{, @}). See groups section above for more information on how we use modules/groups.