├── .gitignore ├── LICENSE ├── Parser.php ├── README.md ├── cli.php ├── composer.json ├── composer.lock └── test └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .*.swp 3 | .*.swo 4 | ._* 5 | .DS_Store 6 | /Debug/ 7 | /ImgCache/ 8 | /Backup_rar/ 9 | /Debug/ 10 | /debug/ 11 | /upload/ 12 | /avatar/ 13 | /.idea/ 14 | /.vagrant/ 15 | node_modules 16 | Vagrantfile 17 | *.orig 18 | *.aps 19 | *.APS 20 | *.chm 21 | *.exp 22 | *.pdb 23 | *.rar 24 | .smbdelete* 25 | *.sublime* 26 | .sass-cache 27 | config.rb 28 | config.codekit 29 | npm-debug.log 30 | *.log 31 | *.iml 32 | 33 | /vendor/ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2015, SegmentFault 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, this 13 | list of conditions and the following disclaimer in the documentation and/or 14 | other materials provided with the distribution. 15 | 16 | * Neither the name of schillmania.com nor the names of its contributors may be 17 | used to endorse or promote products derived from this software without 18 | specific prior written permission from schillmania.com. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Parser.php: -------------------------------------------------------------------------------- 1 | 10 | * @license BSD License 11 | */ 12 | class Parser 13 | { 14 | /** 15 | * _whiteList 16 | * 17 | * @var string 18 | */ 19 | private $_commonWhiteList = 'kbd|b|i|strong|em|sup|sub|br|code|del|a|hr|small'; 20 | 21 | /** 22 | * html tags 23 | * 24 | * @var string 25 | */ 26 | private $_blockHtmlTags = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption|svg|script|noscript'; 27 | 28 | /** 29 | * _specialWhiteList 30 | * 31 | * @var mixed 32 | * @access private 33 | */ 34 | private $_specialWhiteList = array( 35 | 'table' => 'table|tbody|thead|tfoot|tr|td|th' 36 | ); 37 | 38 | /** 39 | * _footnotes 40 | * 41 | * @var array 42 | */ 43 | private $_footnotes; 44 | 45 | /** 46 | * @var bool 47 | */ 48 | private $_html = false; 49 | 50 | /** 51 | * @var bool 52 | */ 53 | private $_line = false; 54 | 55 | /** 56 | * @var array 57 | */ 58 | private $blockParsers = array( 59 | array('code', 10), 60 | array('shtml', 20), 61 | array('pre', 30), 62 | array('ahtml', 40), 63 | array('shr', 50), 64 | array('list', 60), 65 | array('math', 70), 66 | array('html', 80), 67 | array('footnote', 90), 68 | array('definition', 100), 69 | array('quote', 110), 70 | array('table', 120), 71 | array('sh', 130), 72 | array('mh', 140), 73 | array('dhr', 150), 74 | array('default', 9999) 75 | ); 76 | 77 | /** 78 | * _blocks 79 | * 80 | * @var array 81 | */ 82 | private $_blocks; 83 | 84 | /** 85 | * _current 86 | * 87 | * @var string 88 | */ 89 | private $_current; 90 | 91 | /** 92 | * _pos 93 | * 94 | * @var int 95 | */ 96 | private $_pos; 97 | 98 | /** 99 | * _definitions 100 | * 101 | * @var array 102 | */ 103 | private $_definitions; 104 | 105 | /** 106 | * @var array 107 | */ 108 | private $_hooks = array(); 109 | 110 | /** 111 | * @var array 112 | */ 113 | private $_holders; 114 | 115 | /** 116 | * @var string 117 | */ 118 | private $_uniqid; 119 | 120 | /** 121 | * @var int 122 | */ 123 | private $_id; 124 | 125 | /** 126 | * @var array 127 | */ 128 | private $_parsers = array(); 129 | 130 | /** 131 | * makeHtml 132 | * 133 | * @param mixed $text 134 | * @return string 135 | */ 136 | public function makeHtml($text) 137 | { 138 | $this->_footnotes = array(); 139 | $this->_definitions = array(); 140 | $this->_holders = array(); 141 | $this->_uniqid = md5(uniqid()); 142 | $this->_id = 0; 143 | 144 | usort($this->blockParsers, function ($a, $b) { 145 | return $a[1] < $b[1] ? -1 : 1; 146 | }); 147 | 148 | foreach ($this->blockParsers as $parser) { 149 | list($name) = $parser; 150 | 151 | if (isset($parser[2])) { 152 | $this->_parsers[$name] = $parser[2]; 153 | } else { 154 | $this->_parsers[$name] = array($this, 'parseBlock' . ucfirst($name)); 155 | } 156 | } 157 | 158 | $text = $this->initText($text); 159 | $html = $this->parse($text); 160 | $html = $this->makeFootnotes($html); 161 | $html = $this->optimizeLines($html); 162 | 163 | return $this->call('makeHtml', $html); 164 | } 165 | 166 | /** 167 | * @param $html 168 | */ 169 | public function enableHtml($html = true) 170 | { 171 | $this->_html = $html; 172 | } 173 | 174 | /** 175 | * @param bool $line 176 | */ 177 | public function enableLine($line = true) 178 | { 179 | $this->_line = $line; 180 | } 181 | 182 | /** 183 | * @param $type 184 | * @param $callback 185 | */ 186 | public function hook($type, $callback) 187 | { 188 | $this->_hooks[$type][] = $callback; 189 | } 190 | 191 | /** 192 | * @param $str 193 | * @return string 194 | */ 195 | public function makeHolder($str) 196 | { 197 | $key = "\r" . $this->_uniqid . $this->_id . "\r"; 198 | $this->_id ++; 199 | $this->_holders[$key] = $str; 200 | 201 | return $key; 202 | } 203 | 204 | /** 205 | * @param $text 206 | * @return mixed 207 | */ 208 | private function initText($text) 209 | { 210 | $text = str_replace(array("\t", "\r"), array(' ', ''), $text); 211 | return $text; 212 | } 213 | 214 | /** 215 | * @param $html 216 | * @return string 217 | */ 218 | private function makeFootnotes($html) 219 | { 220 | if (count($this->_footnotes) > 0) { 221 | $html .= '
' . htmlspecialchars($matches[3]) . '
'
389 | );
390 | },
391 | $text
392 | );
393 |
394 | // mathjax
395 | $text = preg_replace_callback(
396 | "/(^|[^\\\])(\\$+)(.+?)\\2/",
397 | function ($matches) {
398 | return $matches[1] . $this->makeHolder(
399 | $matches[2] . htmlspecialchars($matches[3]) . $matches[2]
400 | );
401 | },
402 | $text
403 | );
404 |
405 | // escape
406 | $text = preg_replace_callback(
407 | "/\\\(.)/u",
408 | function ($matches) {
409 | $prefix = preg_match("/^[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]$/", $matches[1]) ? '' : '\\';
410 | $escaped = htmlspecialchars($matches[1]);
411 | $escaped = str_replace('$', '$', $escaped);
412 | return $this->makeHolder($prefix . $escaped);
413 | },
414 | $text
415 | );
416 |
417 | // link
418 | $text = preg_replace_callback(
419 | "/<(https?:\/\/.+|(?:mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+)>/i",
420 | function ($matches) {
421 | $url = $this->cleanUrl($matches[1]);
422 | $link = $this->call('parseLink', $url);
423 |
424 | return $this->makeHolder(
425 | "{$link}"
426 | );
427 | },
428 | $text
429 | );
430 |
431 | // encode unsafe tags
432 | $text = preg_replace_callback(
433 | "/<(\/?)([a-z0-9-]+)(\s+[^>]*)?>/i",
434 | function ($matches) use ($whiteList) {
435 | if ($this->_html || false !== stripos(
436 | '|' . $this->_commonWhiteList . '|' . $whiteList . '|', '|' . $matches[2] . '|'
437 | )) {
438 | return $this->makeHolder($matches[0]);
439 | } else {
440 | return $this->makeHolder(htmlspecialchars($matches[0]));
441 | }
442 | },
443 | $text
444 | );
445 |
446 | if ($this->_html) {
447 | $text = preg_replace_callback("//", function ($matches) {
448 | return $this->makeHolder($matches[0]);
449 | }, $text);
450 | }
451 |
452 | $text = str_replace(array('<', '>'), array('<', '>'), $text);
453 |
454 | // footnote
455 | $text = preg_replace_callback(
456 | "/\[\^((?:[^\]]|\\\\\]|\\\\\[)+?)\]/",
457 | function ($matches) {
458 | $id = array_search($matches[1], $this->_footnotes);
459 |
460 | if (false === $id) {
461 | $id = count($this->_footnotes) + 1;
462 | $this->_footnotes[$id] = $this->parseInline($matches[1], '', false);
463 | }
464 |
465 | return $this->makeHolder(
466 | "{$id}"
467 | );
468 | },
469 | $text
470 | );
471 |
472 | // image
473 | $text = preg_replace_callback(
474 | "/!\[((?:[^\]]|\\\\\]|\\\\\[)*?)\]\(((?:[^\)]|\\\\\)|\\\\\()+?)\)/",
475 | function ($matches) {
476 | $escaped = htmlspecialchars($this->escapeBracket($matches[1]));
477 | $url = $this->escapeBracket($matches[2]);
478 | list ($url, $title) = $this->cleanUrl($url, true);
479 | $title = empty($title)? $escaped : " title=\"{$title}\"";
480 |
481 | return $this->makeHolder(
482 | "'
1287 | . $str . '
';
1288 | }
1289 |
1290 | /**
1291 | * parsePre
1292 | *
1293 | * @param array $lines
1294 | * @param mixed $value
1295 | * @param int $start
1296 | * @return string
1297 | */
1298 | private function parsePre(array $lines, $value, $start)
1299 | {
1300 | foreach ($lines as &$line) {
1301 | $line = htmlspecialchars(substr($line, 4));
1302 | }
1303 |
1304 | $str = implode("\n", $this->markLines($lines, $start));
1305 | return preg_match("/^\s*$/", $str) ? '' : '' . $str . '
';
1306 | }
1307 |
1308 | /**
1309 | * parseAhtml
1310 | *
1311 | * @param array $lines
1312 | * @param mixed $value
1313 | * @param int $start
1314 | * @return string
1315 | */
1316 | private function parseAhtml(array $lines, $value, $start)
1317 | {
1318 | return trim(implode("\n", $this->markLines($lines, $start)));
1319 | }
1320 |
1321 | /**
1322 | * parseShtml
1323 | *
1324 | * @param array $lines
1325 | * @param mixed $value
1326 | * @param int $start
1327 | * @return string
1328 | */
1329 | private function parseShtml(array $lines, $value, $start)
1330 | {
1331 | return trim(implode("\n", $this->markLines(array_slice($lines, 1, -1), $start + 1)));
1332 | }
1333 |
1334 | /**
1335 | * parseMath
1336 | *
1337 | * @param array $lines
1338 | * @param mixed $value
1339 | * @param int $start
1340 | * @param int $end
1341 | * @return string
1342 | */
1343 | private function parseMath(array $lines, $value, $start, $end)
1344 | {
1345 | return '' . $this->markLine($start, $end) . htmlspecialchars(implode("\n", $lines)) . '
'; 1346 | } 1347 | 1348 | /** 1349 | * parseSh 1350 | * 1351 | * @param array $lines 1352 | * @param int $num 1353 | * @param int $start 1354 | * @param int $end 1355 | * @return string 1356 | */ 1357 | private function parseSh(array $lines, $num, $start, $end) 1358 | { 1359 | $line = $this->markLine($start, $end) . $this->parseInline(trim($lines[0], '# ')); 1360 | return preg_match("/^\s*$/", $line) ? '' : "' . $this->parse($str, true, $start) . ''; 1393 | } 1394 | 1395 | /** 1396 | * parseList 1397 | * 1398 | * @param array $lines 1399 | * @param mixed $value 1400 | * @param int $start 1401 | * @return string 1402 | */ 1403 | private function parseList(array $lines, $value, $start) 1404 | { 1405 | $html = ''; 1406 | list($space, $type, $tab) = $value; 1407 | $rows = array(); 1408 | $suffix = ''; 1409 | $last = 0; 1410 | 1411 | foreach ($lines as $key => $line) { 1412 | if (preg_match("/^(\s{" . $space . "})((?:[0-9]+\.?)|\-|\+|\*)(\s+)(.*)$/i", $line, $matches)) { 1413 | if ($type == 'ol' && $key == 0) { 1414 | $start = intval($matches[2]); 1415 | 1416 | if ($start != 1) { 1417 | $suffix = ' start="' . $start . '"'; 1418 | } 1419 | } 1420 | 1421 | $rows[] = [$matches[4]]; 1422 | $last = count($rows) - 1; 1423 | } else { 1424 | $rows[$last][] = preg_replace("/^\s{" . ($tab + $space) . "}/", '', $line); 1425 | } 1426 | } 1427 | 1428 | foreach ($rows as $row) { 1429 | $html .= "
";
1574 | }, $str);
1575 | $str = preg_replace("/\n/", "
", $str);
1576 |
1577 | return preg_match("/^\s*$/", $str) ? '' : ($inline ? $str : "
{$str}
"); 1578 | } 1579 | 1580 | /** 1581 | * parseFootnote 1582 | * 1583 | * @param array $lines 1584 | * @param array $value 1585 | * @return string 1586 | */ 1587 | private function parseFootnote(array $lines, array $value) 1588 | { 1589 | list($space, $note) = $value; 1590 | $index = array_search($note, $this->_footnotes); 1591 | 1592 | if (false !== $index) { 1593 | $lines[0] = preg_replace("/^\[\^((?:[^\]]|\\]|\\[)+?)\]:/", '', $lines[0]); 1594 | $this->_footnotes[$index] = $lines; 1595 | } 1596 | 1597 | return ''; 1598 | } 1599 | 1600 | /** 1601 | * parseDefine 1602 | * 1603 | * @return string 1604 | */ 1605 | private function parseDefinition() 1606 | { 1607 | return ''; 1608 | } 1609 | 1610 | /** 1611 | * parseHtml 1612 | * 1613 | * @param array $lines 1614 | * @param string $type 1615 | * @param int $start 1616 | * @return string 1617 | */ 1618 | private function parseHtml(array $lines, $type, $start) 1619 | { 1620 | foreach ($lines as &$line) { 1621 | $line = $this->parseInline($line, 1622 | isset($this->_specialWhiteList[$type]) ? $this->_specialWhiteList[$type] : ''); 1623 | } 1624 | 1625 | return implode("\n", $this->markLines($lines, $start)); 1626 | } 1627 | 1628 | /** 1629 | * @param $url 1630 | * @param bool $parseTitle 1631 | * 1632 | * @return mixed 1633 | */ 1634 | private function cleanUrl($url, $parseTitle = false) 1635 | { 1636 | $title = null; 1637 | $url = trim($url); 1638 | 1639 | if ($parseTitle) { 1640 | $pos = strpos($url, ' '); 1641 | 1642 | if ($pos !== false) { 1643 | $title = htmlspecialchars(trim(substr($url, $pos + 1), ' "\'')); 1644 | $url = substr($url, 0, $pos); 1645 | } 1646 | } 1647 | 1648 | $url = preg_replace("/[\"'<>\s]/", '', $url); 1649 | 1650 | if (preg_match("/^(mailto:)?[_a-z0-9-\.\+]+@[_\w-]+(?:\.[a-z]{2,})+$/i", $url, $matches)) { 1651 | if (empty($matches[1])) { 1652 | $url = 'mailto:' . $url; 1653 | } 1654 | } 1655 | 1656 | if (preg_match("/^\w+:/i", $url) && !preg_match("/^(https?|mailto):/i", $url)) { 1657 | return '#'; 1658 | } 1659 | 1660 | return $parseTitle ? [$url, $title] : $url; 1661 | } 1662 | 1663 | /** 1664 | * @param $str 1665 | * @return mixed 1666 | */ 1667 | private function escapeBracket($str) 1668 | { 1669 | return str_replace( 1670 | array('\[', '\]', '\(', '\)'), array('[', ']', '(', ')'), $str 1671 | ); 1672 | } 1673 | 1674 | /** 1675 | * startBlock 1676 | * 1677 | * @param mixed $type 1678 | * @param mixed $start 1679 | * @param mixed $value 1680 | * @return $this 1681 | */ 1682 | private function startBlock($type, $start, $value = NULL) 1683 | { 1684 | $this->_pos ++; 1685 | $this->_current = $type; 1686 | 1687 | $this->_blocks[$this->_pos] = array($type, $start, $start, $value); 1688 | 1689 | return $this; 1690 | } 1691 | 1692 | /** 1693 | * endBlock 1694 | * 1695 | * @return $this 1696 | */ 1697 | private function endBlock() 1698 | { 1699 | $this->_current = 'normal'; 1700 | return $this; 1701 | } 1702 | 1703 | /** 1704 | * isBlock 1705 | * 1706 | * @param mixed $type 1707 | * @param mixed $value 1708 | * @return bool 1709 | */ 1710 | private function isBlock($type, $value = NULL) 1711 | { 1712 | return $this->_current == $type 1713 | && (NULL === $value ? true : $this->_blocks[$this->_pos][3] == $value); 1714 | } 1715 | 1716 | /** 1717 | * getBlock 1718 | * 1719 | * @return array 1720 | */ 1721 | private function getBlock() 1722 | { 1723 | return isset($this->_blocks[$this->_pos]) ? $this->_blocks[$this->_pos] : NULL; 1724 | } 1725 | 1726 | /** 1727 | * setBlock 1728 | * 1729 | * @param mixed $to 1730 | * @param mixed $value 1731 | * @return $this 1732 | */ 1733 | private function setBlock($to = NULL, $value = NULL) 1734 | { 1735 | if (NULL !== $to) { 1736 | $this->_blocks[$this->_pos][2] = $to; 1737 | } 1738 | 1739 | if (NULL !== $value) { 1740 | $this->_blocks[$this->_pos][3] = $value; 1741 | } 1742 | 1743 | return $this; 1744 | } 1745 | 1746 | /** 1747 | * backBlock 1748 | * 1749 | * @param mixed $step 1750 | * @param mixed $type 1751 | * @param mixed $value 1752 | * @return $this 1753 | */ 1754 | private function backBlock($step, $type, $value = NULL) 1755 | { 1756 | if ($this->_pos < 0) { 1757 | return $this->startBlock($type, 0, $value); 1758 | } 1759 | 1760 | $last = $this->_blocks[$this->_pos][2]; 1761 | $this->_blocks[$this->_pos][2] = $last - $step; 1762 | 1763 | if ($this->_blocks[$this->_pos][1] <= $this->_blocks[$this->_pos][2]) { 1764 | $this->_pos ++; 1765 | } 1766 | 1767 | $this->_current = $type; 1768 | $this->_blocks[$this->_pos] = array( 1769 | $type, $last - $step + 1, $last, $value 1770 | ); 1771 | 1772 | return $this; 1773 | } 1774 | 1775 | /** 1776 | * @return $this 1777 | */ 1778 | private function combineBlock() 1779 | { 1780 | if ($this->_pos < 1) { 1781 | return $this; 1782 | } 1783 | 1784 | $prev = $this->_blocks[$this->_pos - 1]; 1785 | $current = $this->_blocks[$this->_pos]; 1786 | 1787 | $prev[2] = $current[2]; 1788 | $this->_blocks[$this->_pos - 1] = $prev; 1789 | $this->_current = $prev[0]; 1790 | unset($this->_blocks[$this->_pos]); 1791 | $this->_pos --; 1792 | 1793 | return $this; 1794 | } 1795 | } 1796 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 为何要写这样一个解析器 2 | ====================== 3 | 4 | Markdown已经面世许多年了,国内外许多大大小小的网站都在用它,但是它的解析器却依然混乱不堪。SegmentFault 是中国较大规模使用 Markdown 语法的网站,我们一直在使用一些开源类库,包括但不限于 5 | 6 | 1. [php-markdown](https://github.com/michelf/php-markdown) 7 | 2. [CommonMark for PHP](https://github.com/thephpleague/commonmark) 8 | 3. [Parsedown](https://github.com/erusev/parsedown) 9 | 10 | 他们都有或多或少的毛病,有的性能较差,有的代码比较业余,更多的情况是由于Markdown本身解析比较复杂,因此我们几乎无法去维护另外一个人写的代码。基于这个原因,我为 SegmentFault 专门编写了这么一个Markdown解析器。 11 | 12 | 使用方法 13 | -------- 14 | 15 | 与常规的解析类库没有任何区别 16 | 17 | ```php 18 | $parser = new HyperDown\Parser; 19 | $html = $parser->makeHtml($text); 20 | ``` 21 | 22 | 当前支持的语法 23 | -------------- 24 | 25 | - 标题 26 | - 列表(可递归) 27 | - 引用(可递归) 28 | - 缩进风格的代码块 29 | - Github风格的代码块 30 | - 各种行内文字加粗,斜体等效果 31 | - 链接,图片 32 | - 自动链接 33 | - 段内折行 34 | - 脚标 35 | - 分隔符 36 | - 表格 37 | - 图片和链接支持互相套用 38 | 39 | 浏览器中使用请参阅 [HyperDown.js](https://github.com/SegmentFault/HyperDown.js) 40 | -------------------------------------------------------------------------------- /cli.php: -------------------------------------------------------------------------------- 1 | enableHtml(true); 35 | } 36 | 37 | if ($line) { 38 | $parser->enableLine(true); 39 | } 40 | 41 | $buff = file_get_contents($file); 42 | echo $parser->makeHtml($buff); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "joyqi/hyper-down", 3 | "description": "A light weight markdown parser library", 4 | "keywords": ["markdown", "hyperdown", "segmentfault"], 5 | "license": "BSD-4-Clause", 6 | "authors": [ 7 | { 8 | "name": "joyqi", 9 | "email": "joyqi@segmentfault.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.1.0", 14 | "ext-mbstring": "*" 15 | }, 16 | "autoload": { 17 | "psr-4": { "HyperDown\\": "./" } 18 | }, 19 | "scripts": { 20 | "test": [ 21 | "php test/test.php" 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "666ba4403a5cfd7267a4b759137db9da", 8 | "content-hash": "04dfedcbd6db661d73db3733ed625e6e", 9 | "packages": [], 10 | "packages-dev": [ 11 | { 12 | "name": "doctrine/instantiator", 13 | "version": "1.0.5", 14 | "source": { 15 | "type": "git", 16 | "url": "https://github.com/doctrine/instantiator.git", 17 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 18 | }, 19 | "dist": { 20 | "type": "zip", 21 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 22 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 23 | "shasum": "" 24 | }, 25 | "require": { 26 | "php": ">=5.3,<8.0-DEV" 27 | }, 28 | "require-dev": { 29 | "athletic/athletic": "~0.1.8", 30 | "ext-pdo": "*", 31 | "ext-phar": "*", 32 | "phpunit/phpunit": "~4.0", 33 | "squizlabs/php_codesniffer": "~2.0" 34 | }, 35 | "type": "library", 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.0.x-dev" 39 | } 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 44 | } 45 | }, 46 | "notification-url": "https://packagist.org/downloads/", 47 | "license": [ 48 | "MIT" 49 | ], 50 | "authors": [ 51 | { 52 | "name": "Marco Pivetta", 53 | "email": "ocramius@gmail.com", 54 | "homepage": "http://ocramius.github.com/" 55 | } 56 | ], 57 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 58 | "homepage": "https://github.com/doctrine/instantiator", 59 | "keywords": [ 60 | "constructor", 61 | "instantiate" 62 | ], 63 | "time": "2015-06-14 21:17:01" 64 | }, 65 | { 66 | "name": "myclabs/deep-copy", 67 | "version": "1.5.0", 68 | "source": { 69 | "type": "git", 70 | "url": "https://github.com/myclabs/DeepCopy.git", 71 | "reference": "e3abefcd7f106677fd352cd7c187d6c969aa9ddc" 72 | }, 73 | "dist": { 74 | "type": "zip", 75 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e3abefcd7f106677fd352cd7c187d6c969aa9ddc", 76 | "reference": "e3abefcd7f106677fd352cd7c187d6c969aa9ddc", 77 | "shasum": "" 78 | }, 79 | "require": { 80 | "php": ">=5.4.0" 81 | }, 82 | "require-dev": { 83 | "doctrine/collections": "1.*", 84 | "phpunit/phpunit": "~4.1" 85 | }, 86 | "type": "library", 87 | "autoload": { 88 | "psr-4": { 89 | "DeepCopy\\": "src/DeepCopy/" 90 | } 91 | }, 92 | "notification-url": "https://packagist.org/downloads/", 93 | "license": [ 94 | "MIT" 95 | ], 96 | "description": "Create deep copies (clones) of your objects", 97 | "homepage": "https://github.com/myclabs/DeepCopy", 98 | "keywords": [ 99 | "clone", 100 | "copy", 101 | "duplicate", 102 | "object", 103 | "object graph" 104 | ], 105 | "time": "2015-11-07 22:20:37" 106 | }, 107 | { 108 | "name": "phpdocumentor/reflection-docblock", 109 | "version": "2.0.4", 110 | "source": { 111 | "type": "git", 112 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 113 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 114 | }, 115 | "dist": { 116 | "type": "zip", 117 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 118 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 119 | "shasum": "" 120 | }, 121 | "require": { 122 | "php": ">=5.3.3" 123 | }, 124 | "require-dev": { 125 | "phpunit/phpunit": "~4.0" 126 | }, 127 | "suggest": { 128 | "dflydev/markdown": "~1.0", 129 | "erusev/parsedown": "~1.0" 130 | }, 131 | "type": "library", 132 | "extra": { 133 | "branch-alias": { 134 | "dev-master": "2.0.x-dev" 135 | } 136 | }, 137 | "autoload": { 138 | "psr-0": { 139 | "phpDocumentor": [ 140 | "src/" 141 | ] 142 | } 143 | }, 144 | "notification-url": "https://packagist.org/downloads/", 145 | "license": [ 146 | "MIT" 147 | ], 148 | "authors": [ 149 | { 150 | "name": "Mike van Riel", 151 | "email": "mike.vanriel@naenius.com" 152 | } 153 | ], 154 | "time": "2015-02-03 12:10:50" 155 | }, 156 | { 157 | "name": "phpspec/prophecy", 158 | "version": "v1.6.0", 159 | "source": { 160 | "type": "git", 161 | "url": "https://github.com/phpspec/prophecy.git", 162 | "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972" 163 | }, 164 | "dist": { 165 | "type": "zip", 166 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972", 167 | "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972", 168 | "shasum": "" 169 | }, 170 | "require": { 171 | "doctrine/instantiator": "^1.0.2", 172 | "php": "^5.3|^7.0", 173 | "phpdocumentor/reflection-docblock": "~2.0", 174 | "sebastian/comparator": "~1.1", 175 | "sebastian/recursion-context": "~1.0" 176 | }, 177 | "require-dev": { 178 | "phpspec/phpspec": "~2.0" 179 | }, 180 | "type": "library", 181 | "extra": { 182 | "branch-alias": { 183 | "dev-master": "1.5.x-dev" 184 | } 185 | }, 186 | "autoload": { 187 | "psr-0": { 188 | "Prophecy\\": "src/" 189 | } 190 | }, 191 | "notification-url": "https://packagist.org/downloads/", 192 | "license": [ 193 | "MIT" 194 | ], 195 | "authors": [ 196 | { 197 | "name": "Konstantin Kudryashov", 198 | "email": "ever.zet@gmail.com", 199 | "homepage": "http://everzet.com" 200 | }, 201 | { 202 | "name": "Marcello Duarte", 203 | "email": "marcello.duarte@gmail.com" 204 | } 205 | ], 206 | "description": "Highly opinionated mocking framework for PHP 5.3+", 207 | "homepage": "https://github.com/phpspec/prophecy", 208 | "keywords": [ 209 | "Double", 210 | "Dummy", 211 | "fake", 212 | "mock", 213 | "spy", 214 | "stub" 215 | ], 216 | "time": "2016-02-15 07:46:21" 217 | }, 218 | { 219 | "name": "phpunit/php-code-coverage", 220 | "version": "3.3.1", 221 | "source": { 222 | "type": "git", 223 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 224 | "reference": "2431befdd451fac43fbcde94d1a92fb3b8b68f86" 225 | }, 226 | "dist": { 227 | "type": "zip", 228 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2431befdd451fac43fbcde94d1a92fb3b8b68f86", 229 | "reference": "2431befdd451fac43fbcde94d1a92fb3b8b68f86", 230 | "shasum": "" 231 | }, 232 | "require": { 233 | "php": "^5.6 || ^7.0", 234 | "phpunit/php-file-iterator": "~1.3", 235 | "phpunit/php-text-template": "~1.2", 236 | "phpunit/php-token-stream": "^1.4.2", 237 | "sebastian/code-unit-reverse-lookup": "~1.0", 238 | "sebastian/environment": "^1.3.2", 239 | "sebastian/version": "~1.0|~2.0" 240 | }, 241 | "require-dev": { 242 | "ext-xdebug": ">=2.1.4", 243 | "phpunit/phpunit": "~5" 244 | }, 245 | "suggest": { 246 | "ext-dom": "*", 247 | "ext-xdebug": ">=2.4.0", 248 | "ext-xmlwriter": "*" 249 | }, 250 | "type": "library", 251 | "extra": { 252 | "branch-alias": { 253 | "dev-master": "3.3.x-dev" 254 | } 255 | }, 256 | "autoload": { 257 | "classmap": [ 258 | "src/" 259 | ] 260 | }, 261 | "notification-url": "https://packagist.org/downloads/", 262 | "license": [ 263 | "BSD-3-Clause" 264 | ], 265 | "authors": [ 266 | { 267 | "name": "Sebastian Bergmann", 268 | "email": "sb@sebastian-bergmann.de", 269 | "role": "lead" 270 | } 271 | ], 272 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 273 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 274 | "keywords": [ 275 | "coverage", 276 | "testing", 277 | "xunit" 278 | ], 279 | "time": "2016-04-08 08:14:53" 280 | }, 281 | { 282 | "name": "phpunit/php-file-iterator", 283 | "version": "1.4.1", 284 | "source": { 285 | "type": "git", 286 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 287 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 288 | }, 289 | "dist": { 290 | "type": "zip", 291 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 292 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 293 | "shasum": "" 294 | }, 295 | "require": { 296 | "php": ">=5.3.3" 297 | }, 298 | "type": "library", 299 | "extra": { 300 | "branch-alias": { 301 | "dev-master": "1.4.x-dev" 302 | } 303 | }, 304 | "autoload": { 305 | "classmap": [ 306 | "src/" 307 | ] 308 | }, 309 | "notification-url": "https://packagist.org/downloads/", 310 | "license": [ 311 | "BSD-3-Clause" 312 | ], 313 | "authors": [ 314 | { 315 | "name": "Sebastian Bergmann", 316 | "email": "sb@sebastian-bergmann.de", 317 | "role": "lead" 318 | } 319 | ], 320 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 321 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 322 | "keywords": [ 323 | "filesystem", 324 | "iterator" 325 | ], 326 | "time": "2015-06-21 13:08:43" 327 | }, 328 | { 329 | "name": "phpunit/php-text-template", 330 | "version": "1.2.1", 331 | "source": { 332 | "type": "git", 333 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 334 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 335 | }, 336 | "dist": { 337 | "type": "zip", 338 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 339 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 340 | "shasum": "" 341 | }, 342 | "require": { 343 | "php": ">=5.3.3" 344 | }, 345 | "type": "library", 346 | "autoload": { 347 | "classmap": [ 348 | "src/" 349 | ] 350 | }, 351 | "notification-url": "https://packagist.org/downloads/", 352 | "license": [ 353 | "BSD-3-Clause" 354 | ], 355 | "authors": [ 356 | { 357 | "name": "Sebastian Bergmann", 358 | "email": "sebastian@phpunit.de", 359 | "role": "lead" 360 | } 361 | ], 362 | "description": "Simple template engine.", 363 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 364 | "keywords": [ 365 | "template" 366 | ], 367 | "time": "2015-06-21 13:50:34" 368 | }, 369 | { 370 | "name": "phpunit/php-timer", 371 | "version": "1.0.7", 372 | "source": { 373 | "type": "git", 374 | "url": "https://github.com/sebastianbergmann/php-timer.git", 375 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" 376 | }, 377 | "dist": { 378 | "type": "zip", 379 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 380 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 381 | "shasum": "" 382 | }, 383 | "require": { 384 | "php": ">=5.3.3" 385 | }, 386 | "type": "library", 387 | "autoload": { 388 | "classmap": [ 389 | "src/" 390 | ] 391 | }, 392 | "notification-url": "https://packagist.org/downloads/", 393 | "license": [ 394 | "BSD-3-Clause" 395 | ], 396 | "authors": [ 397 | { 398 | "name": "Sebastian Bergmann", 399 | "email": "sb@sebastian-bergmann.de", 400 | "role": "lead" 401 | } 402 | ], 403 | "description": "Utility class for timing", 404 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 405 | "keywords": [ 406 | "timer" 407 | ], 408 | "time": "2015-06-21 08:01:12" 409 | }, 410 | { 411 | "name": "phpunit/php-token-stream", 412 | "version": "1.4.8", 413 | "source": { 414 | "type": "git", 415 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 416 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" 417 | }, 418 | "dist": { 419 | "type": "zip", 420 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 421 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 422 | "shasum": "" 423 | }, 424 | "require": { 425 | "ext-tokenizer": "*", 426 | "php": ">=5.3.3" 427 | }, 428 | "require-dev": { 429 | "phpunit/phpunit": "~4.2" 430 | }, 431 | "type": "library", 432 | "extra": { 433 | "branch-alias": { 434 | "dev-master": "1.4-dev" 435 | } 436 | }, 437 | "autoload": { 438 | "classmap": [ 439 | "src/" 440 | ] 441 | }, 442 | "notification-url": "https://packagist.org/downloads/", 443 | "license": [ 444 | "BSD-3-Clause" 445 | ], 446 | "authors": [ 447 | { 448 | "name": "Sebastian Bergmann", 449 | "email": "sebastian@phpunit.de" 450 | } 451 | ], 452 | "description": "Wrapper around PHP's tokenizer extension.", 453 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 454 | "keywords": [ 455 | "tokenizer" 456 | ], 457 | "time": "2015-09-15 10:49:45" 458 | }, 459 | { 460 | "name": "phpunit/phpunit", 461 | "version": "5.3.2", 462 | "source": { 463 | "type": "git", 464 | "url": "https://github.com/sebastianbergmann/phpunit.git", 465 | "reference": "2c6da3536035617bae3fe3db37283c9e0eb63ab3" 466 | }, 467 | "dist": { 468 | "type": "zip", 469 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2c6da3536035617bae3fe3db37283c9e0eb63ab3", 470 | "reference": "2c6da3536035617bae3fe3db37283c9e0eb63ab3", 471 | "shasum": "" 472 | }, 473 | "require": { 474 | "ext-dom": "*", 475 | "ext-json": "*", 476 | "ext-pcre": "*", 477 | "ext-reflection": "*", 478 | "ext-spl": "*", 479 | "myclabs/deep-copy": "~1.3", 480 | "php": "^5.6 || ^7.0", 481 | "phpspec/prophecy": "^1.3.1", 482 | "phpunit/php-code-coverage": "^3.3.0", 483 | "phpunit/php-file-iterator": "~1.4", 484 | "phpunit/php-text-template": "~1.2", 485 | "phpunit/php-timer": "^1.0.6", 486 | "phpunit/phpunit-mock-objects": "^3.1", 487 | "sebastian/comparator": "~1.1", 488 | "sebastian/diff": "~1.2", 489 | "sebastian/environment": "~1.3", 490 | "sebastian/exporter": "~1.2", 491 | "sebastian/global-state": "~1.0", 492 | "sebastian/object-enumerator": "~1.0", 493 | "sebastian/resource-operations": "~1.0", 494 | "sebastian/version": "~1.0|~2.0", 495 | "symfony/yaml": "~2.1|~3.0" 496 | }, 497 | "suggest": { 498 | "phpunit/php-invoker": "~1.1" 499 | }, 500 | "bin": [ 501 | "phpunit" 502 | ], 503 | "type": "library", 504 | "extra": { 505 | "branch-alias": { 506 | "dev-master": "5.3.x-dev" 507 | } 508 | }, 509 | "autoload": { 510 | "classmap": [ 511 | "src/" 512 | ] 513 | }, 514 | "notification-url": "https://packagist.org/downloads/", 515 | "license": [ 516 | "BSD-3-Clause" 517 | ], 518 | "authors": [ 519 | { 520 | "name": "Sebastian Bergmann", 521 | "email": "sebastian@phpunit.de", 522 | "role": "lead" 523 | } 524 | ], 525 | "description": "The PHP Unit Testing framework.", 526 | "homepage": "https://phpunit.de/", 527 | "keywords": [ 528 | "phpunit", 529 | "testing", 530 | "xunit" 531 | ], 532 | "time": "2016-04-12 16:20:08" 533 | }, 534 | { 535 | "name": "phpunit/phpunit-mock-objects", 536 | "version": "3.1.2", 537 | "source": { 538 | "type": "git", 539 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 540 | "reference": "7c34c9bdde4131b824086457a3145e27dba10ca1" 541 | }, 542 | "dist": { 543 | "type": "zip", 544 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/7c34c9bdde4131b824086457a3145e27dba10ca1", 545 | "reference": "7c34c9bdde4131b824086457a3145e27dba10ca1", 546 | "shasum": "" 547 | }, 548 | "require": { 549 | "doctrine/instantiator": "^1.0.2", 550 | "php": ">=5.6", 551 | "phpunit/php-text-template": "~1.2", 552 | "sebastian/exporter": "~1.2" 553 | }, 554 | "require-dev": { 555 | "phpunit/phpunit": "~5" 556 | }, 557 | "suggest": { 558 | "ext-soap": "*" 559 | }, 560 | "type": "library", 561 | "extra": { 562 | "branch-alias": { 563 | "dev-master": "3.1.x-dev" 564 | } 565 | }, 566 | "autoload": { 567 | "classmap": [ 568 | "src/" 569 | ] 570 | }, 571 | "notification-url": "https://packagist.org/downloads/", 572 | "license": [ 573 | "BSD-3-Clause" 574 | ], 575 | "authors": [ 576 | { 577 | "name": "Sebastian Bergmann", 578 | "email": "sb@sebastian-bergmann.de", 579 | "role": "lead" 580 | } 581 | ], 582 | "description": "Mock Object library for PHPUnit", 583 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 584 | "keywords": [ 585 | "mock", 586 | "xunit" 587 | ], 588 | "time": "2016-03-24 05:58:25" 589 | }, 590 | { 591 | "name": "sebastian/code-unit-reverse-lookup", 592 | "version": "1.0.0", 593 | "source": { 594 | "type": "git", 595 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 596 | "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" 597 | }, 598 | "dist": { 599 | "type": "zip", 600 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", 601 | "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", 602 | "shasum": "" 603 | }, 604 | "require": { 605 | "php": ">=5.6" 606 | }, 607 | "require-dev": { 608 | "phpunit/phpunit": "~5" 609 | }, 610 | "type": "library", 611 | "extra": { 612 | "branch-alias": { 613 | "dev-master": "1.0.x-dev" 614 | } 615 | }, 616 | "autoload": { 617 | "classmap": [ 618 | "src/" 619 | ] 620 | }, 621 | "notification-url": "https://packagist.org/downloads/", 622 | "license": [ 623 | "BSD-3-Clause" 624 | ], 625 | "authors": [ 626 | { 627 | "name": "Sebastian Bergmann", 628 | "email": "sebastian@phpunit.de" 629 | } 630 | ], 631 | "description": "Looks up which function or method a line of code belongs to", 632 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 633 | "time": "2016-02-13 06:45:14" 634 | }, 635 | { 636 | "name": "sebastian/comparator", 637 | "version": "1.2.0", 638 | "source": { 639 | "type": "git", 640 | "url": "https://github.com/sebastianbergmann/comparator.git", 641 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 642 | }, 643 | "dist": { 644 | "type": "zip", 645 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 646 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 647 | "shasum": "" 648 | }, 649 | "require": { 650 | "php": ">=5.3.3", 651 | "sebastian/diff": "~1.2", 652 | "sebastian/exporter": "~1.2" 653 | }, 654 | "require-dev": { 655 | "phpunit/phpunit": "~4.4" 656 | }, 657 | "type": "library", 658 | "extra": { 659 | "branch-alias": { 660 | "dev-master": "1.2.x-dev" 661 | } 662 | }, 663 | "autoload": { 664 | "classmap": [ 665 | "src/" 666 | ] 667 | }, 668 | "notification-url": "https://packagist.org/downloads/", 669 | "license": [ 670 | "BSD-3-Clause" 671 | ], 672 | "authors": [ 673 | { 674 | "name": "Jeff Welch", 675 | "email": "whatthejeff@gmail.com" 676 | }, 677 | { 678 | "name": "Volker Dusch", 679 | "email": "github@wallbash.com" 680 | }, 681 | { 682 | "name": "Bernhard Schussek", 683 | "email": "bschussek@2bepublished.at" 684 | }, 685 | { 686 | "name": "Sebastian Bergmann", 687 | "email": "sebastian@phpunit.de" 688 | } 689 | ], 690 | "description": "Provides the functionality to compare PHP values for equality", 691 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 692 | "keywords": [ 693 | "comparator", 694 | "compare", 695 | "equality" 696 | ], 697 | "time": "2015-07-26 15:48:44" 698 | }, 699 | { 700 | "name": "sebastian/diff", 701 | "version": "1.4.1", 702 | "source": { 703 | "type": "git", 704 | "url": "https://github.com/sebastianbergmann/diff.git", 705 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 706 | }, 707 | "dist": { 708 | "type": "zip", 709 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 710 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 711 | "shasum": "" 712 | }, 713 | "require": { 714 | "php": ">=5.3.3" 715 | }, 716 | "require-dev": { 717 | "phpunit/phpunit": "~4.8" 718 | }, 719 | "type": "library", 720 | "extra": { 721 | "branch-alias": { 722 | "dev-master": "1.4-dev" 723 | } 724 | }, 725 | "autoload": { 726 | "classmap": [ 727 | "src/" 728 | ] 729 | }, 730 | "notification-url": "https://packagist.org/downloads/", 731 | "license": [ 732 | "BSD-3-Clause" 733 | ], 734 | "authors": [ 735 | { 736 | "name": "Kore Nordmann", 737 | "email": "mail@kore-nordmann.de" 738 | }, 739 | { 740 | "name": "Sebastian Bergmann", 741 | "email": "sebastian@phpunit.de" 742 | } 743 | ], 744 | "description": "Diff implementation", 745 | "homepage": "https://github.com/sebastianbergmann/diff", 746 | "keywords": [ 747 | "diff" 748 | ], 749 | "time": "2015-12-08 07:14:41" 750 | }, 751 | { 752 | "name": "sebastian/environment", 753 | "version": "1.3.5", 754 | "source": { 755 | "type": "git", 756 | "url": "https://github.com/sebastianbergmann/environment.git", 757 | "reference": "dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf" 758 | }, 759 | "dist": { 760 | "type": "zip", 761 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf", 762 | "reference": "dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf", 763 | "shasum": "" 764 | }, 765 | "require": { 766 | "php": ">=5.3.3" 767 | }, 768 | "require-dev": { 769 | "phpunit/phpunit": "~4.4" 770 | }, 771 | "type": "library", 772 | "extra": { 773 | "branch-alias": { 774 | "dev-master": "1.3.x-dev" 775 | } 776 | }, 777 | "autoload": { 778 | "classmap": [ 779 | "src/" 780 | ] 781 | }, 782 | "notification-url": "https://packagist.org/downloads/", 783 | "license": [ 784 | "BSD-3-Clause" 785 | ], 786 | "authors": [ 787 | { 788 | "name": "Sebastian Bergmann", 789 | "email": "sebastian@phpunit.de" 790 | } 791 | ], 792 | "description": "Provides functionality to handle HHVM/PHP environments", 793 | "homepage": "http://www.github.com/sebastianbergmann/environment", 794 | "keywords": [ 795 | "Xdebug", 796 | "environment", 797 | "hhvm" 798 | ], 799 | "time": "2016-02-26 18:40:46" 800 | }, 801 | { 802 | "name": "sebastian/exporter", 803 | "version": "1.2.1", 804 | "source": { 805 | "type": "git", 806 | "url": "https://github.com/sebastianbergmann/exporter.git", 807 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e" 808 | }, 809 | "dist": { 810 | "type": "zip", 811 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", 812 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e", 813 | "shasum": "" 814 | }, 815 | "require": { 816 | "php": ">=5.3.3", 817 | "sebastian/recursion-context": "~1.0" 818 | }, 819 | "require-dev": { 820 | "phpunit/phpunit": "~4.4" 821 | }, 822 | "type": "library", 823 | "extra": { 824 | "branch-alias": { 825 | "dev-master": "1.2.x-dev" 826 | } 827 | }, 828 | "autoload": { 829 | "classmap": [ 830 | "src/" 831 | ] 832 | }, 833 | "notification-url": "https://packagist.org/downloads/", 834 | "license": [ 835 | "BSD-3-Clause" 836 | ], 837 | "authors": [ 838 | { 839 | "name": "Jeff Welch", 840 | "email": "whatthejeff@gmail.com" 841 | }, 842 | { 843 | "name": "Volker Dusch", 844 | "email": "github@wallbash.com" 845 | }, 846 | { 847 | "name": "Bernhard Schussek", 848 | "email": "bschussek@2bepublished.at" 849 | }, 850 | { 851 | "name": "Sebastian Bergmann", 852 | "email": "sebastian@phpunit.de" 853 | }, 854 | { 855 | "name": "Adam Harvey", 856 | "email": "aharvey@php.net" 857 | } 858 | ], 859 | "description": "Provides the functionality to export PHP variables for visualization", 860 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 861 | "keywords": [ 862 | "export", 863 | "exporter" 864 | ], 865 | "time": "2015-06-21 07:55:53" 866 | }, 867 | { 868 | "name": "sebastian/global-state", 869 | "version": "1.1.1", 870 | "source": { 871 | "type": "git", 872 | "url": "https://github.com/sebastianbergmann/global-state.git", 873 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 874 | }, 875 | "dist": { 876 | "type": "zip", 877 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 878 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 879 | "shasum": "" 880 | }, 881 | "require": { 882 | "php": ">=5.3.3" 883 | }, 884 | "require-dev": { 885 | "phpunit/phpunit": "~4.2" 886 | }, 887 | "suggest": { 888 | "ext-uopz": "*" 889 | }, 890 | "type": "library", 891 | "extra": { 892 | "branch-alias": { 893 | "dev-master": "1.0-dev" 894 | } 895 | }, 896 | "autoload": { 897 | "classmap": [ 898 | "src/" 899 | ] 900 | }, 901 | "notification-url": "https://packagist.org/downloads/", 902 | "license": [ 903 | "BSD-3-Clause" 904 | ], 905 | "authors": [ 906 | { 907 | "name": "Sebastian Bergmann", 908 | "email": "sebastian@phpunit.de" 909 | } 910 | ], 911 | "description": "Snapshotting of global state", 912 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 913 | "keywords": [ 914 | "global state" 915 | ], 916 | "time": "2015-10-12 03:26:01" 917 | }, 918 | { 919 | "name": "sebastian/object-enumerator", 920 | "version": "1.0.0", 921 | "source": { 922 | "type": "git", 923 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 924 | "reference": "d4ca2fb70344987502567bc50081c03e6192fb26" 925 | }, 926 | "dist": { 927 | "type": "zip", 928 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26", 929 | "reference": "d4ca2fb70344987502567bc50081c03e6192fb26", 930 | "shasum": "" 931 | }, 932 | "require": { 933 | "php": ">=5.6", 934 | "sebastian/recursion-context": "~1.0" 935 | }, 936 | "require-dev": { 937 | "phpunit/phpunit": "~5" 938 | }, 939 | "type": "library", 940 | "extra": { 941 | "branch-alias": { 942 | "dev-master": "1.0.x-dev" 943 | } 944 | }, 945 | "autoload": { 946 | "classmap": [ 947 | "src/" 948 | ] 949 | }, 950 | "notification-url": "https://packagist.org/downloads/", 951 | "license": [ 952 | "BSD-3-Clause" 953 | ], 954 | "authors": [ 955 | { 956 | "name": "Sebastian Bergmann", 957 | "email": "sebastian@phpunit.de" 958 | } 959 | ], 960 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 961 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 962 | "time": "2016-01-28 13:25:10" 963 | }, 964 | { 965 | "name": "sebastian/recursion-context", 966 | "version": "1.0.2", 967 | "source": { 968 | "type": "git", 969 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 970 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791" 971 | }, 972 | "dist": { 973 | "type": "zip", 974 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", 975 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791", 976 | "shasum": "" 977 | }, 978 | "require": { 979 | "php": ">=5.3.3" 980 | }, 981 | "require-dev": { 982 | "phpunit/phpunit": "~4.4" 983 | }, 984 | "type": "library", 985 | "extra": { 986 | "branch-alias": { 987 | "dev-master": "1.0.x-dev" 988 | } 989 | }, 990 | "autoload": { 991 | "classmap": [ 992 | "src/" 993 | ] 994 | }, 995 | "notification-url": "https://packagist.org/downloads/", 996 | "license": [ 997 | "BSD-3-Clause" 998 | ], 999 | "authors": [ 1000 | { 1001 | "name": "Jeff Welch", 1002 | "email": "whatthejeff@gmail.com" 1003 | }, 1004 | { 1005 | "name": "Sebastian Bergmann", 1006 | "email": "sebastian@phpunit.de" 1007 | }, 1008 | { 1009 | "name": "Adam Harvey", 1010 | "email": "aharvey@php.net" 1011 | } 1012 | ], 1013 | "description": "Provides functionality to recursively process PHP variables", 1014 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1015 | "time": "2015-11-11 19:50:13" 1016 | }, 1017 | { 1018 | "name": "sebastian/resource-operations", 1019 | "version": "1.0.0", 1020 | "source": { 1021 | "type": "git", 1022 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1023 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" 1024 | }, 1025 | "dist": { 1026 | "type": "zip", 1027 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1028 | "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", 1029 | "shasum": "" 1030 | }, 1031 | "require": { 1032 | "php": ">=5.6.0" 1033 | }, 1034 | "type": "library", 1035 | "extra": { 1036 | "branch-alias": { 1037 | "dev-master": "1.0.x-dev" 1038 | } 1039 | }, 1040 | "autoload": { 1041 | "classmap": [ 1042 | "src/" 1043 | ] 1044 | }, 1045 | "notification-url": "https://packagist.org/downloads/", 1046 | "license": [ 1047 | "BSD-3-Clause" 1048 | ], 1049 | "authors": [ 1050 | { 1051 | "name": "Sebastian Bergmann", 1052 | "email": "sebastian@phpunit.de" 1053 | } 1054 | ], 1055 | "description": "Provides a list of PHP built-in functions that operate on resources", 1056 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1057 | "time": "2015-07-28 20:34:47" 1058 | }, 1059 | { 1060 | "name": "sebastian/version", 1061 | "version": "2.0.0", 1062 | "source": { 1063 | "type": "git", 1064 | "url": "https://github.com/sebastianbergmann/version.git", 1065 | "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5" 1066 | }, 1067 | "dist": { 1068 | "type": "zip", 1069 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", 1070 | "reference": "c829badbd8fdf16a0bad8aa7fa7971c029f1b9c5", 1071 | "shasum": "" 1072 | }, 1073 | "require": { 1074 | "php": ">=5.6" 1075 | }, 1076 | "type": "library", 1077 | "extra": { 1078 | "branch-alias": { 1079 | "dev-master": "2.0.x-dev" 1080 | } 1081 | }, 1082 | "autoload": { 1083 | "classmap": [ 1084 | "src/" 1085 | ] 1086 | }, 1087 | "notification-url": "https://packagist.org/downloads/", 1088 | "license": [ 1089 | "BSD-3-Clause" 1090 | ], 1091 | "authors": [ 1092 | { 1093 | "name": "Sebastian Bergmann", 1094 | "email": "sebastian@phpunit.de", 1095 | "role": "lead" 1096 | } 1097 | ], 1098 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1099 | "homepage": "https://github.com/sebastianbergmann/version", 1100 | "time": "2016-02-04 12:56:52" 1101 | }, 1102 | { 1103 | "name": "symfony/yaml", 1104 | "version": "v3.0.4", 1105 | "source": { 1106 | "type": "git", 1107 | "url": "https://github.com/symfony/yaml.git", 1108 | "reference": "0047c8366744a16de7516622c5b7355336afae96" 1109 | }, 1110 | "dist": { 1111 | "type": "zip", 1112 | "url": "https://api.github.com/repos/symfony/yaml/zipball/0047c8366744a16de7516622c5b7355336afae96", 1113 | "reference": "0047c8366744a16de7516622c5b7355336afae96", 1114 | "shasum": "" 1115 | }, 1116 | "require": { 1117 | "php": ">=5.5.9" 1118 | }, 1119 | "type": "library", 1120 | "extra": { 1121 | "branch-alias": { 1122 | "dev-master": "3.0-dev" 1123 | } 1124 | }, 1125 | "autoload": { 1126 | "psr-4": { 1127 | "Symfony\\Component\\Yaml\\": "" 1128 | }, 1129 | "exclude-from-classmap": [ 1130 | "/Tests/" 1131 | ] 1132 | }, 1133 | "notification-url": "https://packagist.org/downloads/", 1134 | "license": [ 1135 | "MIT" 1136 | ], 1137 | "authors": [ 1138 | { 1139 | "name": "Fabien Potencier", 1140 | "email": "fabien@symfony.com" 1141 | }, 1142 | { 1143 | "name": "Symfony Community", 1144 | "homepage": "https://symfony.com/contributors" 1145 | } 1146 | ], 1147 | "description": "Symfony Yaml Component", 1148 | "homepage": "https://symfony.com", 1149 | "time": "2016-03-04 07:55:57" 1150 | } 1151 | ], 1152 | "aliases": [], 1153 | "minimum-stability": "stable", 1154 | "stability-flags": [], 1155 | "prefer-stable": false, 1156 | "prefer-lowest": false, 1157 | "platform": { 1158 | "php": ">=5.4.0" 1159 | }, 1160 | "platform-dev": [] 1161 | } 1162 | -------------------------------------------------------------------------------- /test/test.php: -------------------------------------------------------------------------------- 1 | $rows) { 13 | echo " ${key}\n"; 14 | 15 | foreach ($rows as $k => $v) { 16 | list ($text, $html) = $v; 17 | 18 | $markdown = $parser->makeHtml($text); 19 | 20 | if ($html == $markdown) { 21 | echo " ✓ ${k}\n"; 22 | } else { 23 | $num = count($errors) + 1; 24 | echo " ${num}) ${k}\n"; 25 | $errors[] = [$key . ' ' . $k, $markdown, $html]; 26 | } 27 | 28 | $count ++; 29 | } 30 | } 31 | 32 | echo "\n " . ($count - count($errors)) . " passing\n"; 33 | echo " " . count($errors) . " falling\n\n"; 34 | 35 | foreach ($errors as $k => $error) { 36 | list ($key, $markdown, $html) = $error; 37 | 38 | echo ' ' . ($k + 1) . ') ' . $title . " {$key}:\n"; 39 | echo ' ' . $html; 40 | echo "\n ==============\n"; 41 | echo ' ' . $markdown; 42 | echo "\n"; 43 | } 44 | } 45 | 46 | test('HyperDown', [ 47 | 'footnote' => [ 48 | '脚注' => [ 49 | "Never write \"[click here][^2]\".\n [^2]: http://www.w3.org/QA/Tips/noClickHere", 50 | 'Never write "[click here]1".
123bold123
' 73 | ] 74 | ], 75 | 'italy' => [ 76 | '*italy*' => [ 77 | '123 *italy* 123', 78 | '123 italy 123
' 79 | ] 80 | ], 81 | 'list' => [ 82 | 'ul' => [ 83 | "\n\n - list", 84 | '[系统盘]:\\Documents and Settings\\[用户名]\\Cookies$\\lambda$
" 99 | ], 100 | 101 | 'table' => [ 102 | "|---------------|-------|\n| Variable_name | Value |\n| ------------- | ----- |\n| sql_mode | ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION |\n|---------------|-------|", 103 | 'Variable_name | Value |
---|---|
sql_mode | ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION |