From e8fdb5109de5506bb2d4a0fc52a73f5613af6b68 Mon Sep 17 00:00:00 2001 From: Ismo Vuorinen Date: Fri, 8 Jun 2018 13:02:36 +0300 Subject: [PATCH] Initial commit with tests, and stuff. --- .gitignore | 2 + .travis.yml | 22 ++ CHANGELOG.md | 87 +++++ CONTRIBUTING.md | 32 ++ LICENSE.md | 22 ++ README.md | 120 +++++++ composer.json | 50 +++ phpunit.xml | 21 ++ src/BBCodeParser.php | 245 ++++++++++++++ src/BBCodeParserServiceProvider.php | 36 +++ src/Facades/BBCodeParser.php | 11 + src/Traits/ArrayTrait.php | 26 ++ tests/ArrayTraitHelper.php | 30 ++ tests/ArrayTraitTest.php | 26 ++ tests/BBCodeParserFacadeTest.php | 34 ++ tests/BBCodeParserServiceProviderTest.php | 20 ++ tests/BBCodeParserTest.php | 376 ++++++++++++++++++++++ tests/TestCase.php | 21 ++ 18 files changed, 1181 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 composer.json create mode 100644 phpunit.xml create mode 100644 src/BBCodeParser.php create mode 100644 src/BBCodeParserServiceProvider.php create mode 100644 src/Facades/BBCodeParser.php create mode 100644 src/Traits/ArrayTrait.php create mode 100644 tests/ArrayTraitHelper.php create mode 100644 tests/ArrayTraitTest.php create mode 100644 tests/BBCodeParserFacadeTest.php create mode 100644 tests/BBCodeParserServiceProviderTest.php create mode 100644 tests/BBCodeParserTest.php create mode 100644 tests/TestCase.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfd6caa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/vendor +composer.lock \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..eec0e5a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +language: php +php: + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - nightly +matrix: + allow_failures: + - php: nightly + fast_finish: true +cache: + directories: + - $HOME/.composer/cache/files +before_script: + - phpenv global "$TRAVIS_PHP_VERSION" + - composer config extra.platform.php $TRAVIS_PHP_VERSION +install: + - flags="--ansi --prefer-dist --no-interaction --optimize-autoloader --no-suggest --no-progress" + - travis_wait 30 composer install $flags +script: +- ./vendor/bin/phpunit -c phpunit.xml diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..527a59a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,87 @@ +# Changelog + +All Notable changes to `ivuorinen\BBCodeParser` will be documented in this file. + +## v3.0.0 - 2018-06-08 + +- Project now in new hands +- Tests + +# Changelog of `Golonka\BBCodeParser` + +## v2.2.0 - 2015-09-07 + +### Added +- You can now strip all BBCode tags by using the ``stripBBCodeTags`` function. + +## v2.1.0 - 2015-06-20 + +### Added +- Made ``parseCaseSensitive`` and ``parseCaseInsensitive`` functions to make parsing more readable. + +## v2.0.0 - 2015-06-02 + +### Added +- Using PSR-4 instead of PSR-0 +- Moved the ``arrayOnly`` and ``arrayExcept`` functions into a trait +- Minimum supported PHP version bumped to 5.4 + +### Fixed +- Renamed some tag names, mostly making them all lowercase + - `` underLine -> underline `` + - `` lineThrough -> linethrough `` + - `` fontSize -> size `` + - `` fontColor -> color `` + - `` namedQuote -> namedquote `` + - `` namedQuote -> namedquote `` + - `` namedLink -> namedlink `` + - `` orderedListNumerical -> orderedlistnumerical `` + - `` orderedListAlpha -> orderedlistalpha `` + - `` unorderedList -> unorderedlist `` + - `` listItem -> listitem `` + +### Removed +- The ``iterate`` property is removed. Unneeded after improvements in parsing method. +- Removed deprecated tags ``[ul]`` and ``[ol]`` + +## v1.4 - 2015-05-05 + +### Added +- Optional parameter enables or disables case insensitivity. Disabled by default. + +## v1.3.0 - 2014-06-30 + +### Fixed +- The only/except functionally have been broken since like 1.1, but now it´s working. Better late then never! + +## v1.2.7 - 2014-05-19 + +### Added +- A new iterate key is added to tags that typically could contain more tags of the same kind, like quotes. + +### Fixed +- Problem where tags of the same kind would just parse the top level. + +## v1.2.6 - 2014-05-17 + +### Fixed +- Fixed a problem where if a tag had a line break in them they wouldn't parse. + +## v1.2.5 - 2014-05-15 + +### Fixed +- Improved most regex matches by removing unnecessary greediness. + +## v1.2.0 - 2014-03-25 + +### Fixed +- Better syntax for lists. + +## v1.1.0 - 2014-01-27 + +### Added +- Support for custom bbcode tags. + +## v1.0.0 - 2013-11-07 + +Released the package. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f564977 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +We accept contributions via Pull Requests on [Github](https://github.com/ivuorinen/bbcodeparser). + + +## Pull Requests + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. + +- **Create feature branches** - Don't ask us to pull from your master branch. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + + +## Running Tests + +``` bash +$ vendor/bin/phpunit +``` + + +**Happy coding**! \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..da008a4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,22 @@ +# The MIT License (MIT) + +Copyright (c) 2018 Ismo Vuorinen +Copyright (c) 2013-2015 Joseph Landberg + +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..499feb3 --- /dev/null +++ b/README.md @@ -0,0 +1,120 @@ +[![Latest Version](https://img.shields.io/github/release/ivuorinen/bbcodeparser.svg?style=flat-square)](https://github.com/ivuorinen/bbcodeparser/releases) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) +[![Build Status](https://img.shields.io/travis/ivuorinen/BBCodeParser/master.svg?style=flat-square)](https://travis-ci.org/ivuorinen/BBCodeParser) +[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/ivuorinen/bbcodeparser/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/ivuorinen/bbcodeparser/code-structure) +[![Quality Score](https://img.shields.io/scrutinizer/g/ivuorinen/bbcodeparser/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/ivuorinen/bbcodeparser) +[![Total Downloads](https://img.shields.io/packagist/dt/ivuorinen/bbcodeparser.svg?style=flat-square)](https://packagist.org/packages/ivuorinen/bbcodeparser) + +The ``ivuorinen\BBCodeParser`` package will help you with parsing BBCode. +This package is build on work by [Joseph Landberg](https://github.com/golonka). + +## Install + +Via Composer + +``` bash +$ composer require ivuorinen/bbcodeparser +``` + +## Usage +To parse some text it's as easy as this! +``` php +$bbcode = new ivuorinen\BBCode\BBCodeParser; + +echo $bbcode->parse('[b]Bold Text![/b]'); +// Bold Text! +``` +Would like the parser to not use all bbcodes? Just do like this. +``` php +$bbcode = new ivuorinen\BBCode\BBCodeParser; + +echo $bbcode->only('bold', 'italic') + ->parse('[b][u]Bold[/u] [i]Italic[/i]![/b]'); + // [u]Bold[/u] Italic! + +echo $bbcode->except('bold') + ->parse('[b]Bold[/b] [i]Italic[/i]'); + // [b]Bold[/b] Italic +``` + +By default the parser is case sensitive. But if you would like the parser to accept tags like `` [B]Bold Text[/B] `` it's really easy. +``` php +$bbcode = new ivuorinen\BBCode\BBCodeParser; + +// Case insensitive +echo $bbcode->parse('[b]Bold[/b] [I]Italic![/I]', true); + // Bold Italic! + +// Or like this + +echo $bbcode->parseCaseInsensitive('[b]Bold[/b] [i]Italic[/i]'); + // Bold Italic! +``` +You could also make it more explicit that the parser is case sensitive by using another helper function. +``` php + $bbcode = new ivuorinen\BBCode\BBCodeParser; + + echo $bbcode->parseCaseSensitive('[b]Bold[/b] [I]Italic![/I]'); + // Bold [I]Italic![/I] +``` + +If you would like to completely remove all BBCode it's just one function call away. +``` php + $bbcode = new ivuorinen\BBCode\BBCodeParser; + + echo $bbcode->stripBBCodeTags('[b]Bold[/b] [i]Italic![/i]'); + // Bold Italic! +``` + +## Laravel integration +The integration into Laravel is really easy, and the method is the same for both Laravel 4 and Laravel 5. +This package supports Laravel Package Auto-Discovery, so it should be picked up automatically. + +If you don't want auto-discovery, or because of old habits, just open your ``app.php`` config file. + +In there you just add this to your providers array +``` php +'ivuorinen\BBCode\BBCodeParserServiceProvider' +``` + +And this to your facades array +``` php +'BBCode' => 'ivuorinen\BBCode\Facades\BBCodeParser' +``` + +The syntax is the same as if you would use it in vanilla PHP but with the ``BBCode::`` before the methods. +Here are some examples. +``` php +// Simple parsing +echo BBCode::parse('[b]Bold Text![/b]'); + +// Limiting the parsers with the only method +echo BBCode::only('bold', 'italic') + ->parse('[b][u]Bold[/u] [i]Italic[/i]![/b]'); + // [u]Bold[/u] Italic! + +// Or the except method +echo BBCode::except('bold') + ->parse('[b]Bold[/b] [i]Italic[/i]'); + // [b]Bold[/b] Italic +``` + +## Testing + +``` bash +$ phpunit +``` + +## Contributing + +Please see [CONTRIBUTING](CONTRIBUTING.md) for details. + +## Credits + +- [Ismo Vuorinen](https://github.com/ivuorinen) +- [Joseph Landberg](https://github.com/golonka) +- [All Contributors](../../contributors) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..15c4bdb --- /dev/null +++ b/composer.json @@ -0,0 +1,50 @@ +{ + "name": "ivuorinen/bbcodeparser", + "description": "Parse your BBCode easy with this library.", + "keywords": ["bbcode", "parser", "laravel", "psr-1", "psr-2", "psr-4"], + "homepage": "https://github.com/ivuorinen/bbcodeparser", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Ismo Vuorinen", + "email": "ismo@ivuorinen.net", + "homepage": "https://github.com/ivuorinen" + }, + { + "name": "Joseph Landberg", + "email": "joseph.landberg@gmail.com", + "homepage": "https://github.com/golonka/" + } + ], + "require": { + "php": ">=7" + }, + "require-dev": { + "phpunit/phpunit": "~5", + "squizlabs/php_codesniffer": "~2", + "orchestra/testbench": "~3.0" + }, + "autoload": { + "psr-4": { + "ivuorinen\\BBCode\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { "ivuorinen\\BBCode\\Tests\\": "tests/" }, + "classmap": [ + "tests/" + ] + }, + "minimum-stability": "stable", + "extra": { + "laravel": { + "providers": [ + "ivuorinen\\BBCode\\BBCodeParserServiceProvider" + ], + "aliases": { + "BBCode": "ivuorinen\\BBCode\\Facades\\BBCodeParser" + } + } + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..b08155f --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,21 @@ + + + + + ./tests + + + + + ./src + + + \ No newline at end of file diff --git a/src/BBCodeParser.php b/src/BBCodeParser.php new file mode 100644 index 0000000..60f405d --- /dev/null +++ b/src/BBCodeParser.php @@ -0,0 +1,245 @@ + [ + 'pattern' => '/\[b\](.*?)\[\/b\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'italic' => [ + 'pattern' => '/\[i\](.*?)\[\/i\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'underline' => [ + 'pattern' => '/\[u\](.*?)\[\/u\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'linethrough' => [ + 'pattern' => '/\[s\](.*?)\[\/s\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'size' => [ + 'pattern' => '/\[size\=([1-7])\](.*?)\[\/size\]/s', + 'replace' => '$2', + 'content' => '$2' + ], + 'color' => [ + 'pattern' => '/\[color\=(#[A-f0-9]{6}|#[A-f0-9]{3})\](.*?)\[\/color\]/s', + 'replace' => '$2', + 'content' => '$2' + ], + 'center' => [ + 'pattern' => '/\[center\](.*?)\[\/center\]/s', + 'replace' => '
$1
', + 'content' => '$1' + ], + 'left' => [ + 'pattern' => '/\[left\](.*?)\[\/left\]/s', + 'replace' => '
$1
', + 'content' => '$1' + ], + 'right' => [ + 'pattern' => '/\[right\](.*?)\[\/right\]/s', + 'replace' => '
$1
', + 'content' => '$1' + ], + 'quote' => [ + 'pattern' => '/\[quote\](.*?)\[\/quote\]/s', + 'replace' => '
$1
', + 'content' => '$1' + ], + 'namedquote' => [ + 'pattern' => '/\[quote\=(.*?)\](.*)\[\/quote\]/s', + 'replace' => '
$1$2
', + 'content' => '$2' + ], + 'link' => [ + 'pattern' => '/\[url\](.*?)\[\/url\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'namedlink' => [ + 'pattern' => '/\[url\=(.*?)\](.*?)\[\/url\]/s', + 'replace' => '$2', + 'content' => '$2' + ], + 'image' => [ + 'pattern' => '/\[img\](.*?)\[\/img\]/s', + 'replace' => '', + 'content' => '$1' + ], + 'orderedlistnumerical' => [ + 'pattern' => '/\[list=1\](.*?)\[\/list\]/s', + 'replace' => '
    $1
', + 'content' => '$1' + ], + 'orderedlistalpha' => [ + 'pattern' => '/\[list=a\](.*?)\[\/list\]/s', + 'replace' => '
    $1
', + 'content' => '$1' + ], + 'unorderedlist' => [ + 'pattern' => '/\[list\](.*?)\[\/list\]/s', + 'replace' => '
    $1
', + 'content' => '$1' + ], + 'listitem' => [ + 'pattern' => '/\[\*\](.*)/', + 'replace' => '
  • $1
  • ', + 'content' => '$1' + ], + 'code' => [ + 'pattern' => '/\[code\](.*?)\[\/code\]/s', + 'replace' => '$1', + 'content' => '$1' + ], + 'youtube' => [ + 'pattern' => '/\[youtube\](.*?)\[\/youtube\]/s', + 'replace' => '', + 'content' => '$1' + ], + 'linebreak' => [ + 'pattern' => '/\r\n/', + 'replace' => '
    ', + 'content' => '' + ] + ]; + + private $enabledParsers; + + public function __construct() + { + $this->enabledParsers = $this->parsers; + } + + /** + * Parses the BBCode string + * @param string $source String containing the BBCode + * @param bool $caseInsensitive + * @return string Parsed string + */ + public function parse($source, $caseInsensitive = false) + { + foreach ($this->enabledParsers as $name => $parser) { + $pattern = ($caseInsensitive) ? $parser['pattern'] . 'i' : $parser['pattern']; + + $source = $this->searchAndReplace($pattern, $parser['replace'], $source); + } + return $source; + } + + /** + * Remove all BBCode + * @param string $source + * @return string Parsed text + */ + public function stripBBCodeTags($source) + { + foreach ($this->parsers as $name => $parser) { + $source = $this->searchAndReplace($parser['pattern'] . 'i', $parser['content'], $source); + } + return $source; + } + + /** + * Searches after a specified pattern and replaces it with provided structure + * @param string $pattern Search pattern + * @param string $replace Replacement structure + * @param string $source Text to search in + * @return string Parsed text + */ + protected function searchAndReplace($pattern, $replace, $source) + { + while (preg_match($pattern, $source)) { + $source = preg_replace($pattern, $replace, $source); + } + + return $source; + } + + /** + * Helper function to parse case sensitive + * @param string $source String containing the BBCode + * @return string Parsed text + */ + public function parseCaseSensitive($source) + { + return $this->parse($source, false); + } + + /** + * Helper function to parse case insensitive + * @param string $source String containing the BBCode + * @return string Parsed text + */ + public function parseCaseInsensitive($source) + { + return $this->parse($source, true); + } + + /** + * Limits the parsers to only those you specify + * @param mixed $only parsers + * @return object BBCodeParser object + */ + public function only($only = null) + { + $only = (is_array($only)) ? $only : func_get_args(); + $this->enabledParsers = $this->arrayOnly($this->parsers, $only); + return $this; + } + + /** + * Removes the parsers you want to exclude + * @param mixed $except parsers + * @return BBCodeParser + */ + public function except($except = null) + { + $except = (is_array($except)) ? $except : func_get_args(); + $this->enabledParsers = $this->arrayExcept($this->parsers, $except); + return $this; + } + + /** + * List of chosen parsers + * @return array array of parsers + */ + public function getParsers() + { + return $this->enabledParsers; + } + + /** + * Sets the parser pattern and replace. + * This can be used for new parsers or overwriting existing ones. + * @param string $name Parser name + * @param string $pattern Pattern + * @param string $replace Replace pattern + * @param string $content Parsed text pattern + * @return void + */ + public function setParser($name, $pattern, $replace, $content) + { + $this->parsers[$name] = array( + 'pattern' => $pattern, + 'replace' => $replace, + 'content' => $content + ); + + $this->enabledParsers[$name] = array( + 'pattern' => $pattern, + 'replace' => $replace, + 'content' => $content + ); + } +} diff --git a/src/BBCodeParserServiceProvider.php b/src/BBCodeParserServiceProvider.php new file mode 100644 index 0000000..040027f --- /dev/null +++ b/src/BBCodeParserServiceProvider.php @@ -0,0 +1,36 @@ +app->bind('bbcode', function () { + return new BBCodeParser; + }); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return ['bbcode']; + } +} diff --git a/src/Facades/BBCodeParser.php b/src/Facades/BBCodeParser.php new file mode 100644 index 0000000..d7a9ab9 --- /dev/null +++ b/src/Facades/BBCodeParser.php @@ -0,0 +1,11 @@ +arrayOnly($parsers, $only); + } + + /** + * @param array $parsers + * @param $except + * @see \ivuorinen\BBCode\Traits\ArrayTrait::arrayExcept + * @return array + */ + public function publicArrayExcept(array $parsers, $except) + { + return $this->arrayExcept($parsers, $except); + } +} diff --git a/tests/ArrayTraitTest.php b/tests/ArrayTraitTest.php new file mode 100644 index 0000000..f1885dd --- /dev/null +++ b/tests/ArrayTraitTest.php @@ -0,0 +1,26 @@ +class = new ArrayTraitHelper(); + } + + public function test_array_only() + { + $this->assertTrue(\method_exists($this->class, 'arrayOnly')); + } + + public function test_array_except() + { + $this->assertTrue(\method_exists($this->class, 'arrayExcept')); + } +} diff --git a/tests/BBCodeParserFacadeTest.php b/tests/BBCodeParserFacadeTest.php new file mode 100644 index 0000000..60f9e6c --- /dev/null +++ b/tests/BBCodeParserFacadeTest.php @@ -0,0 +1,34 @@ +callMethod( + new \ivuorinen\BBCode\Facades\BBCodeParser, + 'getFacadeAccessor', + [] + ); + $this->assertEquals('bbcode', $method); + } catch (ReflectionException $e) { + $this->throwException($e); + } + } + + /** + * @param $obj + * @param $name + * @param array $args + * @return mixed + * @throws ReflectionException + */ + public static function callMethod($obj, $name, array $args) + { + $class = new \ReflectionClass($obj); + $method = $class->getMethod($name); + $method->setAccessible(true); + return $method->invokeArgs($obj, $args); + } + +} diff --git a/tests/BBCodeParserServiceProviderTest.php b/tests/BBCodeParserServiceProviderTest.php new file mode 100644 index 0000000..cfec3e7 --- /dev/null +++ b/tests/BBCodeParserServiceProviderTest.php @@ -0,0 +1,20 @@ +app); + $this->assertEquals(['bbcode'], $provider->provides()); + } + + public function testRegister() + { + $this->assertInstanceOf( + \ivuorinen\BBCode\BBCodeParser::class, + $this->app['bbcode'] + ); + } +} diff --git a/tests/BBCodeParserTest.php b/tests/BBCodeParserTest.php new file mode 100644 index 0000000..590c861 --- /dev/null +++ b/tests/BBCodeParserTest.php @@ -0,0 +1,376 @@ + array( + 'bbcode' => 'b', + 'expected' => 'strong', + ), + 'italic' => array( + 'bbcode' => 'i', + 'expected' => 'em', + ), + 'underline' => array( + 'bbcode' => 'u' + ), + 'linethrough' => array( + 'bbcode' => 'u' + ), + 'size' => array( + 'bbcode_test' => '[size=%s]%s[/size]', + 'expected_test' => '%s', + 'values' => ['1', 'text'] + ), + 'color' => array( + 'bbcode_test' => '[color=%s]%s[/color]', + 'expected_test' => '%s', + 'values' => ['#123456', 'color'] + ), + 'center' => array( + 'bbcode' => 'center', + 'expected_test' => '
    %s
    ', + 'values' => ['center aligned'] + ), + 'left' => array( + 'bbcode' => 'left', + 'expected_test' => '
    %s
    ', + 'values' => ['left aligned'] + ), + 'right' => array( + 'bbcode' => 'right', + 'expected_test' => '
    %s
    ', + 'values' => ['right aligned'] + ), + 'quote' => array( + 'bbcode' => 'quote', + 'expected_test' => '
    %s
    ', + 'values' => ['quotable text'] + ), + 'namedquote' => array( + 'bbcode_test' => '[quote=%s]%s[/quote]', + 'expected_test' => '
    %s%s
    ', + 'values' => ['name', 'quotable text'] + ), + 'link' => array( + 'bbcode' => 'url', + 'bbcode_test' => '[url]%s[/url]', + 'expected_test' => '%1$s', + 'values' => ['value'] + ), + 'namedlink' => array( + 'bbcode' => 'url', + 'bbcode_test' => '[url=%s]%s[/url]', + 'expected_test' => '%s', + 'values' => ['link', 'value'] + ), + 'image' => array( + 'bbcode' => 'img', + 'expected_test' => '', + 'values' => ['link'] + ), + 'linebreak' => array( + 'bbcode_test' => "\r\n", + 'expected_test' => '
    ' + ), + 'code' => array( + 'bbcode' => 'code', + 'values' => ['link'] + ), + 'youtube' => array( + 'bbcode' => 'youtube', + 'expected_test' => '', + 'values' => ['dQw4w9WgXcQ'] + ), + 'listitem' => array( + 'bbcode_test' => "[*] List item", + 'expected_test' => '
  • List item
  • ' + ), + 'orderedlistnumerical' => array( + 'bbcode' => 'list', + 'bbcode_test' => '[list=1]%s[/list]', + 'expected_test' => '
      %s
    ', + 'values' => ['X'] + ), + 'orderedlistalpha' => array( + 'bbcode' => 'list', + 'bbcode_test' => '[list=a]%s[/list]', + 'expected_test' => '
      %s
    ', + 'values' => ['X'] + ), + 'unorderedlist' => array( + 'bbcode' => 'list', + 'bbcode_test' => '[list]%s[/list]', + 'expected_test' => '
      %s
    ', + 'values' => ['X'] + ), + ); + + public $lowerCaseTests = array( + ['bbcode' => '[code]Result[/code]', 'expected' => 'Result'], + ['bbcode' => '[i]Result[/i]', 'expected' => 'Result'], + ); + + public $upperCaseTests = array( + ['bbcode' => '[CODE]Result[/CODE]', 'expected' => 'Result'], + ['bbcode' => '[I]Result[/I]', 'expected' => 'Result'], + ); + + public $mixedCaseTests = array( + ['bbcode' => '[Code]Result[/Code]', 'expected' => 'Result'], + ['bbcode' => '[I]Result[/i]', 'expected' => 'Result'], + ); + + protected function setUp() + { + parent::setUp(); + + $this->parser = new BBCodeParser(); + } + + public function test_it_has_known_parsers() + { + $this->assertEquals( + $this->parser->parsers, + $this->parser->getParsers() + ); + } + + public function test_parsers_have_required_keys() + { + $keys = array('pattern', 'replace', 'content'); + $parsers = $this->parser->getParsers(); + + foreach ($parsers as $parserName => $parser) { + foreach ($keys as $key) { + $this->assertArrayHasKey( + $key, + $parser, + sprintf('Parser: %s, Key: %s', $parserName, $key) + ); + } + } + } + + public function test_parser_regexp_is_valid() + { + $parsers = $this->parser->getParsers(); + foreach ($parsers as $parserName => $parser) { + $pattern = $parser['pattern']; + + $this->assertTrue( + $this->assertRegexpIsValid($pattern), + sprintf('Key: %s, Regexp %s', $parserName, $pattern) + ); + } + } + + public function test_parser_default() + { + foreach ($this->testedParsers as $name => $options) { + $test = $this->createTest($options, $name); + + $result = $this->parser->parse($test['bbcode_test']); + $this->assertEquals($test['expected_test'], $result, $name); + } + } + + public function test_parser_sensitive() + { + $testTemp = 'Test: %s / bbcode: %s / Actual: %s / Expected: %s'; + + /** + * We expect these to pass + * Using: assertEquals + */ + foreach ($this->lowerCaseTests as $case) { + $bbcode = $case['bbcode']; + $expected = $case['expected']; + + $result = $this->parser->parseCaseSensitive($bbcode); + $this->assertEquals($expected, $result, sprintf( + $testTemp, 'lowercase, sensitive', $bbcode, $result, $expected + )); + } + + /** + * We expect these to fail + * Using: assertNotEquals + */ + foreach ($this->upperCaseTests as $case) { + $bbcode = $case['bbcode']; + $expected = $case['expected']; + + $result = $this->parser->parseCaseSensitive($bbcode); + $this->assertNotEquals($expected, $result, sprintf( + $testTemp, 'uppercase, sensitive', $bbcode, $result, $expected + )); + } + + /** + * We expect these to fail + * Using: assertNotEquals + */ + foreach ($this->mixedCaseTests as $case) { + $bbcode = $case['bbcode']; + $expected = $case['expected']; + + $result = $this->parser->parseCaseSensitive($bbcode); + $this->assertNotEquals($expected, $result, sprintf( + $testTemp, 'mixed case, sensitive', $bbcode, $result, $expected + )); + } + } + + public function test_parser_insensitive() + { + /** + * Now we run with insensitive case turned on, so everything + * should pass -- why this is not the default in original + * package baffles me. + */ + $cases = array_merge( + $this->lowerCaseTests, + $this->upperCaseTests, + $this->mixedCaseTests + ); + + foreach ($cases as $case) { + $bbcode = $case['bbcode']; + $expected = $case['expected']; + $this->assertEquals( + $expected, $this->parser->parse($bbcode, true) + ); + + $this->assertEquals( + $expected, + $this->parser->parseCaseInsensitive($bbcode) + ); + } + } + + public function test_stripBBCodeTags() + { + foreach ($this->mixedCaseTests as $test) { + $this->assertEquals( + \strip_tags($test['expected']), + $this->parser->stripBBCodeTags($test['bbcode']) + ); + } + } + + public function test_only() + { + $keys = array('bold', 'underline'); + + $parsers = $this->parser->getParsers(); + $onlyFew = $this->parser->only($keys)->getParsers(); + + $this->assertEquals($keys, array_keys($onlyFew)); + $this->assertNotEquals(array_keys($parsers), array_keys($onlyFew)); + } + + public function test_except() + { + $keys = array('bold', 'underline'); + + $parsers = $this->parser->getParsers(); + $exceptFew = $this->parser->except($keys)->getParsers(); + + $this->assertNotEquals($keys, array_keys($exceptFew)); + $this->assertNotEquals(array_keys($parsers), array_keys($exceptFew)); + } + + public function test_adding_a_new_parser() + { + $this->parser->setParser('test', 'x', 'y', 'z'); + $expected = array('pattern' => 'x', 'replace' => 'y', 'content' => 'z'); + $parsers = $this->parser->getParsers(); + + $this->assertArrayHasKey('test', $parsers); + $this->assertArraySubset($expected, $parsers['test']); + } + + public function test_we_have_all_parsers_tested() + { + $parsers = array_keys($this->parser->except($this->skipParsers)->getParsers()); + $missing = array(); + + foreach ($parsers as $parser) { + $result = isset($this->testedParsers[$parser]); + + if ($result) { + $this->assertTrue($result, sprintf('Parser missing: %s', $parser)); + } + + if (!$result) { + $missing[$parser] = $parser; + } + } + + if (!empty($missing)) { + $missing = sprintf( + 'Missing tests: %s', + implode(", ", $missing) + ); + $this->markTestIncomplete($missing); + } + } + + /** + * This is a helper to use our self::testedParsers + * rules for automated test generation. Automation ftw. + * + * @param array $options + * @param $name + * @return array + */ + public function createTest($options = [], $name) + { + if (!isset($options['bbcode'])) { + $options['bbcode'] = $name; + } + + if (!isset($options['values']) || empty($options['values'])) { + $options['values'] = ['Testing']; + } + + if (!isset($options['bbcode_test'])) { + $bbcodeValues = $options['values']; + array_unshift($bbcodeValues, $options['bbcode']); + array_push($bbcodeValues, $options['bbcode']); + $options['bbcode_test'] = vsprintf('[%s]%s[/%s]', $bbcodeValues); + } else { + $options['bbcode_test'] = vsprintf($options['bbcode_test'], $options['values']); + } + + if (!isset($options['expected'])) { + $options['expected'] = $options['bbcode']; + } + + if (!isset($options['expected_test'])) { + $expectedValues = $options['values']; + array_unshift($expectedValues, $options['expected']); + array_push($expectedValues, $options['expected']); + $options['expected_test'] = vsprintf('<%s>%s', $expectedValues); + } else { + $options['expected_test'] = vsprintf($options['expected_test'], $options['values']); + } + + return $options; + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..06b69fc --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,21 @@ +