SkillAgentSearch skills...

Shortcode

Advanced shortcode (BBCode) parser and engine for PHP

Install / Use

/learn @thunderer/Shortcode
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Shortcode

Build Status Latest Stable Version Total Downloads License Psalm coverage Code Coverage Scrutinizer Code Quality

Shortcode is a framework agnostic PHP library allowing to find, extract and process text fragments called "shortcodes" or "BBCodes". Examples of their usual syntax and usage are shown below:

[user-profile /]
[image width=600]
[link href="http://google.pl" color=red]
[quote="Thunderer"]This is a quote.[/quote]
[text color="red"]This is a text.[/text]

The library is divided into several parts, each of them containing logic responsible for different stages and ways of processing data:

  • parsers extract shortcodes from text and transform them to objects,
  • handlers transform shortcodes into desired replacements,
  • processors use parsers and handlers to extract shortcodes, compute replacements, and apply them in text,
  • events alter the way processors work to provide better control over the whole process,
  • serializers convert shortcodes from and to different formats like Text, XML, JSON, and YAML.

Each part is described in the dedicated section in this document.

Installation

There are no required dependencies and all PHP versions from 5.3 up to latest 8.x are tested and supported. This library is available on Composer/Packagist as thunderer/shortcode, to install it execute:

composer require thunderer/shortcode=^0.7

or manually update your composer.json with:

(...)
"require": {
    "thunderer/shortcode": "^0.7"
}
(...)

and run composer install or composer update afterwards. If you're not using Composer, download sources from GitHub and load them as required. But really, please use Composer.

Usage

Facade

To ease usage of this library there is a class ShortcodeFacade configured for most common needs. It contains shortcut methods for all features described in the sections below:

  • addHandler(): adds shortcode handlers,
  • addHandlerAlias(): adds shortcode handler alias,
  • process(): processes text and replaces shortcodes,
  • parse(): parses text into shortcodes,
  • setParser(): changes processor's parser,
  • addEventHandler(): adds event handler,
  • serialize(): serializes shortcode object to given format,
  • unserialize(): creates shortcode object from serialized input.

Processing

Shortcodes are processed using Processor which requires a parser and handlers. The example below shows how to implement an example that greets the person with name passed as an argument:

use Thunder\Shortcode\HandlerContainer\HandlerContainer;
use Thunder\Shortcode\Parser\RegularParser;
use Thunder\Shortcode\Processor\Processor;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;

$handlers = new HandlerContainer();
$handlers->add('hello', function(ShortcodeInterface $s) {
    return sprintf('Hello, %s!', $s->getParameter('name'));
});
$processor = new Processor(new RegularParser(), $handlers);

$text = '
    <div class="user">[hello name="Thomas"]</div>
    <p>Your shortcodes are very good, keep it up!</p>
    <div class="user">[hello name="Peter"]</div>
';
echo $processor->process($text);

Facade example:

use Thunder\Shortcode\ShortcodeFacade;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;

$facade = new ShortcodeFacade();
$facade->addHandler('hello', function(ShortcodeInterface $s) {
    return sprintf('Hello, %s!', $s->getParameter('name'));
});

$text = '
    <div class="user">[hello name="Thomas"]</div>
    <p>Your shortcodes are very good, keep it up!</p>
    <div class="user">[hello name="Peter"]</div>
';
echo $facade->process($text);

Both result in:

    <div class="user">Hello, Thomas!</div>
    <p>Your shortcodes are very good, keep it up!</p>
    <div class="user">Hello, Peter!</div>

Configuration

Processor has several configuration options available as with*() methods which return the new, changed instance to keep the object immutable.

  • withRecursionDepth($depth) controls the nesting level - how many levels of shortcodes are actually processed. If this limit is reached, all shortcodes deeper than level are ignored. If the $depth value is null (default value), nesting level is not checked, if it's zero then nesting is disabled (only topmost shortcodes are processed). Any integer greater than zero sets the nesting level limit,
  • withMaxIterations($iterations) controls the number of iterations that the source text is processed in. This means that source text is processed internally that number of times until the limit was reached or there are no shortcodes left. If the $iterations parameter value is null, there is no iterations limit, any integer greater than zero sets the limit. Defaults to one iteration,
  • withAutoProcessContent($flag) controls automatic processing of shortcode's content before calling its handler. If the $flag parameter is true then handler receives shortcode with already processed content, if false then handler must process nested shortcodes itself (or leave them for the remaining iterations). This is turned on by default,
  • withEventContainer($events) registers event container which provides handlers for all the events fired at various stages of processing text. Read more about events in the section dedicated to them.

