original source : https://www.packtpub.com/books/content/drupal-7-module-development-drupals-theme-layer
Business logic versus presentation logic
So what would be the best way to get our data and functionality marked up? Do we simply wrap each piece of data in HTML and return the whole as a giant string? Like the following example:
return ’
’ . $data . ’
’;
Fortunately, we don’t. Like all other well-designed applications, Drupal separates its business logic from its presentation logic. Traditionally, the primary motivations for this separation of concerns are as follows:
- To make the code easier to maintain.
- To make it possible to easily swap out one layer’s implementation without having to re-write the other layers.
As we shall see, Drupal takes the “swap-ability” aspect to the extreme.
As we mentioned in the introduction of this article, the default theme selected on the Appearance page is the most obvious part of the theme layer. Also, you might think that the theme is responsible for applying the HTML and CSS for the website. However, there are thousands of contributed modules ondrupal.org. Should the theme be responsible for marking up all of those modules’ data? Obviously not.
Since a module is most intimately familiar with its own data and functionality, it’s the module’s responsibility to provide the default theme implementation. As long as the module uses the theme system properly, a theme will be able to override any HTML and CSS by hot-swapping its own implementation for the module’s implementation.

After the data has been retrieved and manipulated in the heart of your module (the business logic), it will need to provide the default theme implementation. Sometimes a particular theme will need to override your implementation in order for it to achieve a specifc design goal; if the theme provides its own implementation, Drupal will use the theme implementation instead of the module’s default implementation.
$variables = array(‘items’ => $list, ‘type’ => ‘ol’);
$content = theme(‘item_list’, $variables);
By calling the theme() function, we are delegating the responsibility of determining and using the proper theme implementation. We’re saying:
“Hey, theme()! I want to markup my data as an item_list. Can you do that for me? I don’t need to know the details. kthxbye.”
Our module just needs to decide which theme hook it wants to use to markup its data. Should the data be displayed in an unordered list, a table, or a wordle?
Hook crazy?
In addition to API hooks, Drupal also has theme hooks. A theme hook is simply the name of a particular way to markup some data. For example, passing data to the item_list theme hook will result in different markup then passing data to the links theme hook. However, while normally every module’s hook function will be called when Drupal invokes an API hook, only one theme hook implementation will be invoked when Drupal invokes a theme hook.
There are actually two different ways you can make an implementation (which we will discuss later), but for now we’ll only talk about the simplest method for module developers—theme functions. When you call theme(), it will look for a default theme function named theme_HOOKNAME and for an optional theme override function called THEMENAME_HOOKNAME. If you dig into Drupal’s internals, you’ll fnd atheme_item_list() inside includes.inc or theme.inc. This is Drupal’s default theme implementation for anitem_list. If our active theme was Bartik, and if Bartik implemented a theme override calledbartik_item_list(), then theme() would use the Bartik theme’s implementation instead of the default one.
The preceding fgure shows one piece of data as it passes through a module and a theme. However, in order for you to understand the full power of Drupal’s theme layer, you also need to understand how the entire page is built.
However, since all of the active theme’s modifcations occur after any module modifcations, from a module developer’s perspective, all of this theme inheritance is transparent. Since modules don’t need to know anything about the structure o the theme and its ancestry, we’ll simply talk about “the theme” in this book. Just be aware that the actual theme may be more complex.
Base themes and sub-themes
If you’ve previously read anything about Drupal theming, you’ve probably heard about base themes and sub-themes. Any theme can declare a parent theme in its .info file using the base theme key and it will inherit all the hook implementations from its parent theme (and its parent’s parent theme, and so on).
Data granularity
One of the things that makes Drupal theming so powerful is its granularity. Each piece of content is handled separately as it’s passed through the theming system. Each bit of data is themed individually, then combined into ever-larger chunks. At each step in the aggregation process, it’s themed again. The following illustration will make this clearer:

