Skip to content

Commit

Permalink
🚿
Browse files Browse the repository at this point in the history
  • Loading branch information
codemasher committed Jan 30, 2019
1 parent d36ef9c commit be5f46e
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 183 deletions.
175 changes: 9 additions & 166 deletions README.md
Expand Up @@ -2,7 +2,7 @@

A recursive regexp [BBCode](http://en.wikipedia.org/wiki/BBCode) parser for PHP 7+ using [preg_replace_callback()](http://php.net/preg_replace_callback),
based on an example by [MrNiceGuy](http://www.developers-guide.net/forums/member/69,mrniceguy) on
[developers-guide.net](http://www.developers-guide.net/c/152-bbcode-parser-mit-noparse-tag-selbst-gemacht.html).
[developers-guide.net](http://www.developers-guide.net/c/152-bbcode-parser-mit-noparse-tag-selbst-gemacht.html).
Handles nested tags as well as matching brackets and doesn't stumble across invalid tags.

[![version][packagist-badge]][packagist]
Expand All @@ -29,12 +29,9 @@ Handles nested tags as well as matching brackets and doesn't stumble across inva
[donate]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WLYUNAT9ZTJZ4

# Requirements
- PHP 7+ ([PHP 5.6+ compatible branch](https://github.com/chillerlan/php-bbcode/tree/php5))
- PHP 7.2+

# Documentation
## BBCode docs
A documentation of the included BBCodes is planned and will be available soon™ on [the wiki](https://github.com/chillerlan/php-bbcode/wiki).

## Installation
**requires [composer](https://getcomposer.org)**

Expand All @@ -43,186 +40,32 @@ A documentation of the included BBCodes is planned and will be available soon&tr
```json
{
"require": {
"php": ">=7.0.3",
"php": ">=7.2.0",
"chillerlan/php-bbcode": "dev-master"
}
}
```

### Manual installation
Download the desired version of the package from [master](https://github.com/chillerlan/php-bbcode/archive/master.zip) or
Download the desired version of the package from [master](https://github.com/chillerlan/php-bbcode/archive/master.zip) or
[release](https://github.com/chillerlan/php-bbcode/releases) and extract the contents to your project folder. After that:
- run `composer install` to install the required dependencies and generate `/vendor/autoload.php`.
- if you use a custom autoloader, point the namespace `chillerlan\Database` to the folder `src` of the package
- if you use a custom autoloader, point the namespace `chillerlan\BBCode` to the folder `src` of the package

Profit!

## Usage
First of all, you'll need to import the needed classes of course:
```php
namespace MyProject;

use chillerlan\bbcode\Parser;
use chillerlan\bbcode\ParserOptions;
```

### Parser options
In order to create a `Parser` instance, you'll first want to create an instance of `ParserOptions` and alter it if needed.
However, this step is optional (meta, eh?).
```php
$options = new ParserOptions;
$options->languageInterface = MyLanguage::class;
$options->baseModuleInterface = MyAwesomeBaseModule::class;
$options->parserExtensionInterface = MyAwesomeParserExtension::class;
$options->sanitize = true;
$options->nesting_limit = 10;
$options->eol_placeholder = '__MYEOL__';
$options->bbtag_placeholder = '__MYBBTAG__';
$options->allowed_tags = ['mybbcode', 'somebbcode', 'whatever'];
$options->allow_all = false;
```

### Parser
Now we're ready to create the `Parser`:
```php
$bbcode = new Parser($options);

// or...

$bbcode = new Parser;
$bbcode->setOptions($options);
```

Run the parser and output:
```php
echo $bbcode->parse($some_string_containing_bbcode);
```

In case you need some diagnostics, here you go:
```php
$bbcode->getTagmap(); // map of tag -> module FQCN
$bbcode->getAllowed(); // all allowed tags
$bbcode->getNoparse(); // all noparse tags
$bbcode->getSingle(); // all singletags

// get all tags of a module
$module_tags = array_keys($bbcode->getTagmap(), MyAwesomeModule::class);
```

That's all!

## Extend the parser
### Base module
In order to create your own modules, you'll first need an empty base module which contains
all basic settings and methods for each module. To do so, you'll need to extend
`\chillerlan\bbcode\Modules\BaseModuleAbstract` . There's really not much to do, the only and most
important thing is to tell the parser which modules to use. Further, you need to specify
a `sanitize()` method and maybe an EOL token - the rest is up to you and may vary between output types.
```php
namespace Example\MyModules;

use chillerlan\bbcode\Modules\BaseModuleAbstract;
use Example\MyModules\MyAwesomeModule;

class MyAwesomeBaseModule extends BaseModuleAbstract{

protected $modules = [
MyAwesomeModule::class,
];

protected $eol_token = '<br />';

public function sanitize($content){
return htmlspecialchars($content, ENT_NOQUOTES|ENT_HTML5, 'UTF-8', false);
}

}
```

### Encoder module
Now that we have our base module, we're able to create the encoder module, where the actual transform happens.
Each encoder module extends a base module depending on output type (`MyAwesomeBaseModule` here)
and implements `\chillerlan\bbcode\Modules\ModuleInterface`. The property `$tags` and the method `__transform()` are mandatory.
In case your module supports noparse or single tags, you may set the respective properties `$noparse_tags` and `$singletags`.
```php
namespace Example\MyModules;

use chillerlan\bbcode\Modules\ModuleInterface;
use Example\MyModules\MyAwesomeBaseModule;

class MyAwesomeModule extends MyAwesomeBaseModule implements ModuleInterface{
- @todo

protected $tags = ['mybbcode', 'somebbcode', 'whatever'];
For an [implementation example](https://github.com/codemasher/gw1-database/blob/master/public/gwbbcode.php) see the over here: [gw1-database/GWBBCode](https://github.com/codemasher/gw1-database/tree/master/src/GWBBCode).

public function __transform(){
if(empty($this->content)){
return '';
}

return '<'.$this->tag.'>'.$this->content.'</'.$this->tag.'>';
}

}
```

You can also extend one of the existing modules to alter their behaviour, for example if you want the module to support more bbcodes.
In this case, you should be aware that the module already extends a base module, which will be used instead of your own.
However, the module information, EOL token and sanitize method of your base module will be used in the parser then
and should match the extended module's parent.
```php
namespace Example\MyModules;

use chillerlan\bbcode\Modules\ModuleInterface;
use chillerlan\bbcode\Modules\Html5\Simpletext;

class MyAwesomeModule extends Simpletext implements ModuleInterface{

protected $tags = [
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'strong', 'sub', 'sup', 'del', 'small', // default tags
'mybbcode', 'somebbcode', 'whatever', // your own tags
];

}
```

### Parser extension
The parser features an extension which allows you to alter the bbcode during the parsing process,
namely before and after the main parser unit runs. If you want to create your own parser extension,
just implement `\chillerlan\bbcode\ParserExtensionInterface`, set it in the parser options and you're done.
```php
namespace Example;

use chillerlan\bbcode\ParserExtensionInterface;

class MyAwesomeParserExtension implements ParserExtensionInterface{

public function pre($bbcode){

$search = [
"\t", // lets convert all tabs into 4 spaces
'{__BASE_URL__}', // assume we use a special token for our base url
];

$replace = [
' ',
'https://your.base/url/'
];

return str_replace($search, $replace, $bbcode);
}

public function post($bbcode){
return $bbcode;
}

}
```
### Language
- @todo

## Notes
The parser may cause some high CPU load, depending on the input. You should never consider to use it somewhere
in your output subsystem - not even with strong caching. Encode on input - you'll want a preview anyway. ;)
in your output subsystem - encode on input - you'll want a preview anyway. ;)

You may also run into several bugs. In fact, the BBCoder is essentially a tool to squeeze out any PCRE related bug in PHP known to man (and perhaps unknown). Have fun! ;)
[It is highly recommended to use these php.ini settings](https://github.com/chillerlan/php-bbcode/blob/master/travis-php.ini), especially to disable the PCRE JIT in PHP7 which is a troublemaker.
Expand Down
1 change: 0 additions & 1 deletion phpunit.xml
Expand Up @@ -8,7 +8,6 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
Expand Down
2 changes: 1 addition & 1 deletion src/BBCache.php
Expand Up @@ -14,7 +14,7 @@

use Psr\SimpleCache\CacheInterface;

class BBCache implements CacheInterface{
final class BBCache implements CacheInterface{

/**
* @var array
Expand Down
15 changes: 11 additions & 4 deletions src/BBCode.php
Expand Up @@ -28,7 +28,7 @@ class BBCode implements LoggerAwareInterface{
protected $options;

/**
* @var \Psr\SimpleCache\CacheInterface
* @var \Psr\SimpleCache\CacheInterface|\chillerlan\BBCode\BBCache
*/
protected $cache;

Expand Down Expand Up @@ -62,6 +62,11 @@ class BBCode implements LoggerAwareInterface{
*/
protected $allowed = [];

/**
* @var int
*/
protected $limit;

/**
* BBCode constructor.
*
Expand Down Expand Up @@ -143,8 +148,6 @@ public function setOptions(SettingsContainerInterface $options):BBCode{
}
}



if($this->options->preParse || $this->options->postParse){
$this->parserMiddleware = new $this->options->parserMiddlewareInterface($this->options, $this->cache, $this->logger);

Expand All @@ -161,6 +164,7 @@ public function setOptions(SettingsContainerInterface $options):BBCode{

$this->tags = $this->outputInterface->getTags();
$this->noparse = $this->outputInterface->getNoparse();
$this->limit = (int)$this->options->nestingLimit;

if(is_array($this->options->allowedTags) && !empty($this->options->allowedTags)){
$this->allowTags($this->options->allowedTags);
Expand Down Expand Up @@ -196,6 +200,9 @@ public function parse(string $bbcode):string{

// close singletags: [br] -> [br][/br]
$bbcode = preg_replace('#\[('.implode('|', $singleTags).')((?:\s|=)[^]]*)?]#is', '[$1$2][/$1]', $bbcode);

// @todo: find non-singletags without a closing tag and close them (or convert the brackets to entities)

// protect newlines
$bbcode = str_replace(["\r", "\n"], ['', $this->options->placeholder_eol], $bbcode);
// parse the bbcode
Expand Down Expand Up @@ -246,7 +253,7 @@ protected function parseBBCode($bbcode):string{
return '';
}

if($callback_count < (int)$this->options->nestingLimit && !in_array($tag, $this->noparse , true)){
if($callback_count < $this->limit && !in_array($tag, $this->noparse , true)){
$content = preg_replace_callback('#\[(\w+)((?:\s|=)[^]]*)?]((?:[^[]|\[(?!/?\1((?:\s|=)[^]]*)?])|(?R))*)\[/\1]#', __METHOD__, $content);
$e = preg_last_error();

Expand Down
1 change: 0 additions & 1 deletion src/BBCodeOptions.php
Expand Up @@ -12,7 +12,6 @@

namespace chillerlan\BBCode;


use chillerlan\Settings\SettingsContainerAbstract;

/**
Expand Down
4 changes: 1 addition & 3 deletions src/BBCodeOptionsTrait.php
Expand Up @@ -12,9 +12,7 @@

namespace chillerlan\BBCode;

use chillerlan\BBCode\Output\HTML\{
HTMLOutput, HTMLSanitizer
};
use chillerlan\BBCode\Output\HTML\{HTMLOutput, HTMLSanitizer};

trait BBCodeOptionsTrait{

Expand Down
30 changes: 29 additions & 1 deletion src/Output/BBCodeModuleAbstract.php
Expand Up @@ -18,10 +18,39 @@

abstract class BBCodeModuleAbstract implements BBCodeModuleInterface{

/**
* The current bbcode tag
*
* @var string
*/
protected $tag;

/**
* The attributes of the current bbcode
*
* @var array
*/
protected $attributes;

/**
* The content of the current bbcode
*
* @var string
*/
protected $content;

/**
* The full matched string of the current bbcode
*
* @var
*/
protected $match;

/**
* The callback counter
*
* @var
*/
protected $callback_count;

/**
Expand Down Expand Up @@ -249,5 +278,4 @@ protected function tagIn(array $whitelist, $default = false){
: $default;
}


}
11 changes: 5 additions & 6 deletions src/Output/BBCodeOutputAbstract.php
Expand Up @@ -17,7 +17,6 @@
use Psr\SimpleCache\CacheInterface;

abstract class BBCodeOutputAbstract implements BBCodeOutputInterface{
use ClassLoader;

/**
* @var string[]
Expand All @@ -43,6 +42,11 @@ abstract class BBCodeOutputAbstract implements BBCodeOutputInterface{
*/
protected $noparse = [];

/**
* @var string
*/
protected $eol = PHP_EOL;

/**
* @var \chillerlan\BBCode\BBCodeOptions
*/
Expand All @@ -63,11 +67,6 @@ abstract class BBCodeOutputAbstract implements BBCodeOutputInterface{
*/
protected $moduleInterfaces = [];

/**
* @var string
*/
protected $eol = PHP_EOL;

/**
* BBCodeOutputInterface constructor.
*
Expand Down

0 comments on commit be5f46e

Please sign in to comment.