1
0
mirror of https://github.com/erusev/parsedown.git synced 2025-09-04 12:15:27 +02:00

Compare commits

...

134 Commits

Author SHA1 Message Date
Emanuil Rusev
0b274ac959 Format installation command in README 2025-08-31 10:57:44 +03:00
Emanuil Rusev
5b74b74145 Update README.md 2025-06-05 08:59:58 +03:00
Emanuil Rusev
95c7e4c3d7 Update README.md 2025-05-20 21:25:06 +03:00
Emanuil Rusev
c9dc49f68f Update README.md 2024-12-01 18:36:17 +02:00
Emanuil Rusev
28a9b057c7 Update README.md 2024-12-01 17:27:57 +02:00
Emanuil Rusev
e0082193b1 Update README.md 2024-12-01 17:27:10 +02:00
Emanuil Rusev
999fcf7886 update version 2024-11-10 09:39:46 +02:00
Emanuil Rusev
582f9f9cd1 Merge pull request #881 from xPaw/merge-1.8.x
Merge `1.8.x-beta` into `master`
2024-10-12 10:06:08 +03:00
Emanuil Rusev
e76c4e44be Update README.md 2024-09-08 10:15:41 +03:00
Pavel Djundik
cfb313fb1f Revert "Merge pull request #876 from xabbuh/branch-alias"
This reverts commit 0476f3be5b, reversing
changes made to 9d00deadcd.
2024-08-28 11:19:02 +03:00
Pavel Djundik
9b14567c57 Fix commonmark test 2024-08-28 11:18:17 +03:00
Pavel Djundik
c65c451606 Merge branch '1.8.x-beta' 2024-08-28 11:15:16 +03:00
Emanuil Rusev
0476f3be5b Merge pull request #876 from xabbuh/branch-alias
add a branch alias to allow installing untagged 1.8 versions
2024-07-18 10:01:16 +03:00
Emanuil Rusev
9d00deadcd Merge pull request #872 from xabbuh/pr-868-1.8
[PHP 8.4] Fixes for implicit nullability deprecation
2024-07-17 09:19:21 +03:00
Emanuil Rusev
15048b0210 Merge pull request #875 from xabbuh/pr-874
fix GitHub Actions config file syntax
2024-07-17 09:15:20 +03:00
Christian Flothmann
e7a3bccbae add a branch alias to allow installing untagged 1.8 versions 2024-07-17 05:59:36 +02:00
Christian Flothmann
26362c5fe9 fix GitHub Actions config file syntax 2024-07-17 05:52:46 +02:00
Emanuil Rusev
89880dd0a9 Merge pull request #874 from xabbuh/ci-1.8
enable GitHub Actions and drop support for PHP < 7.1
2024-07-15 13:33:21 +03:00
Christian Flothmann
44fd383db7 run tests using GitHub actions 2024-07-15 10:51:54 +02:00
Christian Flothmann
54f1ffc214 drop support for PHP < 7.1 2024-07-15 10:46:34 +02:00
Ayesh Karunaratne
6ae01284b8 [PHP 8.4] Fixes for implicit nullability deprecation
Fixes all issues that emit deprecation notices on PHP 8.4 for implicit nullable parameter type declarations.

