The Drupal community has agreed that its code base must have a standardized look and feel to improve readability and make diving in easier for budding developers. Developers of contributed modules are encouraged to adopt these standards as well. Actually, let me be frank: your modules will not be taken seriously unless you follow the coding standards. I’ll cover
the standards first and then introduce a few automated tools to help you check your code (and even correct it for you!
Line Indention and Whitespace
Drupal code uses two spaces for indentation—not tabs. In most editors, you can set a preference to automatically replace
tabs with spaces, so you can still use the Tab key to indent if you’re working against the force of habit. Lines should
have notrailing whitespace at the end. Files should be formatted with a Unix \n as the end-of-line character and not with
the Windows standard \r\n. All text files should end in a single newline (\n).
Operators
All binary operators, such as +, -, =, !=, ==, >, etc., should have a space before and after the operator. For example,
an assignment should be formatted as c = a + b instead of c=a+b. Unary operators, such as ++, should not have a space
between the operator and the variable they are operating on.
Casting
You should put a space between the (type) and the $variable in a cast, such as (int) $count.
Control Structures
Control structures such as if, for, while, and switch should have one space between the control keyword and the opening
parenthesis, to distinguish them from function calls. For example the if statement below demonstrates the correct use and placement of opening parenthesis.
if (condition1 || condition2) { do something;
}
elseif (condition3 && condition4) { do something else;}
else {
just do this;}
You are strongly encouraged to use curly braces even in situations where they are technically optional. Having them
increases readability and decreases the likelihood of logic errors being introduced when new lines are added. Switch
statements are formatted as demonstrated here:
switch (condition) { case 1:
action1; break;
case 2: action2; break;
default: defaultaction;}
For do-while statements, the format is as follows:
do { actions;
} while ($condition);
Function Calls
In function calls, there should be a single space surrounding the operator (=, <, >, etc.) and no spaces between the nameof the function and the function’s opening parenthesis. There is also no space between a function’s opening parenthesis and
its first parameter. Middle function parameters are separated with a comma and a space, and the last parameter has no
space between it and the closing parenthesis. The following examples illustrate these points:
Incorrect
$var=foo ($bar,$baz);
Correct
$var = foo($bar, $baz);
There’s one exception to the rule. In a block of related assignments, more space may be inserted between assignment
operators if it promotes readability:
$a_value = foo($b);
$another_value = bar();
$third_value = baz();
Function Declarations
There should be no space between a function’s name and its opening parenthesis. When writing a function that uses default
values for some of its parameters, list those parameters last. Also, if your function generates any data that may be useful,
returning that data in case the caller wants to use it is a good practice. Some function declaration examples follow:
Incorrect
function foo ($bar = 'baz', $qux){
$value = $qux + some_function($bar);
}
Correct
function foo($qux, $bar = 'baz') {
$value = $qux + some_function($bar); return $value;}
Function Names
Function names in Drupal are in lowercase and based on the name of the module or system they are
part of. This convention avoids namespace collisions. Underscores are used to separate descriptive parts of the function
name. After the module name, the function should be named with the verb and the object of that verb: modulename_verb_object(). In the first following example, the incorrectly named function has no module prefix, and the verb and its
object are reversed. The subsequent example, obviously, corrects these errors.
Incorrect
function some_text_munge() {...}
Correct
function mymodule_munge_some_text() {...}
Private functions follow the same conventions as other functions but are prefixed with an underscore.
Class Constructor Calls
When calling class constructors with no arguments, always include parentheses, such as the following:
$foo = new MyClassName();
This is to maintain consistency with constructors that have arguments:
$foo = new MyClassName($arg1, $arg2);
Note that if the class name is a variable, the variable will be evaluated first to get the class name, and then the constructor will be called. An example of using a variable as a class name is as follows:
$bar = 'MyClassName';
$foo = new $bar();
$foo = new $bar($arg1, $arg2);
Arrays
Arrays are formatted with spaces separating each element and each assignment operator. If an array block spans more than 80 characters, each element should be moved to its own line. It’s good practice to put each element on its own line
anyway for readabilityand maintainability. This allows you to easily add or remove array elements.
Incorrect
$fruit['basket'] = array('apple'=>TRUE, 'orange'=>FALSE, 'banana'=>TRUE, 'peach'=>FALSE);
Correct
$fruit['basket'] = array( 'apple' => TRUE, 'orange' => FALSE, 'banana' => TRUE, 'peach' => FALSE,);
When creating internal Drupal arrays, such as menu items or form definitions, always list only one element on each line:
$form['flavors'] = array('#type' => 'select', '#title' => t('Flavors'), '#description' => t('Choose a flavor.'), '#options'=> $flavors,);
Quotes
Drupal does not have a hard standard for the use of single quotes vs. double quotes. Where possible, keep consistency
within each module, and respect personal styles of other developers. With that in mind, there is one caveat: single quote
strings are known tobe faster because the parser doesn’t have to look for inline variables. Single quotes are recommended
except in the following:
1. Inline variable usage, e.g., “<h2>$header</h2>”
2. Translated strings where one can avoid escaping single quotes by enclosing the string in double quotes.One such
string would be “He’s a good person.” It would be ‘He\’s a good person.’ with single quotes. Such escaping may not be
properly handled by .pot file generators for text translation, and it’s also a little awkward to read.
String Concatenators
You should always use a space between the dot and the concatenated parts to improve readability, as in the following
example:
$string = ‘Foo’ . $bar;
$string = $bar . ‘Foo’;
$string = bar() . ‘Foo’;
$string = ‘foo’ . ‘bar’;
When you concatenate simple variables, you can use double quotes and add the variable inside, such as the following example:
$string = "Foo $bar";
Comments
Drupal follows most of the Doxygen comment style guidelines. All documentation blocks must use the following syntax:
/**
* Documentation here.
*/
The leading spaces that appear before the asterisks (*) on lines after the first one are required.
When documenting a function, the documentation block must immediately precede the function it documents, with no intervening
blank lines. Drupal understands the Doxygen constructs in the following list; although I’ll cover the most common ones, please refer to the Doxygen site for more information on how to use them:
• @mainpage
• @file
• @defgroup
• @ingroup
• @addtogroup (as a synonym of @ingroup)
• @param
• @return
• @link
• @see
• @{
• @}
The beauty of adhering to these standards is that you can automatically generate documentation for your modules using the API
contributed module. The API module is an implementation of a subset of the Doxygen documentation generator specification, tuned
to produce output that best benefits a Drupal code base. You can see this module in action by visiting http://api.drupal.org, and you can learn more about the API module at http://drupal.org/project/api.
Documentation Examples
Let’s walk through the skeleton of a module from top to bottom and highlight the different types of documentation along the way.Before declaring functions, take a moment to document what the module does using the following format:
/**
* @file
* One-line description/summary of what your module does goes here.
*
* A paragraph or two in broad strokes about your module and how it behaves.
*/
Documenting Constants
PHP constants should be in all capital letters, with underscores separating proper words. When defining PHP constants, it’s a good
idea to explain what they’re going to be used for, as shown in the following code snippet:
/**
* Role ID for authenticated users; should match what's in the "role" table.
*/
define('DRUPAL_AUTHENTICATED_RID', 2);
Documenting Functions
Function documentation should use the following syntax:
/**
* Short description, beginning with a verb.
*
* Longer description goes here.
*
* @param $foo
* A description of what $foo is.
* @param $bar
* A description of what $bar is.
* @return
* A description of what this function will return.
*/
function name_of_function($foo, $bar) {
...
return $baz;}
The short description should begin with an imperative verb in the present tense, such as “Munge form data” or “Do remote address
lookups” (not “Munges form data” or “Does remote address lookups”). Let’s take a look at an example from Drupal core that is
found within system.module:
/**
* Add default buttons to a form and set its prefix.
*
* @param $form
* An associative array containing the structure of the form.
*
* @return
* The form structure.
*
* @see system_settings_form_submit()
* @ingroup forms
*/function system_settings_form($form) {
...
}
There are a couple of new Doxygen constructs in the preceding example:
• @see tells you what other functions to reference. The preceding code is a form definition, so @see points to the submit
handler for the form. When the API module parses this to produce documentation (such as that available athttp://api.drupal.org), it will turn the function name that follows @see into a clickable link.
• @ingroup links a set of related functions together. In this example, it creates a group of functions that provide form definitions.
You can create any group name you wish. Possible core values are: batch, database, file, format, forms, hooks, image, menu, node_access, node_content, schema api, search, theme able, and validation.
Functions that implement common Drupal constructs, such as hooks or form validation/ submission functions, may omit the full
@param and @return syntax but should still contain a one-line description of what the function does, as in this example:
/**
* Validate the book settings form.
*
* @see book_admin_settings()
*/
function book_admin_settings_validate($form, &$form_state) {
...
}
}
It is useful to know if a function is a menu callback (that is, mapped to a URL using hook_menu()):
/**
* Menu callback; prints a listing of all books.
*/
function book_render() {...}
Documenting Hook Implementations
When a function is a hook implementation, there is no need to document the hook. Simply state which hook is being implemented,as in the following example:
/**
* Implements hook_theme().
*/
function statistics_theme(){
...}
Including Code
Any where you are unconditionally including a class file, use required_once(). Anywhere you are including a class file, use include_once(). Either of these will ensure that class files are only included once. They share the same file list, so you don’t need to worryabout mixing them. A file included with require_once() will not be included again by a call to include_once(). An example of using
require_once is as follows:
require_once(DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc'));
PHP Code Tags
Always use <?php ?> to delimit PHP code and not the shorthand <? ?>. This is required for Drupal compliance and is also
the most portable way to include PHP code on different operating systems. The ?> is always omitted from the end of a code
file; this includes modules and include files. The reasons for this include the following:
1. Eliminating the possibility for unwanted whitespace at the end of files, which can cause “header already sent” errors, XHTML/XML validation issues, and other problems
2. The closing delimiter is optional.
3. PHP.net itself removes the closing delimiter from the end of its file, setting the best practice.
You should, however, use the closing ?> tag when you are mixing PHP and HTML and there is HTML that follows
the PHP code.
Semicolons
The PHP language requires semicolons at the end of most lines, but allows them to be omitted at the end of code blocks. Drupal
coding standards require them, even at the end of code blocks.
Example URLs
Use example.com for all example URLs per RFC 2606.
Naming Conventions
Functions and variables should be named using lowercase, and words should be separated by an underscore. Functions should in
addition have the grouping/module name as a prefix, to avoid name collisions between modules. Persistent variables (variables/
settings defined using Drupal’s variable_get()/variable_set()functions) should be named using all lowercase letters, and words shouldbe separated with an underscore. They should use the grouping/module name as a prefix, to avoid name collisions between
modules. Constants should always be in all uppercase, with underscores to separate words. This includes predefined PHP constants
like TRUE, FALSE, and NULL. Module-defined constant names should also be prefixed by an uppercase spelling of the module
they are defined by. Global variables should start with a single underscore followed by the module/theme name and another
underscore. Classes should be named using “CamelCase”—for example, DatabaseConnection. Class methods and properties
should use lower CamelCase, such as $lastStatement. The use of private class methods and properties should be avoided. You
should define classes as protected so that another class can extend your class and change the method if necessary.Protected and public methods and properties should not use an underscore prefix.All documentation files should have their file name extension set
to .txt to make viewing them on Windows systems easier. Also the file names for such files should be in all caps (e.g., README.
txt) while the extension itself should be in lowercase.
Checking Your Coding Style with Coder Module
At http://drupal.org/project/coder, you’ll find a treasure that will save you a lot of time and aggravation. It’s the coder module: a module that reviews the code in other modules. To have the coder module review your module, click the new “Code review” linkin your site navigation, and select the kind of review you want and the module or theme you would like to have reviewed. Or usethe handy Code Review link thatthis module provides on the list of modules.
You can even go a step further and use the coder_format.php script that comes with the coder module. The script actually fixesyour code formatting errors. Here is how to have coder_format.php check the annotate module we wrote in Chapter 2:
$ cd sites/all/modules
$ php contrib/coder/scripts/coder_format/coder_format.php \ custom/annotate/annotate.module
The script modifies the file annotate.module in place and saves the original as annotate.module.coder.orig. To see what the
script did, use diff:
$ diff custom/annotate/annotate.module custom/annotate/annotate.module.coder.orig
Finding Your Way Around Code with grep
grep is a Unix command that searches through files looking for lines that match a supplied regular expression. If you’re a Windows
user and would like to follow along with these examples, you can use grep by installing a precompiled version (see http://unxutils.sourceforge.net) or by installing the Cygwin environment (http://cygwin.com). Otherwise, you can just use the built-in search
functionality of the operating system rather than grep.
grep is a handy tool when looking for the implementation of hooks within Drupal core, finding the place where error messages are
being built, and so on. Let’s look at some examples of using grep from within the Drupal root directory:
$ grep -rl 'hook_init' .
./authorize.php
./includes/common.inc
./modules/simpletest/tests/system_test.module
./modules/simpletest/tests/theme_test.module
./modules/simpletest/tests/theme.test
./modules/simpletest/tests/actions_loop_test.module
./modules/locale/locale.module
./modules/dblog/dblog.module
./modules/update/update.module
./modules/system/system.api.php
./modules/system/system.module
./modules/overlay/overlay.install
./modules/overlay/overlay.module
./update.php
./themes/engines/phptemplate/phptemplate.engine
In the preceding case, we are recursively searching (-r) our Drupal files for instances of hook_init starting at the current directory
(.) and printing out the file names (-l) of the matching instances. Now look at this example:
$ grep -rn 'hook_init' .
./authorize.php:31: * avoid various unwanted operations, such as hook_init() and
./includes/common.inc:2697: * drupal_add_css() in a hook_init() implementation.
./includes/common.inc:2750: * theme .info files. Modules that add stylesheets within hook_init()
./includes/common.inc:3770: * drupal_add_css() in a hook_init() implementation.
./includes/common.inc:3810: * hook_init() implementations, or from other code that ensures that the
./includes/common.inc:4829: // Initialize $_GET['q'] prior to invoking hook_init().
./includes/common.inc:4835: // Prior to invoking hook_init(), initialize the theme (potentially a custom
./includes/common.inc:4837: // - Modules with hook_init() implementations that call theme() or
./modules/simpletest/tests/system_test.module:184: * Implements hook_init().
…
Here, we are recursively searching (-r) our Drupal files for instances of the string hook_init and printing out the actual lines and
line numbers (-n) where they occur. We could further refine our search by piping results into another search. In the following
example, we search for occurrences of the word poll in the previous example’s search result set:
$grep -rn 'hook_init' . | grep 'dblog'./modules/dblog/dblog.module:88: * Implements hook_init().
Another way to refine your search is by using the -v flag for grep, which means “invert this match”; that is, let matches through
that do not match the string. Let’s find all the occurrences of the word lock without matching the words block or Block:
$ grep -rn 'lock' . | grep -v '[B|b]lock'
./includes/common.inc:2548: // See if the semaphore is still locked.
./includes/database.mysql.inc:327:function db_lock_table($table) {
./includes/database.mysql.inc:332: * Unlock all locked tables.
...
Summary
After reading this chapter, you should be able to
• Code according to Drupal coding conventions.
• Document your code so that your comments can be reused by the API module.
• Comfortably search through Drupal’s code base using grep.
• Identify Drupal coding ninjas by their best practices.
./themes/engines/phptemplate/phptemplate.engine
In the preceding case, we are recursively searching (-r) our Drupal files for instances of hook_init starting at the current directory
(.) and printing out the file names (-l) of the matching instances. Now look at this example:
$ grep -rn 'hook_init' .
./authorize.php:31: * avoid various unwanted operations, such as hook_init() and
./includes/common.inc:2697: * drupal_add_css() in a hook_init() implementation.
./includes/common.inc:2750: * theme .info files. Modules that add stylesheets within hook_init()
./includes/common.inc:3770: * drupal_add_css() in a hook_init() implementation.
./includes/common.inc:3810: * hook_init() implementations, or from other code that ensures that the
./includes/common.inc:4829: // Initialize $_GET['q'] prior to invoking hook_init().
./includes/common.inc:4835: // Prior to invoking hook_init(), initialize the theme (potentially a custom
./includes/common.inc:4837: // - Modules with hook_init() implementations that call theme() or
./modules/simpletest/tests/system_test.module:184: * Implements hook_init().
…
Here, we are recursively searching (-r) our Drupal files for instances of the string hook_init and printing out the actual lines and
line numbers (-n) where they occur. We could further refine our search by piping results into another search. In the following
example, we search for occurrences of the word poll in the previous example’s search result set:
$grep -rn 'hook_init' . | grep 'dblog'./modules/dblog/dblog.module:88: * Implements hook_init().
Another way to refine your search is by using the -v flag for grep, which means “invert this match”; that is, let matches through
that do not match the string. Let’s find all the occurrences of the word lock without matching the words block or Block:
$ grep -rn 'lock' . | grep -v '[B|b]lock'
./includes/common.inc:2548: // See if the semaphore is still locked.
./includes/database.mysql.inc:327:function db_lock_table($table) {
./includes/database.mysql.inc:332: * Unlock all locked tables.
...
Summary
After reading this chapter, you should be able to
• Code according to Drupal coding conventions.
• Document your code so that your comments can be reused by the API module.
• Comfortably search through Drupal’s code base using grep.
• Identify Drupal coding ninjas by their best practices.
Không có nhận xét nào:
Đăng nhận xét