Events

If processor was configured with events container there are several possibilities to alter the way shortcodes are processed:

  • Events::FILTER_SHORTCODES uses FilterShortcodesEvent class. It receives current parent shortcode and array of shortcodes from parser. Its purpose is to allow modifying that array before processing them,
  • Events::REPLACE_SHORTCODES uses ReplaceShortcodesEvent class and receives the parent shortcode, currently processed text, and array of replacements. It can alter the way shortcodes handlers results are applied to the source text. If none of the listeners set the result, the default method is used.

There are several ready to use event handlers in the Thunder\Shortcode\EventHandler namespace:

  • FilterRawEventHandler implements FilterShortcodesEvent and allows to implement any number of "raw" shortcodes whose content is not processed,
  • ReplaceJoinEventHandler implements ReplaceShortcodesEvent and provides the mechanism to apply shortcode replacements by discarding text and returning just replacements.

The example below shows how to manually implement a [raw] shortcode that returns its verbatim content without calling any handler for nested shortcodes:

use Thunder\Shortcode\Event\FilterShortcodesEvent;
use Thunder\Shortcode\EventContainer\EventContainer;
use Thunder\Shortcode\Events;
use Thunder\Shortcode\HandlerContainer\HandlerContainer;
use Thunder\Shortcode\Parser\RegularParser;
use Thunder\Shortcode\Processor\Processor;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;

$handlers = new HandlerContainer();
$handlers->add('raw', function(ShortcodeInterface $s) { return $s->getContent(); });
$handlers->add('n', function(ShortcodeInterface $s) { return $s->getName(); });
$handlers->add('c', function(ShortcodeInterface $s) { return $s->getContent(); });

$events = new EventContainer();
$events->addListener(Events::FILTER_SHORTCODES, function(FilterShortcodesEvent $event) {
    $parent = $event->getParent();
    if($parent && ($parent->getName() === 'raw' || $parent->hasAncestor('raw'))) {
        $event->setShortcodes(array());
    }
});

$processor = new Processor(new RegularParser(), $handlers);
$processor = $processor->withEventContainer($events);

assert(' [n /] [c]cnt[/c] ' === $processor->process('[raw] [n /] [c]cnt[/c] [/raw]'));
assert('n true  [n /] ' === $processor->process('[n /] [c]true[/c] [raw] [n /] [/raw]'));

Facade example:

use Thunder\Shortcode\Event\FilterShortcodesEvent;
use Thunder\Shortcode\Events;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
use Thunder\Shortcode\ShortcodeFacade;

$facade = new ShortcodeFacade();
$facade->addHandler('raw', function(ShortcodeInterface $s) { return $s->getContent(); });
$facade->addHandler('n', function(ShortcodeInterface $s) { return $s->getName(); });
$facade->addHandler('c', function(ShortcodeInterface $s) { return $s->getContent(); });

$facade->addEventHandler(Events::FILTER_SHORTCODES, function(FilterShortcodesEvent $event) {
    $parent = $event->getParent();
    if($parent && ($parent->getName() === 'raw' || $parent->hasAncestor('raw'))) {
        $event->setShortcodes(array());
    }
});

assert(' [n /] [c]cnt[/c] ' === $facade->process('[raw] [n /] [c]cnt[/c] [/raw]'));
assert('n true  [n /] ' === $facade->process('[n /] [c]true[/c] [raw] [n /] [/raw]'));

Parsing

This section discusses available shortcode parsers. Regardless of the parser that you will choose, remember that:

  • shortcode names can be only aplhanumeric characters and dash -, basically must conform to the [a-zA-Z0-9-]+ regular expression,
  • unsupported shortcodes (no registered handler or default handler) will be ignored and left as they are,
  • mi

Related Skills

View on GitHub
GitHub Stars389
CategoryDevelopment
Updated20d ago
Forks32

Languages

PHP

Security Score

100/100

Audited on Mar 6, 2026

No findings