As you can see in the preceding illustration, for a typical blog post, each comment is pulled from the database and sent through the theme system to get HTML markup added to it. Then all the comments are aggregated together into a “comment wrapper” where additional markup and, usually, a “new comment” form is added. Then the single group of comments is passed to the node theming where it is combined with other pieces of the blog post’s content. This process of theming bits of content, aggregation, and theming again is repeated until we’ve built the entire HTML page ready to be sent to a web browser.
There are two advantages to this granular system. First, since each module is responsible for theming its own data, it can either create a very specialized theme hook for its data or it can re-use an existing theme hook. Re-using a theme hook ensures a consistent set of markup for similar data structures while still allowing customized CSS classes (Most theme hooks allow custom classes to be passed as parameters.) For example, the list of links after a node (read more, add new comment, and so on) re-uses the links theme hook, and the links after each comment use the same links theme hook.
The second advantage is for the theme developer. Having a fine-grained theming system means that a theme, if it chooses to, can literally rewrite all of the markup for its own design purposes. As module developers we need to be keenly aware of the themer’s desire to have granular theming overrides.
Theme engines
Some themes require alternate theme engines. Theme engines can provide alternate template syntax, naming standards, and helper functions. Several theme engines are available for download athttp://drupal.org/project/theme+engines. However, we won’t be discussing any theme engines except for Drupal’s default theme engine, PHPTemplate. The PHPTemplate theme engine has been the default theme since Drupal 4.7, has been continuously improved with each version, and has proven its worth again and again. Over 99% of themes available for download on drupal.org use the default PHPTemplate theme engine.
Two ways to theme
So now that we have a good understanding of higher level concepts, let’s get down to the nitty-gritty of theme implementations. There are actually two different ways to implement a theme hook:
- Theme functions: pass data to a PHP function to wrap it in markup
- Templates: pass data to a template which is a PHP file mixed with markup and PHP print statements
Let’s look at each of these in turn.
Theme functions
For a module developer, the easiest type of implementation to understand is a theme function. Theme functions just need to follow a few simple rules in order for them to work properly.
First, the name of the theme function follows the pattern:
theme_[theme hook name]
Since the theme hook name is used directly in the theme function’s name, theme hook names have the same constraints on naming as regular PHP function names; the only valid characters in theme hook names are alphanumeric characters and underscores. So if a module has created anexample_format theme hook, it would implement it with theme function namedtheme_example_format().
Second, the theme function will only have a single parameter, as follows:
function theme_THEME_HOOK_NAME($variables) {…}
The theme function variables are an associative array containing the pieces of data we wish to markup and any options we want to pass to the function. It may seem extremely odd not to use multiple parameters and PHP’s ability to specify default values for each parameter. In fact, previous versions of Drupal did use multiple parameters. We’ll see why Drupal now only uses one parameter in just a moment when we talk about preprocess functions.
For an example of a $variables array, let’s look at how the DocBlock of the theme_item_list() function defnes it:
- Items: An array of items to be displayed in the list. If an item is a string, then it is used as is. If an item is an array, then the data element of the array is used as the contents of the list item. If an item is an array with a “children” element, those children are displayed in a nested list. All other elements are treated as attributes of the list item element.
- Title: The title of the list.
- Type: The type of list to return (e.g. ul,ol).
- Attributes: The attributes applied to the list element.
The items and title keys hold the actual data, and the type and attributes keys are options that specify how to build the item list.
Third, the theme function should return a string that contains the rendered representation of the data. This is usually a string of HTML, but some theme hooks return other types of themed markup. For example, theme_syslog_format returns a simple string with pipe-separated data values for use in a *NIX syslog error log.
That’s it! As you can see, theme functions have very simple requirements and in every other way are standard PHP functions.
The major difference between most functions and theme functions is that you should never call theme functions directly. It may be tempting to take your data and call theme_item_list($vars) directly, but you should instead call theme(“item_list”, $vars). This method of calling theme functions indirectly ensures that themes are able to override any module’s default theme function (or template). It also allows thetheme() function to work additional magic, including allowing other modules to alter the theme function’s variables before they are used.
Preprocess functions
Now we’re starting to see the real flexibility of the theme system. Preprocess functions allow one module to alter the variables used by another module when it calls a theme hook. So if some code passes data to theme() for a particular theme hook, preprocess functions will be called to alter the data before the actual theme hook implementation is called. The following steps are carried out:
- Code calls theme(‘hook_name’, $variables).
- theme() calls preprocess functions for hook_name.
- Preprocess functions modify variables.
- theme() calls actual implementation for hook_name with modifed variables.
All preprocess functions take the form of:
[module]_preprocess_[theme hook name](&$variables)
So if the foo module wants to alter the variables for the item_list theme hook, it could define the function as follows:
function foo_preprocess_item_list(&$variables) {
// Add a class to the list wrapper.
$variables[‘attributes’][‘class’][] = ‘foo-list’;
}
Notice that the $variables parameter is defned with an ampersand in front of it. That’s PHP notation to pass the parameter by reference. Instead of getting a copy of the variables, the foo_preprocess_item_list()function will get access to the actual $variables which is later passed to the theme function implementation. So any modifications that the preprocess function makes to the $variables parameter will be preserved when those variables are passed to the theme function. That’s the reason our examplefoo_preprocess_item_list() function doesn’t return anything; its work is done directly on the original$variables.
This is extremely handy for module developers as it allows all sorts of integration with other modules. Since the variables parameter is a mix of data and options, modules can alter both the raw data and change the way data will be rendered. This can be as simple as one module needing a special class for use in its JavaScript code and adding that class to another module’s themed content by appending to the$variables[‘attributes’][‘class’] array, or can be more complex interactions like the i18n module translating the language used in blocks.
Imagine we’ve built a retro module that integrates GeoCities and we want to replace all links to a user’s profle page with a link to the user’s GeoCities homepage. We can do that relatively easily with a preprocess function.
First let’s look at the following theme_username function’s documentation:
/**
* Format a username.
*
* @param $variables
* An associative array containing:
* – account: The user object to format.
* – name: The user’s name, sanitized.
* – extra: Additional text to append to the user’s name, sanitized.
* – link_path: The path or URL of the user’s profile page, home
* page, or other desired page to link to for more information
* about the user.
* – link_options: An array of options to pass to the l() function’s
* $options parameter if linking the user’s name to the user’s
* page.
* – attributes_array: An array of attributes to pass to the
* drupal_attributes() function if not linking to the user’s page.
*/
Quite conveniently, theme_username() has a handy $link_path variable that we want to alter to achieve our old-school giggles. Assuming that we’ve used some other business logic with the user module’s hooks to load our GeoCities URL into the user’s account (the “hard” part), replacing the link to the user’s profle page can be accomplished with the following simple preprocess function:
/**
* Implements awesomeness with hook_preprocess_username().
*/
function retro_preprocess_username(&$variables) {
$variables[‘link_path’] = $variables[‘account’]->geocities_url;
}
That’s it! We don’t have to override the user module’s theme implementation; we just modify its parameters.
Theme overrides
While module developers usually don’t have to worry about whether a theme overrides a particular theme function or not, it’s still important to understand how this mechanism works.
Drupal theme is normally composed of CSS, images, JavaScripts, template files (discussed shortly), a .infofile, and a template.php file. The template.php file is analogous to a module’s .module file. It contains all of the PHP functions for the theme and is automatically loaded when the theme is initialized.
If a theme wants to override a particular theme function, it needs to copy the theme function from its original location and paste it into its template.php file. Then it needs to change the function’s prefix fromtheme to its own name and finally, it needs to start making the desired changes to the function.
For example, if the Bartik theme wants to override the theme_menu_local_tasks() function in order to add some markup around the page’s tabs, it would copy the entire function from includes/menu.inc, paste it into Bartik’s template.php, and rename it to bartik_menu_local_tasks().
Fortunately, when a theme overrides a default theme function, a module’s preprocess functions continue to work as normal.
Themes also have the ability to create preprocess functions. If the Bartik theme decides to format a user’s name in “last name, frst name” format, it can implement a bartik_preprocess_username() function. Fortunately, a theme’s preprocess functions do not override a module’s preprocess functions. All preprocess functions are run; frst any module’s preprocess functions and then the theme’s preprocess function.
Template files
While theme functions might be the easiest for module developers to understand, template files are the easiest for themes to grasp. When a theme hook is implemented with template files, they are used instead of theme functions. However, from a module developer’s standpoint, there is actually a remarkable amount of similarity between template files and theme functions. First, let’s take a closer look at template files.
Templates are files primarily containing HTML but with some PHP statements mixed in using the template’s variables. Instead of declaring a theme_hook_name() function, a module would instead create a hook-name.tpl.php file. The following are the contents of a typical template file, typical-hook.tpl.php:
”>
>
’@time’ => $time,
’!author’ => $author,
)); ?>
>
// We hide the links now so that we can render them later.
hide($content[‘links’]);
print render($content);
?>
<?php print render($content[‘links’]); ?>
</div>
The preceding example shows the full gamut of the things that you are likely see in a template file. They are as follows:
- Printing a variable containing a string?
- Printing a translatable string using t()
- Conditional if/else/endif statement
- Delaying rendering on part of a render element with hide()
- Printing a render element
All of the PHP in a template should be limited to printing out variables. This limited amount of PHP makes it much easier for non-programmers to learn how to use template fles compared to theme functions. However, for module developers, the template implementation is still very similar to the theme function implementation; the handful of differences are relatively minor.
As with theme function implementations, our module would still need to invoke the theme hook usingtheme().
$variables = array(‘typical’ => $typical_object);
$output = theme(‘typical_hook’, $variables);
The theme() function would discover that the typical_hook theme hook was implemented as a template and render the corresponding typical-hook.tpl.php file.
The only valid characters in theme hook names are alphanumeric characters and underscores. This is true of all theme hooks, regardless of whether they are implemented as a theme function or as a template file. However, when theme() looks for template implementations, it will automatically convert any underscores in the theme hook name into hyphens while searching for the template file. For example, calling theme(‘user_picture’, $variables) will result in the template file nameduser-picture.tpl.php being rendered.
Also, just like theme functions, other modules can modify the variables using preprocess functions.
In template fles the focus is on printing out variables in various places in the markup. So for template fles, the preprocess function takes on a more important role. The only difference between a theme function’s preprocess functions and a template file’s are the number and type of preprocess functions.
Read more about this book
(For more resources on this subject, see here.)
The preprocess zoo
When you write a theme function, its natural to pass the raw data in as parameters and generate any display-related meta-data inside the function. With a template file, that’s not really possible without putting complex PHP inside the template. However, as was stated earlier, all of the PHP in a template file should be limited to just the bare minimum required to print out a PHP variable. Any processing that we need to do on the raw data parameters to ease it into print-ready variables should be done in preprocess functions.
“template_” preprocess functions
When a module defnes a theme hook by creating a template file, that module should also create a corresponding preprocess function to set up and process any variables that are needed by the template file, but are not passed as parameters to heme(). By convention, that preprocess function should be of the following form:
template_preprocess_[theme hook name](&$variables)
The template_prefix tells Drupal’s theme system that this preprocess function is the primary preprocessor for the theme hook’s variables and should be run before any other module’s preprocess function.
Here’s an example that should make this concept a bit clearer. This is an actual code snippet from Drupal’s block preprocess function. In each page region, all of the blocks in the region get a variable whose value alternates between “odd” and “even”. These values can be used to create zebra-striped styling, that is, alternate styling on every other block in a region.
function template_preprocess_block(&$variables) {
// We store all block counters using drupal_static().
$block_counter = &drupal_static(__FUNCTION__, array());
// All blocks get an independent counter for each region.
if (!isset($block_counter[$variables[‘block’]->region])) {
$block_counter[$variables[‘block’]->region] = 1;
}
// Generate the zebra striping variable.
$variables[‘block_zebra’] = ($block_counter[$variables[‘block’]-
>region] % 2) ? ‘odd’ : ‘even’;
// Increment the region’s block count.
$block_counter[$variables[‘block’]->region]++;
}
The PHP logic in this function is directly related to the display of the block and not to the general business logic of this data. So, it doesn’t make sense that the block module would calculate that meta data before calling theme(); the meta data clearly belongs to the display logic, which is why it’s placed in the block module’s preprocess function.
Multi-hook preprocess functions
In some rare circumstances, you may need to alter or provide some variables for all theme hooks. In fact, Drupal’s theme system does provide some variables to all templates; the preprocess function that provides these variables is both a “template_” preprocess function and a multi-hook preprocess function. Multi-hook preprocess functions are simply functions that don’t have a _HOOK suffx added to their name and are run for every single template file. Their name is of the following form:
[module]_preprocess(&$variables, $hook)
Obviously, there can be a big performance hit if a module needlessly implements a multi-hook preprocess function. If you’re contemplating writing one, if at all possible, consider writing several preprocess functions that target the specifc hooks you need instead, rather then hit all hooks.
Now, if you were paying close attention to the form of the name, you’ll also notice that these functions actually receive two parameters, namely, the $variables array and a $hook parameter. $hook, as the name suggests, contains the name of the actual theme hook currently being run. So, while afoo_preprocess(&$variables, $hook) function is run for every template file, it will still be able to tell which template is currently being requested. In fact, $hook is the second parameter for all preprocess functions, but $hook is only useful for multi-hook preprocess functions.
For a good example of a multi-hook preprocess function, let’s look at the function that the theme system uses to set up several variables common to all template files—the template_preprocess() function, which is as follows:
function template_preprocess(&$variables, $hook) {
// Tell all templates where they are located.
$variables[‘directory’] = path_to_theme();
// Initialize html class attribute for the current hook.
$variables[‘classes_array’] = array(drupal_html_class($hook));
}
As you can see, this preprocess function creates a $directory variable which can be used to tell where the template file is located on the web server. In the $classes_array variable, it also starts to set up the CSS classes used in the outer-most wrapping div of the template.
Process functions
Obviously, inside our template file, when we print out our dynamically created list of classes, we’ll need the variable to be a string. <?php print $classes_array; ?> will, most unhelpfully print out “array”. In earlier versions of Drupal, classes were dynamically created but were immediately concatenated into strings. So themes would see one long string with multiple classes in it, menu-block-wrapper menu-block-1 menu-name-management, for example. This made removing or altering classes difficult as themers had to master PHP’s string-manipulation functions or even (gasp!) regular expressions.
In Drupal 7, this problem for themers has been solved using the new process functions. Process functions are an additional phase of variable processing functions that run after the initial preprocess functions. In all respects, process functions are exactly like preprocess functions; there are template_prefixed process functions, multi-hook process functions, module-provided process functions, and theme-provided process functions. The only difference is that process functions are run after all preprocess functions have been run.
Process functions are extremely useful when you have meta data that is likely to be manipulated by other modules or themes and you wish to delay the rendering of the meta data until just before the template file itself is rendered.
In the preceding code example, the template_preprocess() function creates a $classes_array variable that holds an array of classes to be used on the wrapping div in the template file. Modules and themes can easily add classes by simply adding an additional array element from inside their preprocess function, as follows:
$variables[‘classes_array’][] = ‘extra-savoir-faire’;
Themes can use much simpler array manipulation functions in order to remove or alter classes.
// Search for the bogus class and return its array key
// location. If not found, array_search returns FALSE.
// Remember that 0 is a valid key.
$key = array_search(‘bogus’, $variables[‘classes_array’]);
if ($key !== FALSE) {
// Alter the class.
$variables[‘classes_array’][$key] .= ’-dude’;
}
// Or remove the no-soup class.
$variables[‘classes_array’] = array_diff($variables[‘classes_array’],
array(‘no-soup’));
In addition to the $classes_array variable, the template_preprocess() function also creates $attributes_array,$title_attributes_array, and $content_attributes_array variables which are used for HTML attributes on the outermost wrapping div, the title’s heading tag, and the content’s wrapping div, respectively. You’ll see each of these variables used in the typical-hook.tpl.php example.
After modules and themes are given an opportunity to alter these variables, the theme system uses thetemplate_process() function to render those arrays into a simple string, as follows:
function template_process(&$variables, $hook) {
// Flatten out classes.
$variables[‘classes’] = implode(’ ’, $variables[‘classes_array’]);
// Flatten out attributes, title_attributes, and content_attributes.
$variables[‘attributes’] = drupal_attributes(
$variables[‘attributes_array’]);
$variables[‘title_attributes’] = drupal_attributes(
$variables[‘title_attributes_array’]);
$variables[‘content_attributes’] = drupal_attributes(
$variables[‘content_attributes_array’]);
}
A similar problem troubled module developers in Drupal 6. It was impossible to call drupal_add_css() ordrupal_add_js() in a MODULE_preprocess_page() function because the lists of CSS files and JavaScript files were already generated before any of the preprocess functions were run. Again, process functions come to the rescue. Drupal 7 delays the generation of these lists until the template_process_html() function is run.
Read more about this book
(For more resources on this subject, see here.)
Order of preprocess execution
Now with all these different favors of processing functions, it can get a bit confusing as to which function runs in what order. Fortunately, there are just three simple rules that are used to determine the order of processing. They are as follows:
- All preprocess functions run before all process functions
- template_ prefixed functions run frst. [module]_ prefixed functions run next. [theme]_ prefixedfunctions run last
- Multi-hook functions run before hook-specifc functions
This results in the following order of execution for a particular theme hook:
- template_preprocess()
- template_preprocesss_HOOK()
- MODULE_preprocess()
- THEME_preprocess()
- THEME_preprocess_HOOK()
- template_process()
- template_processs_HOOK()
- MODULE_process()
- MODULE_process_HOOK()
- THEME_process()
- THEME_process_HOOK()
Whew
If the THEME is actually a list of inherited base and sub-themes, each THEME_-prefixed item above could be a list of each base theme’s and sub-theme’s functions, which would make the list even longer. See the “Base themes and sub-themes” tip near the beginning of this chapter if you haven’t read it already.
Summary
In this article we have covered:
- Business logic versus presentation logic
- Data granularity
- Theme engines
- Two ways to theme
summarized by me
module은 theme에서 수행되는 markup 작업에 세세하게 관여하지 않아도 된다. 다만 default theme implementation만을 수행하면된다.
$variables = array(‘items’ => $list, ‘type’ => ‘ol’);
$content = theme(‘item_list’, $variables);
위의 내용은 다시 “Hey, theme()! I want to markup my data as an item_list. Can you do that for me? I don’t need to know the details. kthxbye.” 와 같이 이해 될수 있다.
hook에 대한 내용
drupal에는 api hooks 와 theme hooks가 있다. theme hook는 data를 markup하는 방법을 말한다. ( 원문 A theme hook is simply the name of a particular way to markup some data. )
예를 들어 item_list theme hook 와 link theme hook에 같은 data를 제공하는 경우 앞은 경우는 list의 형태로 markup 된다. 후자는 link의 형태로 markup 된다. module hook의 경우 drupal core에서 hook가 호출되면 module각각에 들어있는 해당하는 hook가 모두 실행된다. theme hook의 경우는 대응하는 hook가 단 하나 존재한다.
data를 implementation 하는 방법에는 두가지가 있다.
-
theme function을 이용하는 방법이다.
theme()을 호출하면 우선 default theme function을 찾는다. 이름은 theme_HOOK이름 형식이며 include.inc 또는 theme.in 화일에 존재한다. 우선 기본 함수를 찾고 그다음은 각각의 theme에서 제공하는 override 함수가 있는지 확인하고 있으면 그것을 적용한다. 이름의 형식은 THEME이름_HOOK이름 의 형태가 된다. 예를 들어 list형태로 data를 출력하는 경우 theme_item_list()를 기본적으로 이용하나 bartik theme에서 이를 override하는 함수가 있는경우는 bartik_item_list()가 대신 이용된다.
base themes and sub-themes
어느 theme이든 base theme key를 이용 .info 화일에서 parent theme을 선언할수 있다. 이런경우 모든 theme hook implementations 이 sub-theme에도 상속된다.
theme engine
drupal에는 theme engine이 존재하며 이는 template syntax, naming standards, helper functions을 관장한다. 이또한 수정가능하다. 그러나 보통은 수정이 필요하지 않으며 몇몇 engine이 이미 나와 있다. 그러나 PHPTemplate이 가장 많이 사용되며 기본 engine으로 사용된다.
theme implementation
2가지의 방법이 존재한다.
-
theme functions을 이용한다. data를 theme() 함수에 전달 markup data 로 전환한다.
-
templates를 이용한다. php구문, print구문과 markup 구문이 섞여있는 template에 data를 전달하는 방법이다.
theme 함수 (기본 작업을 수행하는 includes.inc나 theme.inc에 존재한다.)
우선 theme function의 이름은 theme_[theme hook 이름] 의 형식이다. theme hook 이름 은 실제 함수이름으로 사용되므로 php 함수이름 정의 규칙을 따른다. 알파벳, 숫자 , 언더스코어를 사용할수 았다. 예를 들어 example_format theme hook의 경우는 theme_example_format()이 된다.
실제 function정의의 형태는 function theme_THEME_HOOK_이름 ($variables){ …….} 가 된다. parameter는 단 하나이며 여기에 markup 될 data 와 방법에 관련된 options이 모두 array의 형태로 전달된다.
예시)
For an example of a $variables array, let’s look at how the DocBlock of the theme_item_list() function defnes it:
-
Items: An array of items to be displayed in the list. If an item is a string, then it is used as is. If an item is an array, then the data element of the array is used as the contents of the list item. If an item is an array with a “children” element, those children are displayed in a nested list. All other elements are treated as attributes of the list item element.
-
Title: The title of the list.
-
Type: The type of list to return (e.g. ul,ol).
-
Attributes: The attributes applied to the list element.
위와 같이 정의된 theme function 은 markup된 형태의 string을 return한다. 대부분은 html string 이 될것이다. 특수한 경우는 markup된 형태가 아닌 다른 형식의 string이 return 되기도한다. 예를 들어 error log 를 얻어내야 하는 경우.
theme function은 일반 function과 달리 직접 호출해서 사용하지 않고 theme()이용하여 간접 사용한다.
preprocess functions
preprocess function 은 theme hook가 호출될때 하나의 module이 다른 module에 의해 사용되는 variables을 변경할수 있게 한다. 특정theme hook를 위해 theme()에 data를 전달하는 경우 preprocess function가 호출되고 data가 preprocess function을 거쳐 변경된 data가 원래 theme hook로 전달된다.
과정
-
theme(‘hook이름’, $variables) 가 호출된다.
-
theme()는 특정 hook를 위해 preprocess function을 호출한다.
-
preprocess function이 data를 변경한다
-
theme()은 변경된 data를 이용 원래 hook를 호출수행한다.
preprocess function의 이름 형식 [module이름]_preprocess_[themehook이름] 이어야 한다.
함수 정의 형식예
function foo_preprocess_item_list(&$variables)
{
// add a class to the list wrapper
$variables[‘attributes’][‘class’][]=’foo-list’;
}
유의사항: variable은 by reference형태로 전달된다.그러므로 함수내에서 return 구문이 필요없으며 직접 값은 원래 변수에서 변경된다.
theme overrides
drupal theme 은 css, images, javascripts, template files, .info file 와 template.php 화일로 구성된다.
template.php 화일은 module의 .module 화일과 유사한 기능을 한다. 모든 필요한 php 함수들을 포함하고 있으며 theme이 initialized 되면 자동 load된다. 기본적으로 정의된 theme hook fucntion을 override 하는 경우는 기존형식인
function theme_hook이름($variables){…..}
에서 function 새이름_hook이름($variables){….}를 template.php 에 정의한다.
예를 들면 bartik theme을 사용하며 includes/menu.inc에 있는 theme_menu_local_tasks()를 override하는 경우 template.php 안에 bartik_menu_local_tasks()를 만든다. bartik이 아닌 다른 문구도 상관없으나 theme 이름을 하면 이해하기 쉬울듯하다. theme function이 override 되어도 preprocess function은 원래 의도된 그대로 실행된다. theme preprocess function도 만들수 있으며 theme이름_preprocess_hook이름()의 형식으로 만들면된다. theme preprocess가 module preprocess 를 override하지는 않는다. module preprocess 먼저 그리고 theme preprocess 이 다음으로 순서대로 실행된다.
template files
theme functions 대신에 template files을 이용해 theme hook가 implementation 할수있다.template file은 기본적으로 html 문서내용에 template’s variables 를 사용하는 php statements를 포함하고 있다. theme_hook이름() 을 이용하기 보다 hook이름.tpl.php 화일을 이용한다.
예를 들면
div class=“<?php print $classes; ?>”<?php print $attributes; ?>>
<?php if ($title): ?>
<h2<?php print $title_attributes; ?>>
<?php print $title; ?>
</h2>
<?php endif;?>
’@time’ => $time,
’!author’ => $author,
)); ?>
>
// We hide the links now so that we can render them later.
hide($content[‘links’]);
print render($content);
?>
<?php print render($content[‘links’]); ?>
</div>
이런 형식이 된다.
theme function implementation 과 마찬가지로 theme()을 이용하여 theme hook를 호출해야 한다.
예를 들어 $variables = array(‘typical’ => $typical_object);
$output = theme(‘typical_hook’, $variables);
theme()를 이용하여 template이 호출될때 theme hook 이름 자리에 들어 있는 언더스코어는 대쉬 (-) 로 전환되어 호출된다. 예를 들어 theme(‘user_picture’, $variables)의 경우는 user-picture.tpl.php 가 검색 호출된다. theme function 과 마찬 가지로 template 의 경우도 preprocess function을 이용하여 variables을 변경할수 있다. template 화일에는 되도록이면 더이상 변경이 필요없는 출력 준비된 variable만을 이용하는 것을 권장한다. data 처리는 되도록이면 preprocess에서 처리하는 것을 권장한다.
“template_” preprocess functions
module 이 template file을 이용하여 theme hook를 정의하는 경우, 해당 module은 이에 대응하는 preprocess function을 만들어야 한다. 형식은 template_preprocess_[theme_hook이름](&$variables) 이 된다.
예시를 들면 function template_preprocess_block(&$variables) {
// We store all block counters using drupal_static().
$block_counter = &drupal_static(__FUNCTION__, array());
// All blocks get an independent counter for each region.
if (!isset($block_counter[$variables[‘block’]->region])) {
$block_counter[$variables[‘block’]->region] = 1;
}
// Generate the zebra striping variable.
$variables[‘block_zebra’] = ($block_counter[$variables[‘block’]-
>region] % 2) ? ‘odd’ : ‘even’;
// Increment the region’s block count.
$block_counter[$variables[‘block’]->region]++;
}
preprocess function안에는 data의 표시 형식 변경에 관련된 내용만 들어가게 한다. 직접적인 business logic은 이전에 이미 처리되어서 전달 되어져야 한다.
multi-hook preprocess functions
모든 theme hooks를 위해 variables를 변경해야 하는 경우 multi-hook preprocess가 사용된다. _HOOK 접미사가 함수이름에 붙지 않으며 모든 template file을 위해 사용된다.
[module이름]_preprocess(&$variables,$hook) 이 함수이름 형식이다.이 함수는 모든 hook실행시 전에 variables의 변경을 수행함으로 성능에 좋지 않은 영향을 줄수 있다. 함수 정의 구문에서 사용되는 2번째 파라미터는 현재 사용중인 theme hook 이름을 가지고 있다. 보통 multi-hook preprocess는 모든 template에서 사용되는 기본 variables의 설정에 사용된다.
process functions
preprocess functions을 통해서 css, class 이름, attributes의 내용등을 수정할수 있다. 그리고 그 최종 생산물은 array형태가 되며 이를 각각의 string 형태로 만들어 가는 과정을 process function이 담당한다. drupal 6에서는 MODULE_preprocess_page() 함수에서 drupal_add_js(), drupal_add_css()를 이용하는 것이 불가능했지만 drupal 7에서는 template_process_html()이 호출되기 전까지 string을 만들어 내는 것을 지연시키므로 가능하다.
order of preprocess 실행
모든 preprocess functions은 process function 전에 실행된다.
template_ 로 시작되는 함수가 [module이름]_ 으로 시작되는 함수보다 먼저 실행된다. 맨 마지막이 [theme이름]_ 으로 시작되는 함수가 실행된다.
multi-hook 함수가 특정 hook 에 한정된 함수보다 먼저 실행된다.
-
template_preprocess()
-
template_preprocesss_HOOK()
-
MODULE_preprocess()
-
THEME_preprocess()
-
THEME_preprocess_HOOK()
-
template_process()
-
template_processs_HOOK()
-
MODULE_process()
-
MODULE_process_HOOK()
-
THEME_process()
-
THEME_process_HOOK()
base theme 이 있고 sub theme 이 있어서 상속되는 형태라면 base theme 의 내용이 실행되고 sub theme의 내용이 실행되는 보다 복잡한 순서 전개가 이루어 진다.