1
0
mirror of https://github.com/flextype/flextype.git synced 2025-08-10 07:06:45 +02:00

Merge branch '562-parsers-improvements-and-refactoring' into dev

This commit is contained in:
Awilum
2021-08-05 14:15:54 +03:00
17 changed files with 258 additions and 58 deletions

View File

@@ -57,7 +57,8 @@
"php-di/php-di": "^6.3.4",
"php-di/slim-bridge": "^3.1.1",
"middlewares/whoops": "^2.0",
"nette/neon": "^3.2"
"nette/neon": "^3.2",
"league/commonmark": "^2.0"
},
"suggest": {
"ext-zend-opcache": "Recommended for better performance",

View File

@@ -19,17 +19,24 @@ if (registry()->get('flextype.settings.entries.fields.parsers.enabled')) {
if (entries()->registry()->get('fetch.data.parsers') != null) {
foreach (entries()->registry()->get('fetch.data.parsers') as $parserName => $parserData) {
if (in_array($parserName, ['shortcodes'])) {
if (in_array($parserName, ['shortcodes', 'markdown'])) {
if (entries()->registry()->get('fetch.data.parsers.'.$parserName.'.enabled') === true) {
if (entries()->registry()->get('fetch.data.parsers.'.$parserName.'.fields') != null) {
if (is_array(entries()->registry()->get('fetch.data.parsers.'.$parserName.'.fields'))) {
foreach (entries()->registry()->get('fetch.data.parsers.'.$parserName.'.fields') as $field) {
if (! in_array($field, registry()->get('flextype.settings.entries.fields'))) {
if ($parserName == 'markdown') {
if (arrays(entries()->registry()->get('fetch.data'))->has($field)) {
entries()->registry()->set('fetch.data.'.$field,
parsers()->markdown()->parse(entries()->registry()->get('fetch.data.'.$field), $cache));
}
}
if ($parserName == 'shortcodes') {
if (arrays(entries()->registry()->get('fetch.data'))->has($field)) {
entries()->registry()->set('fetch.data.'.$field,
parsers()->shortcodes()->process(entries()->registry()->get('fetch.data.'.$field), $cache));
parsers()->shortcodes()->parse(entries()->registry()->get('fetch.data.'.$field), $cache));
}
}
}

View File

@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);
/**
* Flextype (https://flextype.org)
* Founded by Sergey Romanenko and maintained by Flextype Community.
*/
namespace Flextype\Parsers;
use Exception;
use League\CommonMark\MarkdownConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Attributes\AttributesExtension;
use League\CommonMark\Extension\Table\TableExtension;
use function flextype;
use function strings;
final class Markdown
{
/**
* Markdown instance.
*/
private static ?Markdown $instance = null;
/**
* Markdown Environment
*/
private $environment = null;
/**
* Markdown Converter
*/
private $converter = null;
/**
* Markdown should not be cloneable.
*/
protected function __clone()
{
throw new Exception('Cannot clone a Markdown.');
}
/**
* Markdown should not be restorable from strings.
*/
public function __wakeup(): void
{
throw new Exception('Cannot unserialize a Markdown.');
}
/**
* Markdown construct
*/
protected function __construct()
{
$this->environment = new Environment(registry()->get('flextype.settings.parsers.markdown.commonmark'));
$this->environment->addExtension(new CommonMarkCoreExtension());
$this->environment->addExtension(new AttributesExtension());
$this->environment->addExtension(new TableExtension());
$this->converter = new MarkdownConverter($this->environment);
}
/**
* Markdown Environment
*/
public function environment(): Environment
{
return $this->environment;
}
/**
* Markdown Converter
*/
public function converter(): MarkdownConverter
{
return $this->converter;
}
/**
* Gets the instance via lazy initialization (created on first usage).
*/
public static function getInstance(): Markdown
{
if (static::$instance === null) {
static::$instance = new self();
}
return static::$instance;
}
/**
* Takes a MARKDOWN encoded string and converts it into a PHP variable.
*
* @param string $input A string containing MARKDOWN
*
* @return mixed The MARKDOWN converted to a PHP value
*/
public function parse(string $input)
{
$cache = registry()->get('flextype.settings.parsers.markdown.cache');
if ($cache === true && registry()->get('flextype.settings.cache.enabled') === true) {
$key = $this->getCacheID($input);
if ($dataFromCache = cache()->get($key)) {
return $dataFromCache;
}
$data = $this->converter()->convertToHtml($input)->getContent();
cache()->set($key, $data);
return $data;
}
return $this->converter()->convertToHtml($input)->getContent();
}
/**
* Get Cache ID for markdown.
*
* @param string $input Input.
*
* @return string Cache ID.
*
* @access public
*/
public function getCacheID(string $input): string
{
return strings('markdown' . $input)->hash()->toString();
}
}

View File

@@ -11,6 +11,7 @@ namespace Flextype\Parsers;
use Atomastic\Macroable\Macroable;
use Flextype\Parsers\Shortcodes;
use Flextype\Parsers\Markdown;
class Parsers
{
@@ -23,4 +24,12 @@ class Parsers
{
return Shortcodes::getInstance();
}
/**
* Create a Markdown instance.
*/
public function markdown(): Markdown
{
return Markdown::getInstance();
}
}

View File

@@ -18,12 +18,12 @@ use function strings;
final class Shortcodes
{
/**
* Registry instance
* Shortcodes instance.
*/
private static ?Shortcodes $instance = null;
/**
* Shortcode facade
* Shortcode facade.
*/
private $shortcodeFacade = null;
@@ -44,7 +44,7 @@ final class Shortcodes
}
/**
* Shortcode construct
* Shortcode construct.
*/
protected function __construct()
{
@@ -52,7 +52,7 @@ final class Shortcodes
}
/**
* Gets the instance via lazy initialization (created on first usage)
* Gets the instance via lazy initialization (created on first usage).
*/
public static function getInstance(): Shortcodes
{
@@ -64,7 +64,7 @@ final class Shortcodes
}
/**
* Shortcode facade
* Shortcode facade.
*/
public function facade(): ShortcodeFacade
{
@@ -76,7 +76,7 @@ final class Shortcodes
*/
public function initShortcodes(): void
{
$shortcodes = registry()->get('flextype.settings.parsers.shortcodes');
$shortcodes = registry()->get('flextype.settings.parsers.shortcodes.shortcodes');
if (
! isset($shortcodes) ||
@@ -103,8 +103,8 @@ final class Shortcodes
/**
* Add shortcode handler.
*
* @param string $name Shortcode
* @param callable $handler Handler
* @param string $name Shortcode.
* @param callable $handler Handler.
*
* @access public
*/
@@ -116,8 +116,8 @@ final class Shortcodes
/**
* Add event handler.
*
* @param string $name Event
* @param callable $handler Handler
* @param string $name Event.
* @param callable $handler Handler.
*
* @access public
*/
@@ -133,21 +133,23 @@ final class Shortcodes
*
* @access public
*/
public function parse(string $input)
public function parseText(string $input)
{
return $this->facade()->parse($input);
}
/**
* Processes text and replaces shortcodes.
* Parse and processes text to replaces shortcodes.
*
* @param string $input A text containing SHORTCODE
* @param bool $cache Cache result data or no. Default is true
* @param bool $cache Cache result data or no. Default is true.
*
* @access public
*/
public function process(string $input, bool $cache = true)
public function parse(string $input)
{
$cache = registry()->get('flextype.settings.parsers.shortcodes.cache');
if ($cache === true && registry()->get('flextype.settings.cache.enabled') === true) {
$key = $this->getCacheID($input);
@@ -165,11 +167,11 @@ final class Shortcodes
}
/**
* Get Cache ID for shortcode
* Get Cache ID for shortcode.
*
* @param string $input Input
* @param string $input Input.
*
* @return string Cache ID
* @return string Cache ID.
*
* @access public
*/

View File

@@ -12,7 +12,7 @@ namespace Flextype\Parsers\Shortcodes;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
// Shortcode: [entries_fetch id="entry-id" field="field-name" default="default-value"]
if (registry()->get('flextype.settings.parsers.shortcodes.entries.enabled')) {
if (registry()->get('flextype.settings.parsers.shortcodes.shortcodes.entries.enabled')) {
parsers()->shortcodes()->addHandler('entries_fetch', static function (ShortcodeInterface $s) {
return arrays(entries()->fetch($s->getParameter('id')))->get($s->getParameter('field'), $s->getParameter('default'));
});

View File

@@ -12,7 +12,7 @@ namespace Flextype\Parsers\Shortcodes;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
// Shortcode: [media_files_fetch id="media-id" field="field-name" default="default-value"]
if (registry()->get('flextype.settings.parsers.shortcodes.media.enabled')) {
if (registry()->get('flextype.settings.parsers.shortcodes.shortcodes.media.enabled')) {
parsers()->shortcodes()->addHandler('media_files_fetch', static function (ShortcodeInterface $s) {
return arrays(flextype('media')->files()->fetch($s->getParameter('id')))->get($s->getParameter('field'), $s->getParameter('default'));
});

View File

@@ -14,7 +14,7 @@ use Thunder\Shortcode\Events;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
// Shortcode: [raw]
if (registry()->get('flextype.settings.parsers.shortcodes.raw.enabled')) {
if (registry()->get('flextype.settings.parsers.shortcodes.shortcodes.raw.enabled')) {
parsers()->shortcodes()->addHandler('raw', static function (ShortcodeInterface $s) {
return $s->getContent();
});

View File

@@ -12,7 +12,7 @@ namespace Flextype\Parsers\Shortcodes;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
// Shortcode: [registry_get name="item-name" default="default-value"]
if (registry()->get('flextype.settings.parsers.shortcodes.registry.enabled')) {
if (registry()->get('flextype.settings.parsers.shortcodes.shortcodes.registry.enabled')) {
parsers()->shortcodes()->addHandler('registry_get', static function (ShortcodeInterface $s) {
return registry()->get($s->getParameter('name'), $s->getParameter('default'));
});

View File

@@ -13,7 +13,7 @@ use Slim\Http\Environment;
use Slim\Http\Uri;
// Shortcode: [url]
if (registry()->get('flextype.settings.parsers.shortcodes.url.enabled')) {
if (registry()->get('flextype.settings.parsers.shortcodes.shortcodes.url.enabled')) {
parsers()->shortcodes()->addHandler('url', static function () {
if (registry()->has('flextype.settings.url') && registry()->get('flextype.settings.url') !== '') {
return registry()->get('flextype.settings.url');

View File

@@ -417,26 +417,56 @@ serializers:
# Parsers
#
# Shortcode
#
# - shortcodes: Flextype Shortcodes to load.
# markdown.cache: Cache result data or no. Default is true.
# markdown.commonmark.renderer.block_separator: String to use for separating renderer block elements.
# markdown.commonmark.renderer.inner_separator: String to use for separating inner block contents.
# markdown.commonmark.renderer.soft_break: String to use for rendering soft breaks.
# markdown.commonmark.commonmark.enable_em: Disable <em> parsing by setting to false; enable with true (default: true)
# markdown.commonmark.commonmark.enable_strong: Disable <strong> parsing by setting to false; enable with true (default: true)
# markdown.commonmark.commonmark.use_asterisk: Disable parsing of * for emphasis by setting to false; enable with true (default: true)
# markdown.commonmark.commonmark.use_underscore: Disable parsing of _ for emphasis by setting to false; enable with true (default: true)
# markdown.commonmark.commonmark.unordered_list_markers: Array of characters that can be used to indicate a bulleted list.
# markdown.commonmark.html_input: `strip` all HTML (equivalent to 'safe' => true). `allow` all HTML input as-is (default value; equivalent to `safe => false) `escape` Escape all HTML.
# markdown.commonmark.allow_unsafe_links: Remove risky link and image URLs by setting this to false (default: true).
# markdown.commonmark.max_nesting_level: The maximum nesting level for blocks (default: PHP_INT_MAX). Setting this to a positive integer can help protect against long parse times and/or segfaults if blocks are too deeply-nested.
# markdown.commonmark.slug_normalizer.max_length: Limits the size of generated slugs (defaults to 255 characters)
parsers:
markdown:
cache: true
commonmark:
renderer:
block_separator: "\n"
inner_separator: "\n"
soft_break: "\n"
commonmark:
enable_em: true
enable_strong: true
use_asterisk: true
use_underscore: true
unordered_list_markers: ['-', '*', '+']
html_input: 'allow'
allow_unsafe_links: false
max_nesting_level: 9223372036854775807
slug_normalizer:
max_length: 255
shortcodes:
media:
path: "/src/flextype/Parsers/Shortcodes/MediaShortcode.php"
enabled: true
entries:
path: "/src/flextype/Parsers/Shortcodes/EntriesShortcode.php"
enabled: true
raw:
path: "/src/flextype/Parsers/Shortcodes/RawShortcode.php"
enabled: true
registry:
path: "/src/flextype/Parsers/Shortcodes/RegistryShortcode.php"
enabled: true
url:
path: "/src/flextype/Parsers/Shortcodes/UrlShortcode.php"
enabled: true
cache: true
shortcodes:
media:
path: "/src/flextype/Parsers/Shortcodes/MediaShortcode.php"
enabled: true
entries:
path: "/src/flextype/Parsers/Shortcodes/EntriesShortcode.php"
enabled: true
raw:
path: "/src/flextype/Parsers/Shortcodes/RawShortcode.php"
enabled: true
registry:
path: "/src/flextype/Parsers/Shortcodes/RegistryShortcode.php"
enabled: true
url:
path: "/src/flextype/Parsers/Shortcodes/UrlShortcode.php"
enabled: true
# CORS
#

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
test('test getInstance() method', function () {
$this->assertInstanceOf(Flextype\Parsers\Markdown::class, parsers()->markdown()->getInstance());
});
test('test parse() method', function () {
$this->assertEquals('<p><strong>Bold</strong></p>', trim(parsers()->markdown()->parse('**Bold**')));
});
test('test getCacheID() method', function () {
$this->assertNotEquals(parsers()->markdown()->getCacheID('**Bold**'),
parsers()->markdown()->getCacheID('**Bold Text**'));
});

View File

@@ -20,19 +20,19 @@ test('test addEventHandler() method', function () {
return 'Barz';
});
parsers()->shortcodes()->addEventHandler(Events::FILTER_SHORTCODES, new FilterRawEventHandler(['barz']));
$this->assertEquals('Barz', parsers()->shortcodes()->process('[barz]'));
$this->assertEquals('Barz', parsers()->shortcodes()->parse('[barz]'));
});
test('test parseText() method', function () {
$this->assertInstanceOf(Thunder\Shortcode\ShortcodeFacade::class, parsers()->shortcodes()->addHandler('bar', static function() { return ''; }));
$this->assertTrue(is_array(parsers()->shortcodes()->parseText('[bar]')));
$this->assertTrue(is_object(parsers()->shortcodes()->parseText('[bar]')[0]));
});
test('test parse() method', function () {
$this->assertInstanceOf(Thunder\Shortcode\ShortcodeFacade::class, parsers()->shortcodes()->addHandler('bar', static function() { return ''; }));
$this->assertTrue(is_array(parsers()->shortcodes()->parse('[bar]')));
$this->assertTrue(is_object(parsers()->shortcodes()->parse('[bar]')[0]));
});
test('test process() method', function () {
$this->assertInstanceOf(Thunder\Shortcode\ShortcodeFacade::class, parsers()->shortcodes()->addHandler('zed', static function() { return 'Zed'; }));
$this->assertEquals('Zed', parsers()->shortcodes()->process('[zed]'));
$this->assertEquals('fòôBàřZed', parsers()->shortcodes()->process('fòôBàř[zed]'));
$this->assertEquals('Zed', parsers()->shortcodes()->parse('[zed]'));
$this->assertEquals('fòôBàřZed', parsers()->shortcodes()->parse('fòôBàř[zed]'));
});
test('test getCacheID() method', function () {

View File

@@ -12,6 +12,6 @@ afterEach(function (): void {
test('test entries_fetch shortcode', function () {
$this->assertTrue(entries()->create('foo', ['title' => 'Foo']));
$this->assertEquals('Foo', parsers()->shortcodes()->process('[entries_fetch id="foo" field="title"]'));
$this->assertEquals('Bar', parsers()->shortcodes()->process('[entries_fetch id="foo" field="bar" default="Bar"]'));
$this->assertEquals('Foo', parsers()->shortcodes()->parse('[entries_fetch id="foo" field="title"]'));
$this->assertEquals('Bar', parsers()->shortcodes()->parse('[entries_fetch id="foo" field="bar" default="Bar"]'));
});

View File

@@ -13,5 +13,5 @@ afterEach(function (): void {
test('test raw shortcode', function () {
$this->assertTrue(entries()->create('foo', ['title' => 'Foo']));
$this->assertEquals('[entries_fetch id="foo" field="title"]',
parsers()->shortcodes()->process('[raw][entries_fetch id="foo" field="title"][/raw]'));
parsers()->shortcodes()->parse('[raw][entries_fetch id="foo" field="title"][/raw]'));
});

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
test('test registry_get shortcode', function () {
$this->assertEquals('Flextype',
parsers()->shortcodes()->process('[registry_get name="flextype.manifest.name"]'));
parsers()->shortcodes()->parse('[registry_get name="flextype.manifest.name"]'));
$this->assertEquals('default-value',
parsers()->shortcodes()->process('[registry_get name="item-name" default="default-value"]'));
parsers()->shortcodes()->parse('[registry_get name="item-name" default="default-value"]'));
});

View File

@@ -5,5 +5,5 @@ declare(strict_types=1);
test('test registry_get shortcode', function () {
registry()->set('flextype.settings.url', 'https://flextype.org');
$this->assertStringContainsString('https://flextype.org', parsers()->shortcodes()->process('[url]'));
$this->assertStringContainsString('https://flextype.org', parsers()->shortcodes()->parse('[url]'));
});