See:
 - [RFC](https://wiki.php.net/rfc/deprecate-implicitly-nullable-types)
 - [PHP 8.4: Implicitly nullable parameter declarations deprecated](https://php.watch/versions/8.4/implicitly-marking-parameter-type-nullable-deprecated)
2024-07-09 10:54:53 +02:00
Emanuil Rusev
26cfde9dbf Merge pull request #868 from Ayesh/php84/nullability
[PHP 8.4] Fixes for implicit nullability deprecation
2024-07-09 09:15:45 +03:00
Emanuil Rusev
232c57201e Update README.md 2024-06-10 10:08:19 +03:00
Emanuil Rusev
5362f4cbe3 Update README.md 2024-05-11 08:10:21 +03:00
Emanuil Rusev
b3e2fa192c Update README.md 2024-04-11 08:59:50 +03:00
Ayesh Karunaratne
908754bcdd [PHP 8.4] Fixes for implicit nullability deprecation
Fixes all issues that emit deprecation notices on PHP 8.4 for implicit nullable parameter type declarations.

See:
 - [RFC](https://wiki.php.net/rfc/deprecate-implicitly-nullable-types)
 - [PHP 8.4: Implicitly nullable parameter declarations deprecated](https://php.watch/versions/8.4/implicitly-marking-parameter-type-nullable-deprecated)
2024-03-16 02:44:40 +07:00
Emanuil Rusev
1ff0382739 Update README.md 2024-03-12 07:27:45 +02:00
Emanuil Rusev
f5aa6fd1ca Update README.md 2024-02-11 11:36:09 +02:00
Emanuil Rusev
77947eda2f Update README.md 2023-10-13 11:10:28 +03:00
Emanuil Rusev
0586729b46 Update README.md 2023-10-13 11:09:51 +03:00
Emanuil Rusev
a86a4e19da Update README.md 2023-10-13 11:08:28 +03:00
Emanuil Rusev
6598f3860c update readme 2020-08-09 17:12:21 +03:00
Emanuil Rusev
1e5080190c update readme
we already say "warning" in bold and upper case — the warning emoji feels unnecessary
2020-08-09 17:06:19 +03:00
Emanuil Rusev
1610e4747c Merge pull request #741 from GrahamCampbell/patch-1
Update .travis.yml
2020-02-18 12:38:52 +02:00
Emanuil Rusev
3159a9d3cd Merge pull request #751 from jeanmonod/patch-1
Update GitHub flavored markdown url
2020-01-22 09:53:45 +02:00
Jeanmonod David
dbee8ab4f2 Update GitHub flavored markdown url 2020-01-21 22:07:12 +01:00
Graham Campbell
dba4125b59 Update .travis.yml 2019-12-20 00:05:10 +00:00
Aidan Woods
87b57bf3cb Add test to prevent regression 2019-04-10 21:54:34 +01:00
Aidan Woods
bfaa76d370 Reflect travis breadth in README
Closes https://github.com/erusev/parsedown/issues/692
2019-04-07 16:36:22 +01:00
Aidan Woods
3825db53a2 Merge branch '1.8.x-beta' 2019-04-06 17:58:29 +01:00
Aidan Woods
fe7a50eceb New release due to mislabeled previous tag 2019-03-17 18:47:21 +00:00
Aidan Woods
bce642f2d7 7.3 was released 2019-03-17 18:38:54 +00:00
Aidan Woods
7d4c06cb52 Bump version 2019-03-17 17:19:07 +00:00
Aidan Woods
f7b66e6b20 Merge pull request #701 from aidantwoods/fix/spaces-in-class-names-1.8.x-beta
[1.8.x-beta] Fix spaces in class names
2019-03-17 17:10:10 +00:00
Aidan Woods
811bc32726 Fix test platforms 2019-03-17 17:04:25 +00:00
Aidan Woods
8fd5464c46 [1.8.x-beta] Fix spaces in class names 2019-03-17 17:01:52 +00:00
Aidan Woods
21c8c792de Merge pull request #698 from cybernet/patch-1
Symfony Demo link update
2019-03-14 18:49:20 +00:00
cybernet
6ca29539e1 Symfony Demo link update 2019-03-12 20:49:21 +00:00
Emanuil Rusev
a503c1a69b composer/composer#7990 2019-03-02 12:08:48 +02:00
Emanuil Rusev
819c68899d Simplify installation instructions 2018-12-28 13:17:22 +02:00
Emanuil Rusev
4c2d79fc6a More consistent code blocks in readme 2018-12-28 12:50:30 +02:00
Emanuil Rusev
48a2fb26fe Add badges to readme 2018-12-28 12:47:13 +02:00
Emanuil Rusev
33b79d2446 More logo padding and no underline 2018-12-28 02:21:55 +02:00
Emanuil Rusev
d6d2d96459 Add repo name as h1 2018-12-28 02:19:44 +02:00
Emanuil Rusev
d5b6ab5198 Readme logo to be centered 2018-12-28 02:14:25 +02:00
Emanuil Rusev
15e8439c7f Back to smaller padding in readme logo 2018-12-28 02:04:16 +02:00
Emanuil Rusev
ff6148f9b9 Improve readme badges 2018-12-28 01:56:45 +02:00
Emanuil Rusev
dfd8657bc5 h1 around logo in readme 2018-12-28 01:28:59 +02:00
Emanuil Rusev
ee64646765 More padding for logo in readme 2018-12-28 01:21:51 +02:00
Emanuil Rusev
c956090b55 Update readme logo 2018-12-28 01:07:49 +02:00
Emanuil Rusev
0be26550f3 Update readme logo 2018-12-28 00:55:48 +02:00
Emanuil Rusev
8e26a65a6f More consistent letter case in readme 2018-12-28 00:41:14 +02:00
Emanuil Rusev
2a24a8583b More consistent formatting in readme 2018-12-28 00:36:04 +02:00
Emanuil Rusev
1d55344e92 Simpler readme header 2018-12-27 23:59:37 +02:00
Emanuil Rusev
5dc8d1cc39 Simpler readme examples 2018-12-27 23:51:07 +02:00
Emanuil Rusev
33cf0f0b99 Centered header in readme 2018-12-27 23:46:53 +02:00
Emanuil Rusev
dc1ff7d6c2 Remove horizontal rule from readme 2018-12-27 22:50:39 +02:00
Emanuil Rusev
0f0987571d Bigger headings in readme 2018-12-27 22:32:54 +02:00
Emanuil Rusev
18eaa649b5 Add logo to readme 2018-12-27 22:23:17 +02:00
Aidan Woods
e124572b60 Merge pull request #675 from andreybolonin/patch-1
add php 7.3
2018-11-16 07:45:32 +00:00
Andrey Bolonin
1686a34469 add php 7.3 to allow_failures 2018-11-16 09:34:17 +02:00
Aidan Woods
2bd7113c55 Merge pull request #676 from aidantwoods/fix/uninitialized-string-offset
Fix access to potentially uninitialised offset
2018-11-06 21:49:46 +00:00
Aidan Woods
29fce0ec37 Fix access to potentially uninitialised offset 2018-11-06 21:10:23 +00:00
Andrey Bolonin
b0bbc275d4 add php 7.3 2018-10-31 15:48:28 +02:00
Aidan Woods
72f9ca92ae Merge pull request #671 from aidantwoods/fix/line-ending-standardisation
Ensure line-breaks get standardised when using Parsedown via `line` method
2018-10-16 18:51:05 +01:00
Aidan Woods
89c3fa05d9 Ensure line-breaks get standardised when using via line method
As noted in https://github.com/erusev/parsedown/pull/624 there are
occasions where line break standardisation is assumed (e.g. where
`inlineCode` replaces line breaks with a space).

Closes #624
2018-10-16 18:41:42 +01:00
Aidan Woods
69163d6e88 Merge pull request #670 from tillkruss/patch-1
Add "tel:" to whitelist
2018-10-14 21:48:40 +01:00
Till Krüss
3a0c964291 Add "tel:" to whitelist 2018-10-02 16:38:21 -07:00
Aidan Woods
1829106e60 Merge pull request #668 from itshoro/block-heading-min
remove redundant header level cap
2018-09-19 21:28:02 +01:00
horodev
464f5f9329 removed min function as it is redundant in the context 2018-09-19 17:36:40 +02:00
Aidan Woods
c26a2ee4bf Bump beta version 2018-06-11 19:15:32 +01:00
Aidan Woods
ba3b60d6e4 Merge pull request #641 from aidantwoods/fix/api-stability-complete-function-removal
Restore existence of protected API methods
2018-06-08 14:38:42 +01:00
Aidan Woods
0b1e6b8c86 Restore existence of protected API methods 2018-06-07 19:47:09 +01:00
Aidan Woods
1f69f7e697 Bump version 2018-05-08 22:46:15 +01:00
Aidan Woods
c83af0a7d5 Merge pull request #628 from aidantwoods/fix/revert-rawHtml-breaks-insertion
Preserve plain-text in AST to avoid blinding extensions to it
2018-05-08 22:41:44 +01:00
Aidan Woods
4686daf8c2 Preserve plain-text in AST to avoid blinding extensions to it 2018-05-08 22:32:57 +01:00
Aidan Woods
c9e7183cfa Merge pull request #627 from aidantwoods/fix/hidden-blocks
Intepret special "hidden" key as an empty element
2018-05-08 22:07:51 +01:00
Aidan Woods
9eed1104e7 Intepret special "hidden" key as an empty element 2018-05-08 21:54:30 +01:00
Aidan Woods
fd95703da5 Version bump 2018-05-07 14:26:12 +01:00
Aidan Woods
8d172a2994 Merge pull request #614 from aidantwoods/enhancement/performance-tweaks
General and performance tweaks
2018-05-07 11:18:21 +01:00
Aidan Woods
dfab7240a4 Merge pull request #621 from paukenba/master
Tilde characters may be escaped
2018-04-24 17:39:09 +01:00
Attila Vachter
113c6d2b21 Tilde characters may be escaped 2018-04-23 15:09:30 +02:00
Aidan Woods
a9764ec90f Remove complex string interpolation expressions 2018-04-14 15:27:06 +01:00
Aidan Woods
0a842fb5b1 Merge pull request #615 from aidantwoods/fix/old-handler-compat
Compatability fixes
2018-04-12 22:29:29 +01:00
Aidan Woods
7f4318dbdb PHP 5.3 == 💩 2018-04-12 22:22:53 +01:00
Aidan Woods
3e70819a20 Readability improvements, thanks @PhrozenByte 2018-04-12 22:16:25 +01:00
Aidan Woods
2bf7ca41a0 Add compat for extensions using old markup key. 2018-04-12 21:25:50 +01:00
Aidan Woods
b75fd409ff Must unset text key so that our destination is preferred as content 2018-04-12 21:10:09 +01:00
Aidan Woods
88a3f31dd7 Rewrite as one statement 2018-04-12 19:33:01 +01:00
Aidan Woods
726d4ef44a Sanity checks before starting regex engine 2018-04-09 18:09:45 +01:00
Aidan Woods
450a74fedf More expensive statement last 2018-04-09 18:09:45 +01:00
Aidan Woods
7e15d99d90 Remove regex from block rule 2018-04-09 18:09:44 +01:00
Aidan Woods
d2dd736e1b Remove regex from fenced code block
Also remove unused function
2018-04-09 18:09:44 +01:00
Aidan Woods
e74a5bd7ed In theory PHP stores the length of strings, so looking this up should be quick 2018-04-09 18:09:44 +01:00
Aidan Woods
b53aa74a72 Use standard library function 2018-04-09 18:09:44 +01:00
Aidan Woods
3ea08140b6 Remove use of array 2018-04-09 18:09:44 +01:00
Aidan Woods
c45e41950f Use standard library over while loop 2018-04-09 18:09:44 +01:00
Aidan Woods
2faba6fef5 Remove unneeded complete function 2018-04-09 18:09:44 +01:00
Aidan Woods
b42add3762 Make some regexes possesive 2018-04-09 18:09:43 +01:00
Aidan Woods
107223d3a0 Avoid recomputation 2018-04-09 18:09:43 +01:00
Aidan Woods
d4f1ac465c String interpolation is slightly faster than concat 2018-04-09 18:09:43 +01:00
Aidan Woods
d6e306d620 Optimise commonly used regexes to fail fast 2018-04-09 18:09:04 +01:00
Aidan Woods
dc5cf8770b The AST has high complexity here (and so traversal is hard anyway)
We gain quite a bit of a speed boost by working with text here
since this is a very common function
2018-04-09 18:09:04 +01:00
Aidan Woods
70f5c02d47 Use non-nestable values as keys for O(1) lookup 2018-04-09 18:09:04 +01:00
Aidan Woods
90ad738933 General readability 2018-04-09 18:09:04 +01:00
Aidan Woods
f2327023c1 No need to unset if not set 2018-04-09 18:09:04 +01:00
Aidan Woods
6f13f97674 Use mutating loop instead of array_map 2018-04-09 18:08:58 +01:00
Aidan Woods
8091e5586a Merge pull request #612 from aidantwoods/fix/table-columns
Table header should not be allowed to contain new lines
2018-04-09 16:53:07 +01:00
Aidan Woods
cb33daf0e6 Assert table header does not contain new lines 2018-04-09 16:38:03 +01:00
Aidan Woods
c440c91af5 Add failing test case 2018-04-09 16:32:36 +01:00
Aidan Woods
3514881e14 Merge pull request #611 from aidantwoods/enhancement/paragraph-block-semantics
Paragraph block semantics
2018-04-09 16:30:33 +01:00
Aidan Woods
043c55e4c6 Give paragraph block semantics for overloading 2018-04-09 15:12:17 +01:00
Aidan Woods
e4cd13350b Remove setLiteralBreaks 2018-04-09 15:11:45 +01:00
Aidan Woods
ae8067e862 Swap undefined type for type === 'Paragraph' for ease of reading
The way in which we use this assumes that it is a paragraph, for example
appending text into the handler argument — so there is no loss of
generality here, we're simply being explicit.
2018-04-09 14:48:48 +01:00
Aidan Woods
5353ebb524 Avoid needing two arrays
We only need to collect elements, we can discard finished blocks
2018-04-09 14:48:39 +01:00
Aidan Woods
39df7d4f8e Swap 'hidden' blocks for empty elements 2018-04-09 14:46:24 +01:00
Aidan Woods
50f15add44 Merge pull request #610 from aidantwoods/fix/lost-line-breaks
Fix lost line breaks
2018-04-09 14:19:38 +01:00
Aidan Woods
3f5b0ee781 Count number of interrupts 2018-04-09 14:13:10 +01:00
Aidan Woods
9a021b2130 Add failing test cases 2018-04-09 14:11:49 +01:00
Aidan Woods
43d25a74fe Fix function name 2018-04-08 18:40:50 +01:00
Aidan Woods
1d68e5506c Merge pull request #608 from aidantwoods/fix/recursion
Add seperate depth-first function instead of replacing recursive method
2018-04-08 18:02:17 +01:00
Aidan Woods
86940be224 Use mutating loop instead of creating new array 2018-04-08 17:49:36 +01:00
Aidan Woods
cdaf86b039 Add seperate depth-first function instead of replacing recursive method 2018-04-08 17:39:24 +01:00
Aidan Woods
1d65fb858a Restore file permission to that of 1.7.1 2018-04-08 14:30:23 +01:00
19 changed files with 398 additions and 320 deletions

37
.github/workflows/unit-tests.yaml vendored Normal file
View File

@@ -0,0 +1,37 @@
on:
- push
- pull_request
jobs:
phpunit:
runs-on: ubuntu-latest
strategy:
matrix:
php:
- '7.1'
- '7.2'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
- '8.2'
- '8.3'
- '8.4'
steps:
- name: Checkout the source code
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '${{ matrix.php }}'
- name: Install dependencies
run: composer install
- name: Run tests
run: |
vendor/bin/phpunit
vendor/bin/phpunit test/CommonMarkTestWeak.php || true

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
composer.lock
vendor/
.phpunit.result.cache

View File

@@ -1,30 +0,0 @@
language: php
dist: trusty
sudo: false
matrix:
include:
- php: 5.3
dist: precise
- php: 5.4
- php: 5.5
- php: 5.6
- php: 7.0
- php: 7.1
- php: 7.2
- php: nightly
- php: hhvm
- php: hhvm-nightly
fast_finish: true
allow_failures:
- php: nightly
- php: hhvm-nightly
install:
- composer install --prefer-dist --no-interaction --no-progress
script:
- vendor/bin/phpunit
- vendor/bin/phpunit test/CommonMarkTestWeak.php || true
- '[ -z "$TRAVIS_TAG" ] || [ "$TRAVIS_TAG" == "$(php -r "require(\"Parsedown.php\"); echo Parsedown::version;")" ]'

479
Parsedown.php Executable file → Normal file
View File

@@ -17,7 +17,7 @@ class Parsedown
{
# ~
const version = '1.8.0-beta-1';
const version = '1.8.0';
# ~
@@ -65,15 +65,6 @@ class Parsedown
protected $breaksEnabled;
function setLiteralBreaks($literalBreaks)
{
$this->literalBreaks = $literalBreaks;
return $this;
}
protected $literalBreaks;
function setMarkupEscaped($markupEscaped)
{
$this->markupEscaped = $markupEscaped;
@@ -116,6 +107,7 @@ class Parsedown
'ftp://',
'ftps://',
'mailto:',
'tel:',
'data:image/png;base64,',
'data:image/gif;base64,',
'data:image/jpeg;base64,',
@@ -174,43 +166,34 @@ class Parsedown
protected function linesElements(array $lines)
{
$Elements = array();
$CurrentBlock = null;
foreach ($lines as $line)
{
if ( ! $this->literalBreaks and chop($line) === '')
if (chop($line) === '')
{
if (isset($CurrentBlock))
{
$CurrentBlock['interrupted'] = true;
$CurrentBlock['interrupted'] = (isset($CurrentBlock['interrupted'])
? $CurrentBlock['interrupted'] + 1 : 1
);
}
continue;
}
if (strpos($line, "\t") !== false)
while (($beforeTab = strstr($line, "\t", true)) !== false)
{
$parts = explode("\t", $line);
$shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4;
$line = $parts[0];
unset($parts[0]);
foreach ($parts as $part)
{
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
$line .= str_repeat(' ', $shortage);
$line .= $part;
}
$line = $beforeTab
. str_repeat(' ', $shortage)
. substr($line, strlen($beforeTab) + 1)
;
}
$indent = 0;
while (isset($line[$indent]) and $line[$indent] === ' ')
{
$indent ++;
}
$indent = strspn($line, ' ');
$text = $indent > 0 ? substr($line, $indent) : $line;
@@ -222,7 +205,8 @@ class Parsedown
if (isset($CurrentBlock['continuable']))
{
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
$methodName = 'block' . $CurrentBlock['type'] . 'Continue';
$Block = $this->$methodName($Line, $CurrentBlock);
if (isset($Block))
{
@@ -234,22 +218,15 @@ class Parsedown
{
if ($this->isBlockCompletable($CurrentBlock['type']))
{
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
$methodName = 'block' . $CurrentBlock['type'] . 'Complete';
$CurrentBlock = $this->$methodName($CurrentBlock);
}
}
}
# ~
if (isset($text[0]))
{
$marker = $text[0];
}
elseif ($this->literalBreaks)
{
$marker = '\n';
$text = ' ';
}
$marker = $text[0];
# ~
@@ -268,7 +245,7 @@ class Parsedown
foreach ($blockTypes as $blockType)
{
$Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
$Block = $this->{"block$blockType"}($Line, $CurrentBlock);
if (isset($Block))
{
@@ -276,7 +253,10 @@ class Parsedown
if ( ! isset($Block['identified']))
{
$Blocks []= $CurrentBlock;
if (isset($CurrentBlock))
{
$Elements[] = $this->extractElement($CurrentBlock);
}
$Block['identified'] = true;
}
@@ -294,17 +274,21 @@ class Parsedown
# ~
if (
isset($CurrentBlock)
and isset($CurrentBlock['element']['name'])
and $CurrentBlock['element']['name'] === 'p'
and ! isset($CurrentBlock['interrupted'])
) {
$CurrentBlock['element']['handler']['argument'] .= "\n".$text;
if (isset($CurrentBlock) and $CurrentBlock['type'] === 'Paragraph')
{
$Block = $this->paragraphContinue($Line, $CurrentBlock);
}
if (isset($Block))
{
$CurrentBlock = $Block;
}
else
{
$Blocks []= $CurrentBlock;
if (isset($CurrentBlock))
{
$Elements[] = $this->extractElement($CurrentBlock);
}
$CurrentBlock = $this->paragraph($Line);
@@ -316,27 +300,15 @@ class Parsedown
if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
{
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
$methodName = 'block' . $CurrentBlock['type'] . 'Complete';
$CurrentBlock = $this->$methodName($CurrentBlock);
}
# ~
$Blocks []= $CurrentBlock;
unset($Blocks[0]);
# ~
$Elements = array();
foreach ($Blocks as $Block)
if (isset($CurrentBlock))
{
if (isset($Block['hidden']))
{
continue;
}
$Elements[] = $Block['element'];
$Elements[] = $this->extractElement($CurrentBlock);
}
# ~
@@ -344,14 +316,31 @@ class Parsedown
return $Elements;
}
protected function extractElement(array $Component)
{
if ( ! isset($Component['element']))
{
if (isset($Component['markup']))
{
$Component['element'] = array('rawHtml' => $Component['markup']);
}
elseif (isset($Component['hidden']))
{
$Component['element'] = array();
}
}
return $Component['element'];
}
protected function isBlockContinuable($Type)
{
return method_exists($this, 'block'.$Type.'Continue');
return method_exists($this, 'block' . $Type . 'Continue');
}
protected function isBlockCompletable($Type)
{
return method_exists($this, 'block'.$Type.'Complete');
return method_exists($this, 'block' . $Type . 'Complete');
}
#
@@ -359,7 +348,7 @@ class Parsedown
protected function blockCode($Line, $Block = null)
{
if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
if (isset($Block) and $Block['type'] === 'Paragraph' and ! isset($Block['interrupted']))
{
return;
}
@@ -388,7 +377,7 @@ class Parsedown
{
if (isset($Block['interrupted']))
{
$Block['element']['element']['text'] .= "\n";
$Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);
unset($Block['interrupted']);
}
@@ -405,10 +394,6 @@ class Parsedown
protected function blockCodeComplete($Block)
{
$text = $Block['element']['element']['text'];
$Block['element']['element']['text'] = $text;
return $Block;
}
@@ -462,33 +447,56 @@ class Parsedown
protected function blockFencedCode($Line)
{
if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches))
$marker = $Line['text'][0];
$openerLength = strspn($Line['text'], $marker);
if ($openerLength < 3)
{
$Element = array(
'name' => 'code',
'text' => '',
);
if (isset($matches[2]))
{
$class = 'language-'.$matches[2];
$Element['attributes'] = array(
'class' => $class,
);
}
$Block = array(
'char' => $Line['text'][0],
'openerLength' => mb_strlen($matches[1]),
'element' => array(
'name' => 'pre',
'element' => $Element,
),
);
return $Block;
return;
}
$infostring = trim(substr($Line['text'], $openerLength), "\t ");
if (strpos($infostring, '`') !== false)
{
return;
}
$Element = array(
'name' => 'code',
'text' => '',
);
if ($infostring !== '')
{
/**
* https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
* Every HTML element may have a class attribute specified.
* The attribute, if specified, must have a value that is a set
* of space-separated tokens representing the various classes
* that the element belongs to.
* [...]
* The space characters, for the purposes of this specification,
* are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
* U+000A LINE FEED (LF), U+000C FORM FEED (FF), and
* U+000D CARRIAGE RETURN (CR).
*/
$language = substr($infostring, 0, strcspn($infostring, " \t\n\f\r"));
$Element['attributes'] = array('class' => "language-$language");
}
$Block = array(
'char' => $marker,
'openerLength' => $openerLength,
'element' => array(
'name' => 'pre',
'element' => $Element,
),
);
return $Block;
}
protected function blockFencedCodeContinue($Line, $Block)
@@ -500,14 +508,13 @@ class Parsedown
if (isset($Block['interrupted']))
{
$Block['element']['element']['text'] .= "\n";
$Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']);
unset($Block['interrupted']);
}
if (
preg_match('/^(['.preg_quote($Block['char']).']{3,})[ ]*$/', $Line['text'], $matches)
and mb_strlen($matches[1]) >= $Block['openerLength']
if (($len = strspn($Line['text'], $Block['char'])) >= $Block['openerLength']
and chop(substr($Line['text'], $len), ' ') === ''
) {
$Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1);
@@ -516,17 +523,13 @@ class Parsedown
return $Block;
}
$Block['element']['element']['text'] .= "\n".$Line['body'];
$Block['element']['element']['text'] .= "\n" . $Line['body'];
return $Block;
}
protected function blockFencedCodeComplete($Block)
{
$text = $Block['element']['element']['text'];
$Block['element']['element']['text'] = $text;
return $Block;
}
@@ -535,12 +538,7 @@ class Parsedown
protected function blockHeader($Line)
{
$level = 1;
while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
{
$level ++;
}
$level = strspn($Line['text'], '#');
if ($level > 6)
{
@@ -558,7 +556,7 @@ class Parsedown
$Block = array(
'element' => array(
'name' => 'h' . min(6, $level),
'name' => 'h' . $level,
'handler' => array(
'function' => 'lineElements',
'argument' => $text,
@@ -573,11 +571,11 @@ class Parsedown
#
# List
protected function blockList($Line, array $CurrentBlock = null)
protected function blockList($Line, ?array $CurrentBlock = null)
{
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}[.\)]');
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]');
if (preg_match('/^('.$pattern.'([ ]+|$))(.*)/', $Line['text'], $matches))
if (preg_match('/^('.$pattern.'([ ]++|$))(.*+)/', $Line['text'], $matches))
{
$contentIndent = strlen($matches[2]);
@@ -592,19 +590,22 @@ class Parsedown
$matches[1] .= ' ';
}
$markerWithoutWhitespace = strstr($matches[1], ' ', true);
$Block = array(
'indent' => $Line['indent'],
'pattern' => $pattern,
'data' => array(
'type' => $name,
'marker' => $matches[1],
'markerType' => ($name === 'ul' ? strstr($matches[1], ' ', true) : substr(strstr($matches[1], ' ', true), -1)),
'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : substr($markerWithoutWhitespace, -1)),
),
'element' => array(
'name' => $name,
'elements' => array(),
),
);
$Block['data']['markerTypeRegex'] = preg_quote($Block['data']['markerType'], '/');
if ($name === 'ol')
{
@@ -614,7 +615,7 @@ class Parsedown
{
if (
isset($CurrentBlock)
and ! isset($CurrentBlock['type'])
and $CurrentBlock['type'] === 'Paragraph'
and ! isset($CurrentBlock['interrupted'])
) {
return;
@@ -652,10 +653,10 @@ class Parsedown
and (
(
$Block['data']['type'] === 'ol'
and preg_match('/^[0-9]+'.preg_quote($Block['data']['markerType']).'(?:[ ]+(.*)|$)/', $Line['text'], $matches)
and preg_match('/^[0-9]++'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
) or (
$Block['data']['type'] === 'ul'
and preg_match('/^'.preg_quote($Block['data']['markerType']).'(?:[ ]+(.*)|$)/', $Line['text'], $matches)
and preg_match('/^'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches)
)
)
) {
@@ -717,7 +718,7 @@ class Parsedown
if ( ! isset($Block['interrupted']))
{
$text = preg_replace('/^[ ]{0,'.$requiredIndent.'}/', '', $Line['body']);
$text = preg_replace('/^[ ]{0,'.$requiredIndent.'}+/', '', $Line['body']);
$Block['li']['handler']['argument'] []= $text;
@@ -746,7 +747,7 @@ class Parsedown
protected function blockQuote($Line)
{
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
{
$Block = array(
'element' => array(
@@ -770,7 +771,7 @@ class Parsedown
return;
}
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches))
{
$Block['element']['handler']['argument'] []= $matches[1];
@@ -790,7 +791,9 @@ class Parsedown
protected function blockRule($Line)
{
if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
$marker = $Line['text'][0];
if (substr_count($Line['text'], $marker) >= 3 and chop($Line['text'], " $marker") === '')
{
$Block = array(
'element' => array(
@@ -805,17 +808,15 @@ class Parsedown
#
# Setext
protected function blockSetextHeader($Line, array $Block = null)
protected function blockSetextHeader($Line, ?array $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
{
return;
}
if (
chop(chop($Line['text'], ' '), $Line['text'][0]) === ''
and $Line['indent'] < 4
) {
if ($Line['indent'] < 4 and chop(chop($Line['text'], ' '), $Line['text'][0]) === '')
{
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
return $Block;
@@ -832,7 +833,7 @@ class Parsedown
return;
}
if (preg_match('/^<[\/]?+(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
if (preg_match('/^<[\/]?+(\w*)(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+(\/)?>/', $Line['text'], $matches))
{
$element = strtolower($matches[1]);
@@ -860,7 +861,7 @@ class Parsedown
return;
}
$Block['element']['rawHtml'] .= "\n".$Line['body'];
$Block['element']['rawHtml'] .= "\n" . $Line['body'];
return $Block;
}
@@ -870,24 +871,20 @@ class Parsedown
protected function blockReference($Line)
{
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
{
if (strpos($Line['text'], ']') !== false
and preg_match('/^\[(.+?)\]:[ ]*+<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches)
) {
$id = strtolower($matches[1]);
$Data = array(
'url' => $matches[2],
'title' => null,
'title' => isset($matches[3]) ? $matches[3] : null,
);
if (isset($matches[3]))
{
$Data['title'] = $matches[3];
}
$this->DefinitionData['Reference'][$id] = $Data;
$Block = array(
'hidden' => true,
'element' => array(),
);
return $Block;
@@ -897,9 +894,9 @@ class Parsedown
#
# Table
protected function blockTable($Line, array $Block = null)
protected function blockTable($Line, ?array $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted']))
{
return;
}
@@ -908,6 +905,7 @@ class Parsedown
strpos($Block['element']['handler']['argument'], '|') === false
and strpos($Line['text'], '|') === false
and strpos($Line['text'], ':') === false
or strpos($Block['element']['handler']['argument'], "\n") !== false
) {
return;
}
@@ -984,7 +982,7 @@ class Parsedown
$alignment = $alignments[$index];
$HeaderElement['attributes'] = array(
'style' => 'text-align: '.$alignment.';',
'style' => "text-align: $alignment;",
);
}
@@ -1035,7 +1033,7 @@ class Parsedown
$row = trim($row);
$row = trim($row, '|');
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches);
$cells = array_slice($matches[0], 0, count($Block['alignments']));
@@ -1055,7 +1053,7 @@ class Parsedown
if (isset($Block['alignments'][$index]))
{
$Element['attributes'] = array(
'style' => 'text-align: '.$Block['alignments'][$index].';',
'style' => 'text-align: ' . $Block['alignments'][$index] . ';',
);
}
@@ -1079,16 +1077,27 @@ class Parsedown
protected function paragraph($Line)
{
$Block = array(
return array(
'type' => 'Paragraph',
'element' => array(
'name' => 'p',
'handler' => array(
'function' => 'lineElements',
'argument' => $Line['text'],
'destination' => 'elements',
)
),
),
);
}
protected function paragraphContinue($Line, array $Block)
{
if (isset($Block['interrupted']))
{
return;
}
$Block['element']['handler']['argument'] .= "\n".$Line['text'];
return $Block;
}
@@ -1118,22 +1127,30 @@ class Parsedown
# ~
#
public function line($text, $nonNestables=array())
public function line($text, $nonNestables = array())
{
return $this->elements($this->lineElements($text, $nonNestables));
}
protected function lineElements($text, $nonNestables = array())
{
# standardize line breaks
$text = str_replace(array("\r\n", "\r"), "\n", $text);
$Elements = array();
$nonNestables = (empty($nonNestables)
? array()
: array_combine($nonNestables, $nonNestables)
);
# $excerpt is based on the first occurrence of a marker
while ($excerpt = strpbrk($text, $this->inlineMarkerList))
{
$marker = $excerpt[0];
$markerPosition = strpos($text, $marker);
$markerPosition = strlen($text) - strlen($excerpt);
$Excerpt = array('text' => $excerpt, 'context' => $text);
@@ -1141,12 +1158,12 @@ class Parsedown
{
# check to see if the current inline type is nestable in the current context
if ( ! empty($nonNestables) and in_array($inlineType, $nonNestables))
if (isset($nonNestables[$inlineType]))
{
continue;
}
$Inline = $this->{'inline'.$inlineType}($Excerpt);
$Inline = $this->{"inline$inlineType"}($Excerpt);
if ( ! isset($Inline))
{
@@ -1169,10 +1186,11 @@ class Parsedown
# cause the new element to 'inherit' our non nestables
foreach ($nonNestables as $non_nestable)
{
$Inline['element']['nonNestables'][] = $non_nestable;
}
$Inline['element']['nonNestables'] = isset($Inline['element']['nonNestables'])
? array_merge($Inline['element']['nonNestables'], $nonNestables)
: $nonNestables
;
# the text that comes before the inline
$unmarkedText = substr($text, 0, $Inline['position']);
@@ -1182,7 +1200,7 @@ class Parsedown
$Elements[] = $InlineText['element'];
# compile the inline
$Elements[] = $Inline['element'];
$Elements[] = $this->extractElement($Inline);
# remove the examined text
$text = substr($text, $Inline['position'] + $Inline['extent']);
@@ -1203,14 +1221,13 @@ class Parsedown
$InlineText = $this->inlineText($text);
$Elements[] = $InlineText['element'];
$Elements = array_map(
function ($Element) {
$Element['autobreak'] = isset($Element['autobreak'])
? $Element['autobreak'] : false;
return $Element;
},
$Elements
);
foreach ($Elements as &$Element)
{
if ( ! isset($Element['autobreak']))
{
$Element['autobreak'] = false;
}
}
return $Elements;
}
@@ -1223,33 +1240,17 @@ class Parsedown
{
$Inline = array(
'extent' => strlen($text),
'element' => array(
'elements' => array(),
),
'element' => array(),
);
if ($this->breaksEnabled)
{
$Inline['element']['elements'] = self::pregReplaceElements(
'/[ ]*\n/',
array(
array('name' => 'br'),
array('text' => "\n"),
),
$text
);
}
else
{
$Inline['element']['elements'] = self::pregReplaceElements(
'/(?:[ ][ ]+|[ ]*\\\\)\n/',
array(
array('name' => 'br'),
array('text' => "\n"),
),
$text
);
}
$Inline['element']['elements'] = self::pregReplaceElements(
$this->breaksEnabled ? '/[ ]*+\n/' : '/(?:[ ]*+\\\\|[ ]{2,}+)\n/',
array(
array('name' => 'br'),
array('text' => "\n"),
),
$text
);
return $Inline;
}
@@ -1258,10 +1259,10 @@ class Parsedown
{
$marker = $Excerpt['text'][0];
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
if (preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(?<!['.$marker.'])\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
{
$text = $matches[2];
$text = preg_replace("/[ ]*\n/", ' ', $text);
$text = preg_replace('/[ ]*+\n/', ' ', $text);
return array(
'extent' => strlen($matches[0]),
@@ -1287,7 +1288,7 @@ class Parsedown
if ( ! isset($matches[2]))
{
$url = 'mailto:' . $url;
$url = "mailto:$url";
}
return array(
@@ -1417,7 +1418,7 @@ class Parsedown
return;
}
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches))
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches))
{
$Element['attributes']['href'] = $matches[1];
@@ -1466,7 +1467,7 @@ class Parsedown
return;
}
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches))
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches))
{
return array(
'element' => array('rawHtml' => $matches[0]),
@@ -1474,7 +1475,7 @@ class Parsedown
);
}
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?+[^-])*-->/s', $Excerpt['text'], $matches))
{
return array(
'element' => array('rawHtml' => $matches[0]),
@@ -1482,7 +1483,7 @@ class Parsedown
);
}
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches))
{
return array(
'element' => array('rawHtml' => $matches[0]),
@@ -1493,10 +1494,11 @@ class Parsedown
protected function inlineSpecialCharacter($Excerpt)
{
if (preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches))
{
if (substr($Excerpt['text'], 1, 1) !== ' ' and strpos($Excerpt['text'], ';') !== false
and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches)
) {
return array(
'element' => array('rawHtml' => '&'.$matches[1].';'),
'element' => array('rawHtml' => '&' . $matches[1] . ';'),
'extent' => strlen($matches[0]),
);
}
@@ -1534,8 +1536,9 @@ class Parsedown
return;
}
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
if (strpos($Excerpt['context'], 'http') !== false
and preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)
) {
$url = $matches[0][0];
$Inline = array(
@@ -1556,7 +1559,7 @@ class Parsedown
protected function inlineUrlTag($Excerpt)
{
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches))
{
$url = $matches[1];
@@ -1598,6 +1601,7 @@ class Parsedown
{
$function = $Element['handler'];
$argument = $Element['text'];
unset($Element['text']);
$destination = 'rawHtml';
}
else
@@ -1613,9 +1617,9 @@ class Parsedown
{
$Element = $this->handle($Element);
}
}
unset($Element['handler']);
unset($Element['handler']);
}
return $Element;
}
@@ -1632,6 +1636,8 @@ class Parsedown
protected function elementApplyRecursive($closure, array $Element)
{
$Element = call_user_func($closure, $Element);
if (isset($Element['elements']))
{
$Element['elements'] = $this->elementsApplyRecursive($closure, $Element['elements']);
@@ -1641,6 +1647,20 @@ class Parsedown
$Element['element'] = $this->elementApplyRecursive($closure, $Element['element']);
}
return $Element;
}
protected function elementApplyRecursiveDepthFirst($closure, array $Element)
{
if (isset($Element['elements']))
{
$Element['elements'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['elements']);
}
elseif (isset($Element['element']))
{
$Element['element'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['element']);
}
$Element = call_user_func($closure, $Element);
return $Element;
@@ -1648,14 +1668,22 @@ class Parsedown
protected function elementsApplyRecursive($closure, array $Elements)
{
$newElements = array();
foreach ($Elements as $Element)
foreach ($Elements as &$Element)
{
$newElements[] = $this->elementApplyRecursive($closure, $Element);
$Element = $this->elementApplyRecursive($closure, $Element);
}
return $newElements;
return $Elements;
}
protected function elementsApplyRecursiveDepthFirst($closure, array $Elements)
{
foreach ($Elements as &$Element)
{
$Element = $this->elementApplyRecursiveDepthFirst($closure, $Element);
}
return $Elements;
}
protected function element(array $Element)
@@ -1674,7 +1702,7 @@ class Parsedown
if ($hasName)
{
$markup .= '<'.$Element['name'];
$markup .= '<' . $Element['name'];
if (isset($Element['attributes']))
{
@@ -1685,7 +1713,7 @@ class Parsedown
continue;
}
$markup .= ' '.$name.'="'.self::escape($value).'"';
$markup .= " $name=\"".self::escape($value).'"';
}
}
}
@@ -1732,7 +1760,7 @@ class Parsedown
}
}
$markup .= $hasName ? '</'.$Element['name'].'>' : '';
$markup .= $hasName ? '</' . $Element['name'] . '>' : '';
}
elseif ($hasName)
{
@@ -1750,8 +1778,13 @@ class Parsedown
foreach ($Elements as $Element)
{
$autoBreakNext = (isset($Element['autobreak']) && $Element['autobreak']
|| ! isset($Element['autobreak']) && isset($Element['name'])
if (empty($Element))
{
continue;
}
$autoBreakNext = (isset($Element['autobreak'])
? $Element['autobreak'] : isset($Element['name'])
);
// (autobreak === false) covers both sides of an element
$autoBreak = !$autoBreak ? $autoBreak : $autoBreakNext;
@@ -1928,12 +1961,12 @@ class Parsedown
# Read-Only
protected $specialCharacters = array(
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', '~'
);
protected $StrongRegex = array(
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
'_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*+[*])+?)[*]{2}(?![*])/s',
'_' => '/^__((?:\\\\_|[^_]|_[^_]*+_)+?)__(?!_)/us',
);
protected $EmRegex = array(
@@ -1941,7 +1974,7 @@ class Parsedown
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
);
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+';
protected $voidElements = array(
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',

View File

@@ -1,82 +1,79 @@
> I also make [Caret](https://caret.io?ref=parsedown) - a Markdown editor for Mac and PC.
# Parsedown
## Parsedown
[![Total Downloads](https://poser.pugx.org/erusev/parsedown/d/total.svg)](https://packagist.org/packages/erusev/parsedown)
[![Version](https://poser.pugx.org/erusev/parsedown/v/stable.svg)](https://packagist.org/packages/erusev/parsedown)
[![License](https://poser.pugx.org/erusev/parsedown/license.svg)](https://packagist.org/packages/erusev/parsedown)
[![Build Status](https://img.shields.io/travis/erusev/parsedown/master.svg?style=flat-square)](https://travis-ci.org/erusev/parsedown)
<!--[![Total Downloads](http://img.shields.io/packagist/dt/erusev/parsedown.svg?style=flat-square)](https://packagist.org/packages/erusev/parsedown)-->
Better Markdown Parser in PHP — <a href="https://parsedown.org/demo">demo</a>
Better Markdown Parser in PHP
## Features
[Demo](http://parsedown.org/demo) |
[Benchmarks](http://parsedown.org/speed) |
[Tests](http://parsedown.org/tests/) |
[Documentation](https://github.com/erusev/parsedown/wiki/)
- One file
- No dependencies
- [Super fast](http://parsedown.org/speed)
- Extensible
- [GitHub flavored](https://github.github.com/gfm)
- [Tested](http://parsedown.org/tests/) in 5.3 to 7.3
- [Markdown Extra extension](https://github.com/erusev/parsedown-extra)
### Features
## Installation
* One File
* No Dependencies
* Super Fast
* Extensible
* [GitHub flavored](https://help.github.com/articles/github-flavored-markdown)
* Tested in 5.3 to 7.2 and in HHVM
* [Markdown Extra extension](https://github.com/erusev/parsedown-extra)
Install the [composer package]:
### Installation
#### Composer
Install the [composer package] by running the following command:
```sh
composer require erusev/parsedown
```
composer require erusev/parsedown
#### Manual
1. Download the "Source code" from the [latest release]
2. Include `Parsedown.php`
Or download the [latest release] and include `Parsedown.php`
[composer package]: https://packagist.org/packages/erusev/parsedown "The Parsedown package on packagist.org"
[latest release]: https://github.com/erusev/parsedown/releases/latest "The latest release of Parsedown"
### Example
## Example
``` php
```php
$Parsedown = new Parsedown();
echo $Parsedown->text('Hello _Parsedown_!'); # prints: <p>Hello <em>Parsedown</em>!</p>
// you can also parse inline markdown only
```
You can also parse inline markdown only:
```php
echo $Parsedown->line('Hello _Parsedown_!'); # prints: Hello <em>Parsedown</em>!
```
More examples in [the wiki](https://github.com/erusev/parsedown/wiki/) and in [this video tutorial](http://youtu.be/wYZBY8DEikI).
### Security
## Security
Parsedown is capable of escaping user-input within the HTML that it generates. Additionally Parsedown will apply sanitisation to additional scripting vectors (such as scripting link destinations) that are introduced by the markdown syntax itself.
To tell Parsedown that it is processing untrusted user-input, use the following:
```php
$parsedown = new Parsedown;
$parsedown->setSafeMode(true);
$Parsedown->setSafeMode(true);
```
If instead, you wish to allow HTML within untrusted user-input, but still want output to be free from XSS it is recommended that you make use of a HTML sanitiser that allows HTML tags to be whitelisted, like [HTML Purifier](http://htmlpurifier.org/).
In both cases you should strongly consider employing defence-in-depth measures, like [deploying a Content-Security-Policy](https://scotthelme.co.uk/content-security-policy-an-introduction/) (a browser security feature) so that your page is likely to be safe even if an attacker finds a vulnerability in one of the first lines of defence above.
#### Security of Parsedown Extensions
Safe mode does not necessarily yield safe results when using extensions to Parsedown. Extensions should be evaluated on their own to determine their specific safety against XSS.
### Escaping HTML
> ⚠️  **WARNING:** This method isn't safe from XSS!
## Escaping HTML
> WARNING: This method is not safe from XSS!
If you wish to escape HTML in trusted input, you can use the following:
If you wish to escape HTML **in trusted input**, you can use the following:
```php
$parsedown = new Parsedown;
$parsedown->setMarkupEscaped(true);
$Parsedown->setMarkupEscaped(true);
```
Beware that this still allows users to insert unsafe scripting vectors, such as links like `[xss](javascript:alert%281%29)`.
Beware that this still allows users to insert unsafe scripting vectors, ex: `[xss](javascript:alert%281%29)`.
### Questions
## Questions
**How does Parsedown work?**
@@ -90,8 +87,12 @@ It passes most of the CommonMark tests. Most of the tests that don't pass deal w
**Who uses it?**
[Laravel Framework](https://laravel.com/), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [October CMS](http://octobercms.com/), [Pico CMS](http://picocms.org), [Statamic CMS](http://www.statamic.com/), [phpDocumentor](http://www.phpdoc.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Symfony demo](https://github.com/symfony/symfony-demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
[Laravel Framework](https://laravel.com/), [Bolt CMS](http://bolt.cm/), [Grav CMS](http://getgrav.org/), [Herbie CMS](http://www.getherbie.org/), [Kirby CMS](http://getkirby.com/), [October CMS](http://octobercms.com/), [Pico CMS](http://picocms.org), [Statamic CMS](http://www.statamic.com/), [phpDocumentor](http://www.phpdoc.org/), [RaspberryPi.org](http://www.raspberrypi.org/), [Symfony Demo](https://github.com/symfony/demo) and [more](https://packagist.org/packages/erusev/parsedown/dependents).
**How can I help?**
Use it, star it, share it and if you feel generous, [donate](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=528P3NZQMP8N2).
**What else should I know?**
I also make [Nota](https://nota.md/) — a notes app designed for local Markdown files.

View File

@@ -13,11 +13,11 @@
}
],
"require": {
"php": ">=5.3.0",
"php": ">=7.1",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
"phpunit/phpunit": "^7.5|^8.5|^9.6"
},
"autoload": {
"psr-0": {"Parsedown": ""}

View File

@@ -1,17 +1,19 @@
<?php
use PHPUnit\Framework\TestCase;
/**
* Test Parsedown against the CommonMark spec
*
* @link http://commonmark.org/ CommonMark
*/
class CommonMarkTestStrict extends PHPUnit_Framework_TestCase
class CommonMarkTestStrict extends TestCase
{
const SPEC_URL = 'https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt';
protected $parsedown;
protected function setUp()
protected function setUp() : void
{
$this->parsedown = new TestParsedown();
$this->parsedown->setUrlsLinked(false);

View File

@@ -17,7 +17,7 @@ class CommonMarkTestWeak extends CommonMarkTestStrict
{
protected $textLevelElementRegex;
protected function setUp()
protected function setUp() : void
{
parent::setUp();

View File

@@ -52,7 +52,6 @@ class ParsedownTest extends TestCase
$this->Parsedown->setSafeMode(substr($test, 0, 3) === 'xss');
$this->Parsedown->setStrictMode(substr($test, 0, 6) === 'strict');
$this->Parsedown->setLiteralBreaks(substr($test, 0, 14) === 'literal_breaks');
$actualMarkup = $this->Parsedown->text($markdown);

View File

@@ -5,4 +5,9 @@ echo $message;</code></pre>
<hr />
<pre><code>&gt; not a quote
- not a list item
[not a reference]: http://foo.com</code></pre>
[not a reference]: http://foo.com</code></pre>
<hr />
<pre><code>foo
bar</code></pre>

View File

@@ -7,4 +7,11 @@
> not a quote
- not a list item
[not a reference]: http://foo.com
[not a reference]: http://foo.com
---
foo
bar

View File

@@ -11,4 +11,10 @@ echo "Hello World";
&lt;a href="http://auraphp.com" &gt;Aura Project&lt;/a&gt;</code></pre>
<pre><code>the following isn't quite enough to close
```
still a fenced code block</code></pre>
still a fenced code block</code></pre>
<pre><code>foo
bar</code></pre>
<pre><code class="language-php">&lt;?php
echo "Hello World";</code></pre>

View File

@@ -28,4 +28,16 @@ echo "Hello World";
the following isn't quite enough to close
```
still a fenced code block
````
````
```
foo
bar
```
```php some-class
<?php
echo "Hello World";
```

View File

@@ -1,6 +0,0 @@
<p>first line
<br />
<br />
<br />
<br />
sixth line</p>

View File

@@ -1,6 +0,0 @@
first line
sixth line

View File

@@ -66,4 +66,10 @@
<td>cell 2.1</td>
</tr>
</tbody>
</table>
</table>
<hr />
<p>Not a table, we haven't ended the paragraph:
header 1 | header 2
-------- | --------
cell 1.1 | cell 1.2
cell 2.1 | cell 2.2</p>

View File

@@ -22,4 +22,12 @@ cell 2.1
header 1
-------|
cell 1.1
cell 2.1
cell 2.1
---
Not a table, we haven't ended the paragraph:
header 1 | header 2
-------- | --------
cell 1.1 | cell 1.2
cell 2.1 | cell 2.2

View File

@@ -1,3 +1,4 @@
<p><del>strikethrough</del></p>
<p>here's <del>one</del> followed by <del>another one</del></p>
<p>~~ this ~~ is not one neither is ~this~</p>
<p>~~ this ~~ is not one neither is ~this~</p>
<p>escaped ~~this~~</p>

View File

@@ -2,4 +2,6 @@
here's ~~one~~ followed by ~~another one~~
~~ this ~~ is not one neither is ~this~
~~ this ~~ is not one neither is ~this~
escaped \~\~this\~\~