2010-05-06 14:54:35 +02:00
|
|
|
<?php
|
|
|
|
|
2011-07-03 11:28:55 +02:00
|
|
|
namespace Knp\Bundle\MarkdownBundle\Parser;
|
2010-05-06 14:54:35 +02:00
|
|
|
|
2011-07-03 11:28:55 +02:00
|
|
|
use Knp\Bundle\MarkdownBundle\MarkdownParserInterface;
|
2011-04-07 08:35:00 +02:00
|
|
|
|
2014-02-13 16:52:14 +01:00
|
|
|
use Michelf\MarkdownExtra;
|
2010-05-06 14:54:35 +02:00
|
|
|
|
2010-07-22 12:06:54 +02:00
|
|
|
/**
|
2011-02-04 15:28:05 +01:00
|
|
|
* MarkdownParser
|
|
|
|
*
|
2010-07-22 12:06:54 +02:00
|
|
|
* This class extends the original Markdown parser.
|
|
|
|
* It allows to disable unwanted features to increase performances.
|
|
|
|
*/
|
2014-02-13 16:52:14 +01:00
|
|
|
class MarkdownParser extends MarkdownExtra implements MarkdownParserInterface
|
2010-05-06 14:54:35 +02:00
|
|
|
{
|
2010-05-12 01:25:34 +02:00
|
|
|
/**
|
2012-10-15 12:24:11 +02:00
|
|
|
* Use the constructor to disable some of them
|
|
|
|
*
|
|
|
|
* @var array Enabled features
|
2010-05-12 01:25:34 +02:00
|
|
|
*/
|
2021-11-15 02:39:23 +01:00
|
|
|
protected array $features = array(
|
2010-05-12 01:25:34 +02:00
|
|
|
'header' => true,
|
|
|
|
'list' => true,
|
|
|
|
'horizontal_rule' => true,
|
|
|
|
'table' => true,
|
|
|
|
'foot_note' => true,
|
|
|
|
'fenced_code_block' => true,
|
|
|
|
'abbreviation' => true,
|
|
|
|
'definition_list' => true,
|
|
|
|
'inline_link' => true, // [link text](url "optional title")
|
|
|
|
'reference_link' => true, // [link text] [id]
|
|
|
|
'shortcut_link' => true, // [link text]
|
2013-12-16 16:38:02 +01:00
|
|
|
'images' => true,
|
2010-05-12 01:25:34 +02:00
|
|
|
'block_quote' => true,
|
|
|
|
'code_block' => true,
|
|
|
|
'html_block' => true,
|
|
|
|
'auto_link' => true,
|
|
|
|
'auto_mailto' => true,
|
2015-05-05 12:39:37 +02:00
|
|
|
'entities' => true,
|
2012-05-28 03:39:35 +02:00
|
|
|
'no_html' => false,
|
2010-05-12 01:25:34 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new instance and enable or disable features.
|
|
|
|
* @param array $features enabled or disabled features
|
2011-02-04 15:28:05 +01:00
|
|
|
*
|
2010-05-12 01:25:34 +02:00
|
|
|
* You can pass an array of features to disable some of them for performance improvement.
|
|
|
|
* E.g.
|
|
|
|
* $features = array(
|
|
|
|
* 'table' => false,
|
|
|
|
* 'definition_list' => false
|
|
|
|
* )
|
|
|
|
*/
|
|
|
|
public function __construct(array $features = array())
|
2010-05-06 14:54:35 +02:00
|
|
|
{
|
2010-05-12 01:25:34 +02:00
|
|
|
parent::__construct();
|
|
|
|
|
|
|
|
$this->features = array_merge($this->features, $features);
|
|
|
|
|
|
|
|
if (!$this->features['header']) {
|
|
|
|
unset($this->block_gamut['doHeaders']);
|
|
|
|
}
|
|
|
|
if (!$this->features['list']) {
|
|
|
|
unset($this->block_gamut['doLists']);
|
|
|
|
}
|
|
|
|
if (!$this->features['horizontal_rule']) {
|
|
|
|
unset($this->block_gamut['doHorizontalRules']);
|
|
|
|
}
|
|
|
|
if (!$this->features['table']) {
|
|
|
|
unset($this->block_gamut['doTables']);
|
|
|
|
}
|
|
|
|
if (!$this->features['foot_note']) {
|
|
|
|
unset($this->document_gamut['stripFootnotes']);
|
|
|
|
unset($this->document_gamut['appendFootnotes']);
|
|
|
|
unset($this->span_gamut['doFootnotes']);
|
|
|
|
}
|
|
|
|
if (!$this->features['fenced_code_block']) {
|
|
|
|
unset($this->document_gamut['doFencedCodeBlocks']);
|
|
|
|
unset($this->block_gamut['doFencedCodeBlocks']);
|
|
|
|
}
|
|
|
|
if (!$this->features['abbreviation']) {
|
|
|
|
unset($this->document_gamut['stripAbbreviations']);
|
|
|
|
unset($this->span_gamut['doAbbreviations']);
|
|
|
|
}
|
|
|
|
if (!$this->features['definition_list']) {
|
|
|
|
unset($this->block_gamut['doDefLists']);
|
|
|
|
}
|
|
|
|
if (!$this->features['reference_link']) {
|
|
|
|
unset($this->document_gamut['stripLinkDefinitions']);
|
|
|
|
}
|
2013-12-16 16:38:02 +01:00
|
|
|
if (!$this->features['images']) {
|
|
|
|
unset($this->span_gamut['doImages']);
|
|
|
|
}
|
2012-10-15 12:24:11 +02:00
|
|
|
if (!$this->features['block_quote']) {
|
2010-05-12 01:25:34 +02:00
|
|
|
unset($this->block_gamut['doBlockQuotes']);
|
|
|
|
}
|
2012-10-15 12:24:11 +02:00
|
|
|
if (!$this->features['code_block']) {
|
2010-05-12 01:25:34 +02:00
|
|
|
unset($this->block_gamut['doCodeBlocks']);
|
|
|
|
}
|
2012-10-15 12:24:11 +02:00
|
|
|
if (!$this->features['auto_link']) {
|
2010-05-12 01:25:34 +02:00
|
|
|
unset($this->span_gamut['doAutoLinks']);
|
|
|
|
}
|
2015-05-05 12:39:37 +02:00
|
|
|
if (false === $this->features['entities']) {
|
2010-05-12 01:25:34 +02:00
|
|
|
$this->no_entities = true;
|
|
|
|
}
|
2015-05-05 12:27:51 +02:00
|
|
|
if (true === $this->features['no_html']) {
|
2015-08-20 09:02:23 +02:00
|
|
|
$this->no_markup = true;
|
2014-05-25 16:59:34 +02:00
|
|
|
}
|
2010-05-12 01:25:34 +02:00
|
|
|
}
|
|
|
|
|
2012-10-15 12:24:11 +02:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public function transformMarkdown($text)
|
|
|
|
{
|
2014-02-13 16:52:14 +01:00
|
|
|
return parent::transform($text);
|
2012-10-15 12:24:11 +02:00
|
|
|
}
|
|
|
|
|
2010-05-12 01:25:34 +02:00
|
|
|
/**
|
|
|
|
* MarkdownExtraParser overwritten methods
|
|
|
|
*/
|
2010-05-06 14:54:35 +02:00
|
|
|
|
2010-05-12 01:25:34 +02:00
|
|
|
/**
|
|
|
|
* Simplify detab
|
|
|
|
*/
|
2021-11-05 17:50:12 +01:00
|
|
|
public function detab($text): string
|
2010-05-12 01:25:34 +02:00
|
|
|
{
|
|
|
|
return str_replace("\t", str_repeat(' ', $this->tab_width), $text);
|
|
|
|
}
|
2012-10-15 12:24:11 +02:00
|
|
|
|
|
|
|
public function _initDetab()
|
2010-05-12 01:25:34 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disable unless html_block
|
|
|
|
*/
|
2012-10-15 12:24:11 +02:00
|
|
|
public function hashHTMLBlocks($text)
|
2010-05-12 01:25:34 +02:00
|
|
|
{
|
|
|
|
if (!$this->features['html_block']) {
|
|
|
|
return $text;
|
|
|
|
}
|
2010-05-06 14:54:35 +02:00
|
|
|
|
2010-05-12 01:25:34 +02:00
|
|
|
return parent::hashHTMLBlocks($text);
|
2010-05-06 14:54:35 +02:00
|
|
|
}
|
|
|
|
|
2010-05-12 01:25:34 +02:00
|
|
|
/**
|
|
|
|
* Disable mailto unless auto_mailto
|
|
|
|
*/
|
2012-10-15 12:24:11 +02:00
|
|
|
public function doAutoLinks($text)
|
2010-05-06 14:54:35 +02:00
|
|
|
{
|
2012-10-15 12:24:11 +02:00
|
|
|
if (!$this->features['auto_mailto']) {
|
|
|
|
return preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', array(&$this, '_doAutoLinks_url_callback'), $text);
|
2010-05-12 01:25:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return parent::doAutoLinks($text);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Conditional features: reference_link, inline_link,
|
|
|
|
*/
|
2012-10-15 12:24:11 +02:00
|
|
|
public function doAnchors($text)
|
2010-05-12 01:25:34 +02:00
|
|
|
{
|
|
|
|
#
|
|
|
|
# Turn Markdown link shortcuts into XHTML <a> tags.
|
|
|
|
#
|
2012-10-15 12:24:11 +02:00
|
|
|
if ($this->in_anchor) {
|
2010-05-12 01:25:34 +02:00
|
|
|
return $text;
|
2012-10-15 12:24:11 +02:00
|
|
|
}
|
2010-05-12 01:25:34 +02:00
|
|
|
$this->in_anchor = true;
|
|
|
|
|
|
|
|
#
|
|
|
|
# First, handle reference-style links: [link text] [id]
|
|
|
|
#
|
|
|
|
if ($this->features['reference_link']) {
|
|
|
|
$text = preg_replace_callback('{
|
2012-10-15 12:24:11 +02:00
|
|
|
( # wrap whole match in $1
|
2010-05-12 01:25:34 +02:00
|
|
|
\[
|
|
|
|
('.$this->nested_brackets_re.') # link text = $2
|
|
|
|
\]
|
|
|
|
|
2012-10-15 12:24:11 +02:00
|
|
|
[ ]? # one optional space
|
|
|
|
(?:\n[ ]*)? # one optional newline followed by spaces
|
2010-05-12 01:25:34 +02:00
|
|
|
|
|
|
|
\[
|
2012-10-15 12:24:11 +02:00
|
|
|
(.*?) # id = $3
|
2010-05-12 01:25:34 +02:00
|
|
|
\]
|
|
|
|
)
|
|
|
|
}xs',
|
|
|
|
array(&$this, '_doAnchors_reference_callback'), $text);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Next, inline-style links: [link text](url "optional title")
|
|
|
|
#
|
|
|
|
if ($this->features['inline_link']) {
|
|
|
|
$text = preg_replace_callback('{
|
2012-10-15 12:24:11 +02:00
|
|
|
( # wrap whole match in $1
|
2010-05-12 01:25:34 +02:00
|
|
|
\[
|
|
|
|
('.$this->nested_brackets_re.') # link text = $2
|
|
|
|
\]
|
2012-10-15 12:24:11 +02:00
|
|
|
\( # literal parent
|
2010-05-12 01:25:34 +02:00
|
|
|
[ \n]*
|
|
|
|
(?:
|
2012-10-15 12:24:11 +02:00
|
|
|
<(.+?)> # href = $3
|
2010-05-12 01:25:34 +02:00
|
|
|
|
|
|
|
|
('.$this->nested_url_parenthesis_re.') # href = $4
|
|
|
|
)
|
|
|
|
[ \n]*
|
2012-10-15 12:24:11 +02:00
|
|
|
( # $5
|
|
|
|
([\'"]) # quote char = $6
|
|
|
|
(.*?) # Title = $7
|
|
|
|
\6 # matching quote
|
|
|
|
[ \n]* # ignore any spaces/tabs between closing quote and )
|
|
|
|
)? # title is optional
|
2010-05-12 01:25:34 +02:00
|
|
|
\)
|
2016-01-08 04:20:19 +01:00
|
|
|
(?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
|
2010-05-12 01:25:34 +02:00
|
|
|
)
|
|
|
|
}xs',
|
|
|
|
array(&$this, '_doAnchors_inline_callback'), $text);
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# Last, handle reference-style shortcuts: [link text]
|
|
|
|
# These must come last in case you've also got [link text][1]
|
|
|
|
# or [link text](/foo)
|
|
|
|
#
|
|
|
|
if ($this->features['shortcut_link']) {
|
|
|
|
$text = preg_replace_callback('{
|
2012-10-15 12:24:11 +02:00
|
|
|
( # wrap whole match in $1
|
2010-05-12 01:25:34 +02:00
|
|
|
\[
|
2012-10-15 12:24:11 +02:00
|
|
|
([^\[\]]+) # link text = $2; can\'t contain [ or ]
|
2010-05-12 01:25:34 +02:00
|
|
|
\]
|
|
|
|
)
|
|
|
|
}xs',
|
|
|
|
array(&$this, '_doAnchors_reference_callback'), $text);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->in_anchor = false;
|
2012-05-27 19:09:50 +02:00
|
|
|
|
2012-10-15 12:24:11 +02:00
|
|
|
return $text;
|
2012-05-27 19:09:50 +02:00
|
|
|
}
|
2010-07-22 12:06:54 +02:00
|
|
|
}
|