The Smarty Tag Cloud plugin turns weighted labels into links whose integer sizes reflect their relative frequency. Version 1.0 was written in 2008 for the plugin conventions and PHP runtime of that period. Its useful core is a logarithmic mapping: large differences remain visible without allowing a few dominant tags to make every other tag unreadably small.
The historical source remains available for reference. It is not a drop-in Smarty 5 component. The original callback signature, automatic plugin discovery, output handling, and two numerical edge cases need attention before using the idea in a current application.
Preparing the Tag Data
Each tag is an associative array with three fields:
tagis the visible label.numis its non-negative weight, such as an article count.linkis the token inserted into the URL pattern.
A current Smarty application can assign this array by value with $smarty->assign(:
<?php
$tags = [
['tag' => 'Robert', 'num' => 55, 'link' => 'robert'],
['tag' => 'raw', 'num' => 66, 'link' => 'raw'],
['tag' => 'Open Source', 'num' => 41, 'link' => 'open-source'],
['tag' => 'PHP', 'num' => 90, 'link' => 'php'],
['tag' => 'Smarty', 'num' => 14, 'link' => 'smarty'],
];
$smarty->assign('tags', $tags); The 2008 example used assign_by_ref(). That call belongs to the old API and is unnecessary here. With the original plugin installed in the legacy plugin directory, the template invocation looked like this:
{cloud tags=$tags minsize=10 maxsize=30 sort="random" limit=50 url="/tag/%link%/" class="tags tag-%size%" font="%size%px"} The plugin substitutes %link% with the link token, %tag% with the label, and %size% with the calculated integer. The default output is an anchor with rel="tag". A CSS class is usually preferable to an inline font size, especially when the site needs responsive typography.
Logarithmic Weight Scaling
Let a tag have weight \(w\), let \(w_{\min}\) and \(w_{\max}\) be the smallest and largest weights, and let \(s_{\min}\) and \(s_{\max}\) be the requested size bounds. Version 1.0 computes the integer size as
$$ s(w) = s_{\min} + \operatorname{round}\left( \frac{\log(w + 2) - \log(w_{\min} + 2)} {\log(w_{\max} + 2) - \log(w_{\min} + 2)} \left(s_{\max} - s_{\min}\right) \right). $$The shift by 2 keeps zero weights inside the logarithm's domain. The minimum weight maps to minsize, the maximum maps to maxsize, and intermediate values are compressed logarithmically. For the sample weights 14, 41, and 90 with a range from 10 to 30, the resulting sizes are 10, 21, and 30.
There is one missing branch in Version 1.0: when all weights are equal, the denominator is zero and the calculation attempts division by zero. A current adaptation must detect that case before applying the formula. Mapping every tag to the rounded midpoint of the size interval is neutral and deterministic:
if ($minimumWeight === $maximumWeight) {
$size = (int) round(($minimumSize + $maximumSize) / 2);
} An empty array needs a separate early return as well. The original implementation reads the first element before checking whether one exists.
Parameters in Version 1.0
| Parameter | Required | Behavior |
|---|---|---|
tags | yes | Array of records containing tag, num, and link. |
minsize | no | Lower integer size bound; the source default is 10. |
maxsize | no | Upper integer size bound; the source default is 30. |
url | no | Anchor URL pattern. The default is #. |
sort | no | Selects random, alphabetic, or weight order. |
limit | no | Restricts the rendered prefix after sorting. |
class | no | Class pattern that may contain %size%. |
font | no | Inline font-size pattern that may contain %size%. |
format | no | Replaces the complete default anchor format. |
The supported sort values are:
randomassigns a random sort key to each item.alphabeticorders labels ascending.alphabetic-descorders labels descending.weightorders numeric weights ascending.weight-descorders numeric weights descending.
The implementation first scans the complete input to establish the weight range. It applies limit after sorting. Consequently, the visible subset is still scaled against all supplied tags. With no sort parameter, input order is retained before the limit is applied.
A custom format can use the same three placeholders:
<a href="/tag/%link%/" class="tags tag-%size%" rel="tag">%tag%</a> Security Boundary
Version 1.0 does not escape the tag label, link token, class pattern, font pattern, or custom format. Direct replacement inside HTML therefore permits attribute injection and cross-site scripting when any of those values can be influenced by an untrusted source.
A current implementation should build the fixed anchor structure in PHP, validate the URL according to the application's routing policy, and pass visible text and attribute values through htmlspecialchars() with ENT_QUOTES | ENT_SUBSTITUTE and an explicit UTF-8 encoding. HTML escaping does not validate a URL scheme, so accepting arbitrary external URLs still requires a separate allowlist or URL parser.
The unrestricted format option should be treated as trusted template configuration, never as user data. A safer redesign would omit it and expose structured options for URL, class, and size instead of interpolating an arbitrary HTML string.
Adapting the Callback for Smarty 5
Current Smarty runtime tags receive the parameter array and a Smarty\Template instance. They return the generated string and can be registered explicitly with registerPlugin(). The adapter boundary looks like this:
<?php
use Smarty\Smarty;
use Smarty\Template;
function smarty_tag_cloud(array $params, Template $template): string
{
return renderSafeTagCloud($params);
}
$smarty->registerPlugin(
Smarty\Smarty::PLUGIN_FUNCTION,
'cloud',
'smarty_tag_cloud'
); renderSafeTagCloud() is intentionally not represented by the historical function unchanged. It must validate the records and bounds, return an empty string for empty input, handle equal weights, escape each output context, and define how invalid sort modes and URLs fail. Registering the old callback under a new API does not repair those behavioral and security gaps.
Presentation and Accessibility
Visual size can suggest relative activity, but it must not be the only carrier of meaning. Every item should remain a normal descriptive link, the minimum size must stay readable, and keyboard focus must remain visible. If precise counts matter, include them in adjacent text or accessible labels rather than expecting readers to infer values from font size alone.
References
- [Source]Smarty Tag Cloud plugin Version 1.0 source.
- [Smarty Tags]Smarty Documentation: Custom tags.
- [Smarty Assign]Smarty Documentation:
assign(). - [PHP Escaping]PHP Manual:
htmlspecialchars().