From 798b13ab769fb2240eb08d0d4648652844902228 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 18:47:51 +0100 Subject: [PATCH 01/20] Everytime you use static keyword, god kills a kitten (it breaks OCP). Removing static factory : it breaks SRP --- .gitignore | 1 + src/Mike42/Wikitext/WikitextParser.php | 361 ++++++++++++------------- test/unit/WikitextParserTest.php | 24 +- 3 files changed, 196 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 49fc447..b4af13f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .project /vendor/ +/nbproject/ diff --git a/src/Mike42/Wikitext/WikitextParser.php b/src/Mike42/Wikitext/WikitextParser.php index fa8229a..6f71a9b 100644 --- a/src/Mike42/Wikitext/WikitextParser.php +++ b/src/Mike42/Wikitext/WikitextParser.php @@ -1,42 +1,44 @@ - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ + Library to add wikitext support to a web app -- http://mike.bitrevision.com/wikitext/ + + Copyright (C) 2012 Michael Billington + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + namespace Mike42\Wikitext; class WikitextParser { + const VERSION = "1.0"; const MAX_INCLUDE_DEPTH = 32; /* Depth of template includes to put up with. set to 0 to disallow inclusion, negative to remove the limit */ - private static $inline; - private static $lineBlock; - public static $backend; - private static $tableBlock; - private static $tableStart; - private static $inlineLookup; - private static $inlineChars; - private static $preprocessor; - private static $preprocessorChars; - private static $initialised = false; + private $inline; + private $lineBlock; + protected $backend; + private $tableBlock; + private $tableStart; + private $inlineLookup; + private $inlineChars; + private $preprocessor; + private $preprocessorChars; /* These are set as a result of parsing */ public $preprocessed; /* Wikitext after preprocessor had a go at it. */ @@ -45,27 +47,27 @@ class WikitextParser /** * Definitions for tokens with special meaning to the parser */ - public static function init(array $sharedVars = []) + public function __construct(array $sharedVars = []) { /* Table elements. These are parsed separately to the other elements */ - self::$tableStart = new ParserInlineElement("{|", "|}"); + $this->tableStart = new ParserInlineElement("{|", "|}"); - self::$tableBlock = array( - 'tr' => new ParserTableElement('|-', '', '', ''), - 'th' => new ParserTableElement('!', '|', '!!', 1), - 'td' => new ParserTableElement('|', '|', '||', 1), - 'caption' => new ParserTableElement('|+', '', '', 0)); + $this->tableBlock = array( + 'tr' => new ParserTableElement('|-', '', '', ''), + 'th' => new ParserTableElement('!', '|', '!!', 1), + 'td' => new ParserTableElement('|', '|', '||', 1), + 'caption' => new ParserTableElement('|+', '', '', 0)); /* Inline elemens. These are parsed recursively and can be nested as deeply as the system will allow. */ - self::$inline = array( - 'nothing' => new ParserInlineElement('', ''), - 'td' => new ParserInlineElement('', ''), // Just used as a marker - 'linkInternal' => new ParserInlineElement('[[', ']]', '|', '='), - 'linkExternal' => new ParserInlineElement('[', ']', ' ', '', 1), - 'bold' => new ParserInlineElement("'''", "'''"), - 'italic' => new ParserInlineElement("''", "''"), - 'switch' => new ParserInlineElement('__', '__')); - self::$inlineChars = [ + $this->inline = array( + 'nothing' => new ParserInlineElement('', ''), + 'td' => new ParserInlineElement('', ''), // Just used as a marker + 'linkInternal' => new ParserInlineElement('[[', ']]', '|', '='), + 'linkExternal' => new ParserInlineElement('[', ']', ' ', '', 1), + 'bold' => new ParserInlineElement("'''", "'''"), + 'italic' => new ParserInlineElement("''", "''"), + 'switch' => new ParserInlineElement('__', '__')); + $this->inlineChars = [ '[' => true, '\'' => true, ']' => true, @@ -82,39 +84,39 @@ public static function init(array $sharedVars = []) /* Create lookup table for efficiency */ - self::$inlineLookup = self::elementLookupTable(self::$inline); - self::$backend = new DefaultParserBackend(); + $this->inlineLookup = $this->elementLookupTable($this->inline); + $this->backend = new DefaultParserBackend(); /* Line-block elements. These are characters which have a special meaning at the start of lines, and use the next end-line as a close tag. */ - self::$lineBlock = array( - 'pre' => new ParserLineBlockElement(array(" "), [], 1, false), - 'ul' => new ParserLineBlockElement(array("*"), [], 32, true), - 'ol' => new ParserLineBlockElement(array("#"), [], 32, true), - 'dl' => new ParserLineBlockElement(array(":", ";"), [], 32, true), - 'h' => new ParserLineBlockElement(array("="), array("="), 6, false)); - - self::$preprocessor = array( - 'noinclude' => new ParserInlineElement('', ''), - 'includeonly' => new ParserInlineElement('', ''), - 'arg' => new ParserInlineElement('{{{', '}}}', '|', '', 1), - 'template' => new ParserInlineElement('{{', '}}', '|', '='), - 'comment' => new ParserInlineElement('')); - self::$preprocessorChars = [ + $this->lineBlock = array( + 'pre' => new ParserLineBlockElement(array(" "), [], 1, false), + 'ul' => new ParserLineBlockElement(array("*"), [], 32, true), + 'ol' => new ParserLineBlockElement(array("#"), [], 32, true), + 'dl' => new ParserLineBlockElement(array(":", ";"), [], 32, true), + 'h' => new ParserLineBlockElement(array("="), array("="), 6, false)); + + $this->preprocessor = array( + 'noinclude' => new ParserInlineElement('', ''), + 'includeonly' => new ParserInlineElement('', ''), + 'arg' => new ParserInlineElement('{{{', '}}}', '|', '', 1), + 'template' => new ParserInlineElement('{{', '}}', '|', '='), + 'comment' => new ParserInlineElement('')); + $this->preprocessorChars = [ '<' => true, '=' => true, '|' => true, '{' => true ]; - self::$initialised = true; + $this->initialised = true; } - private static function elementLookupTable(array $elements) + private function elementLookupTable(array $elements) { $lookup = []; foreach ($elements as $key => $token) { - if (count($token -> startTag) != 0) { - $c = $token -> startTag[0]; + if (count($token->startTag) != 0) { + $c = $token->startTag[0]; if (!isset($lookup[$c])) { $lookup[$c] = []; } @@ -129,42 +131,25 @@ private static function elementLookupTable(array $elements) * * @param string $text */ - public static function parse(string $text) - { - $parser = new WikitextParser($text); - return $parser -> result; - } - - /** - * Initialise a new parser object and parse a standalone document. - * If templates are included, each will processed by a different instance of this object - * - * @param string $text The text to parse - */ - public function __construct(string $text, array $params = []) + public function parse(string $text): string { - if (self::$initialised == false) { - self::init(); - } - $this -> params = $params; - $this -> preprocessed = $this -> preprocessText(self::explodeString($text)); + $this->preprocessed = $this->preprocessText($this->explodeString($text)); /* Now divide into paragraphs */ // TODO operate on arrays instead of strings here - $sections = explode("\n\n", str_replace("\r\n", "\n", $this -> preprocessed)); + $sections = explode("\n\n", str_replace("\r\n", "\n", $this->preprocessed)); $newtext = []; foreach ($sections as $section) { /* Newlines at the start/end have special meaning (compare to how this is called from parseLineBlock) */ - $sectionChars = self::explodeString("\n".$section); - $result = $this -> parseInline($sectionChars, 'p'); + $sectionChars = $this->explodeString("\n" . $section); + $result = $this->parseInline($sectionChars, 'p'); $newtext[] = $result['parsed']; } - - $this -> result = implode($newtext); + return $this->result = implode($newtext); } - private static function explodeString(string $string) + private function explodeString(string $string) { return $chrArray = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY); } @@ -183,27 +168,27 @@ private function preprocessText(array $textChars, array $arg = [], bool $include for ($i = 0; $i < $len; $i++) { $hit = false; $c = $textChars[$i]; - if (!isset(self::$preprocessorChars[$c])) { + if (!isset($this->preprocessorChars[$c])) { /* Fast exit for characters that do not start a tag. */ // TODO Could work faster if we didn't concatenate each character $parsed .= $c; continue; } - foreach (self::$preprocessor as $key => $child) { - if (self::tagIsAt($child -> endTag, $textChars, $i)) { + foreach ($this->preprocessor as $key => $child) { + if ($this->tagIsAt($child->endTag, $textChars, $i)) { if (($key == 'includeonly' && $included) || ($key == 'noinclude' && !$included)) { $hit = true; - $i += count($child -> endTag); + $i += count($child->endTag); /* Ignore expected end-tags */ break; } } - if (self::tagIsAt($child -> startTag, $textChars, $i)) { + if ($this->tagIsAt($child->startTag, $textChars, $i)) { /* Hit a symbol. Parse it and keep going after the result */ $hit = true; - $i += count($child -> startTag); + $i += count($child->startTag); if (($key == 'includeonly' && $included) || ($key == 'noinclude' && !$included)) { /* If this is a good tag, ignore it! */ @@ -211,15 +196,15 @@ private function preprocessText(array $textChars, array $arg = [], bool $include } /* Seek until end tag, looking for splitters */ - $innerArg = []; + $innerArg = []; $innerBuffer = ''; $innerCurKey = ''; for ($i = $i; $i < $len; $i++) { $innerHit = false; - if (self::tagIsAt($child -> endTag, $textChars, $i)) { - $i += count($child -> endTag); + if ($this->tagIsAt($child->endTag, $textChars, $i)) { + $i += count($child->endTag); /* Clear buffers now */ if ($innerCurKey == '') { array_push($innerArg, $innerBuffer); @@ -240,9 +225,9 @@ private function preprocessText(array $textChars, array $arg = [], bool $include } } else if ($key == 'template') { /* Load wikitext of template, and preprocess it */ - if (self::MAX_INCLUDE_DEPTH < 0 || $depth < self::MAX_INCLUDE_DEPTH) { - $markup = trim(self::$backend -> getTemplateMarkup($innerCurKey)); - $parsed .= $this -> preprocessText(self::explodeString($markup), $innerArg, true, $depth + 1); + if ($this->MAX_INCLUDE_DEPTH < 0 || $depth < $this->MAX_INCLUDE_DEPTH) { + $markup = trim($this->backend->getTemplateMarkup($innerCurKey)); + $parsed .= $this->preprocessText($this->explodeString($markup), $innerArg, true, $depth + 1); } } @@ -252,8 +237,8 @@ private function preprocessText(array $textChars, array $arg = [], bool $include } /* Argument splitting -- A dumber, non-recursiver version of what is used in ParseInline() */ - if ($child -> hasArgs && ($child -> argLimit == 0 || $child -> argLimit > count($innerArg))) { - if (self::tagIsAt($child -> argSep, $textChars, $i)) { + if ($child->hasArgs && ($child->argLimit == 0 || $child->argLimit > count($innerArg))) { + if ($this->tagIsAt($child->argSep, $textChars, $i)) { /* Hit argument separator */ if ($innerCurKey == '') { array_push($innerArg, $innerBuffer); @@ -262,13 +247,13 @@ private function preprocessText(array $textChars, array $arg = [], bool $include } $innerCurKey = ''; // Reset key $innerBuffer = ''; // Reset parsed values - $i += count($child -> argSep) - 1; + $i += count($child->argSep) - 1; $innerHit = true; - } elseif ($innerCurKey == '' && self::tagIsAt($child -> argNameSep, $textChars, $i)) { + } elseif ($innerCurKey == '' && $this->tagIsAt($child->argNameSep, $textChars, $i)) { /* Hit name/argument splitter */ $innerCurKey = $innerBuffer; // Set key $innerBuffer = ''; // Reset parsed values - $i += count($child -> argNameSep) - 1; + $i += count($child->argNameSep) - 1; $innerHit = true; } } @@ -291,7 +276,7 @@ private function preprocessText(array $textChars, array $arg = [], bool $include return $parsed; } - private static function tagIsAt(array $tag, array $textChars, int $position) + private function tagIsAt(array $tag, array $textChars, int $position) { if ($textChars[$position] != $tag[0]) { // Fast exit for common case @@ -321,15 +306,15 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) { /* Quick escape if we've run into a table */ $inParagraph = false; - if ($token == '' || !isset(self::$inline[$token])) { + if ($token == '' || !isset($this->inline[$token])) { /* Default to empty token if none is set (these have no end token, ensuring there will be no remainder after this runs) */ if ($token == 'p') { /* Blocks of text here need to be encapsualted in paragraph tags */ $inParagraph = true; } - $inlineElement = self::$inline['nothing']; + $inlineElement = $this->inline['nothing']; } else { - $inlineElement = self::$inline[$token]; + $inlineElement = $this->inline[$token]; } $parsed = ''; // For completely parsed text @@ -344,7 +329,7 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) /* Looping through each character */ $hit = false; // State so that the last part knows whether to simply append this as an unmatched character $c = $textChars[$i]; - if (!isset(self::$inlineChars[$c])) { + if (!isset($this->inlineChars[$c])) { // Fast exit for characters that do not start a tag. // TODO Could work faster if we didn't concatenate each character $buffer .= $c; @@ -352,18 +337,18 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) } /* Looking for this element's close-token */ - if (self::tagIsAt($inlineElement -> endTag, $textChars, $i)) { + if ($this->tagIsAt($inlineElement->endTag, $textChars, $i)) { /* Hit a close tag: Stop parsing here, return the remainder, and let the parent continue */ - $start = $i + count($inlineElement -> endTag); + $start = $i + count($inlineElement->endTag); - if ($inlineElement -> hasArgs) { + if ($inlineElement->hasArgs) { /* Handle arguments if needed */ if ($curKey == '') { array_push($arg, $buffer); } else { $arg[$curKey] = $buffer; } - $buffer = self::$backend -> renderWithArgs($token, $arg); + $buffer = $this->backend->renderWithArgs($token, $arg); } /* Clean up and quit */ @@ -372,8 +357,8 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) } /* Next priority is looking for this element's agument tokens if applicable */ - if ($inlineElement -> hasArgs && ($inlineElement -> argLimit == 0 || $inlineElement -> argLimit > count($arg))) { - if (self::tagIsAt($inlineElement -> argSep, $textChars, $i)) { + if ($inlineElement->hasArgs && ($inlineElement->argLimit == 0 || $inlineElement->argLimit > count($arg))) { + if ($this->tagIsAt($inlineElement->argSep, $textChars, $i)) { /* Hit argument separator */ if ($curKey == '') { array_push($arg, $buffer); @@ -384,46 +369,46 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) $curKey = ''; // Reset key $buffer = ''; // Reset parsed values /* Handle position properly */ - $i += count($inlineElement -> argSep) - 1; + $i += count($inlineElement->argSep) - 1; $hit = true; - } elseif ($curKey == '' && self::tagIsAt($inlineElement -> argNameSep, $textChars, $i)) { + } elseif ($curKey == '' && $this->tagIsAt($inlineElement->argNameSep, $textChars, $i)) { /* Hit name/argument splitter */ $curKey = $buffer; // Set key $buffer = ''; // Reset parsed values /* Handle position properly */ - $i += count($inlineElement -> argNameSep) - 1; + $i += count($inlineElement->argNameSep) - 1; $hit = true; } } - + /* Looking for new open-tokens */ - if (isset(self::$inlineLookup[$c])) { + if (isset($this->inlineLookup[$c])) { /* There are inline elements which start with this character. Check each one,.. */ - foreach (self::$inlineLookup[$c] as $key => $child) { - if (!$hit && self::tagIsAt($child -> startTag, $textChars, $i)) { + foreach ($this->inlineLookup[$c] as $key => $child) { + if (!$hit && $this->tagIsAt($child->startTag, $textChars, $i)) { /* Hit a symbol. Parse it and keep going after the result */ - $start = $i + count($child -> startTag); + $start = $i + count($child->startTag); /* Regular, recursively-parsed element */ - $result = $this -> parseInline($textChars, $key, $start); - $buffer .= self::$backend -> encapsulateElement($key, $result['parsed']); + $result = $this->parseInline($textChars, $key, $start); + $buffer .= $this->backend->encapsulateElement($key, $result['parsed']); $i = $result['remainderIdx'] - 1; $hit = true; } } } - + if (!$hit) { if ($c == "\n" && $i < $len - 1) { - if (self::tagIsAt(self::$tableStart -> startTag, $textChars, $i + 1)) { + if ($this->tagIsAt($this->tableStart->startTag, $textChars, $i + 1)) { $hit = true; - $start = $i + 1 + count(self::$tableStart -> startTag); + $start = $i + 1 + count($this->tableStart->startTag); $key = 'table'; } else { /* Check for non-table line-based stuff coming up next, each time \n is found */ $next = $textChars[$i + 1]; - foreach (self::$lineBlock as $key => $block) { - foreach ($block -> startChar as $char) { + foreach ($this->lineBlock as $key => $block) { + foreach ($block->startChar as $char) { if (!$hit && $next == $char) { $hit = true; $start = $i + 1; @@ -432,18 +417,18 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) } } } - + if ($hit) { /* Go over what's been found */ if ($key == 'table') { - $result = $this -> parseTable($textChars, $start); + $result = $this->parseTable($textChars, $start); } else { /* Let parseLineBlock take care of this on a per-line basis */ - $result = $this -> parseLineBlock($textChars, $key, $start); + $result = $this->parseLineBlock($textChars, $key, $start); } if ($buffer != '') { /* Something before this was part of a paragraph */ - $parsed .= self::$backend -> encapsulateElement('paragraph', $buffer); + $parsed .= $this->backend->encapsulateElement('paragraph', $buffer); $inParagraph == true; } $buffer = ""; @@ -451,7 +436,7 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) $parsed .= $result['parsed']; $i = $result['remainderIdx'] - 1; } - + /* Other \n-related things if it wasn't as exciting as above */ if ($buffer != '' && !$hit) { /* Put in a space if it is not going to be the first thing added. */ @@ -462,31 +447,31 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) $buffer .= $c; } } - + if ($token == 'td') { /* We only get here from table syntax if something else was being parsed, so we can quit here */ $parsed = $buffer; return array('parsed' => $parsed, 'remainderIdx' => $i); } } - + /* Need to throw argument-driven items at the backend first here */ - if ($inlineElement -> hasArgs) { + if ($inlineElement->hasArgs) { if ($curKey == '') { array_push($arg, $buffer); } else { $arg[$curKey] = $buffer; } - $buffer = self::$backend -> renderWithArgs($token, $arg); + $buffer = $this->backend->renderWithArgs($token, $arg); } - + if ($inParagraph && $buffer != '') { /* Something before this was part of a paragraph */ - $parsed .= self::$backend -> encapsulateElement('paragraph', $buffer); + $parsed .= $this->backend->encapsulateElement('paragraph', $buffer); } else { $parsed .= $buffer; } - + return array('parsed' => $parsed, 'remainderIdx' => $i); } @@ -499,13 +484,13 @@ private function parseInline(array $textChars, string $token = '', $idxFrom = 0) private function parseLineBlock(array $textChars, string $token, $fromIdx = 0) { /* Block element we are using */ - $lineBlockElement = self::$lineBlock[$token]; - + $lineBlockElement = $this->lineBlock[$token]; + // Loop through lines $lineStart = $fromIdx; $list = []; - while (($lineLen = self::getLineLen($textChars, $lineStart)) !== false) { - $startTokenLen = self::countChar($lineBlockElement -> startChar, $textChars, $lineStart, $lineBlockElement -> limit); + while (($lineLen = $this->getLineLen($textChars, $lineStart)) !== false) { + $startTokenLen = $this->countChar($lineBlockElement->startChar, $textChars, $lineStart, $lineBlockElement->limit); if ($startTokenLen === 0) { /* Wind back to include "\n" if the next line is not a list item. This is not expected * to trigger on the first iteration, since line-block tags were found for calling this method. @@ -515,24 +500,24 @@ private function parseLineBlock(array $textChars, string $token, $fromIdx = 0) } else { $char = $textChars[$lineStart + $startTokenLen - 1]; $endTokenLen = 0; - if (count($lineBlockElement -> endChar) > 0) { + if (count($lineBlockElement->endChar) > 0) { /* Also need to cut off end letters, such as in == Heading == */ - $endTokenLen = self::countCharReverse($lineBlockElement -> endChar, $textChars, $lineStart + $startTokenLen, $lineStart + $lineLen - 1); + $endTokenLen = $this->countCharReverse($lineBlockElement->endChar, $textChars, $lineStart + $startTokenLen, $lineStart + $lineLen - 1); } /* Remainder of the line */ $lineChars = array_slice($textChars, $lineStart + $startTokenLen, $lineLen - $startTokenLen - $endTokenLen); - $result = $this -> parseInline($lineChars); + $result = $this->parseInline($lineChars); $list[] = array('depth' => $startTokenLen, 'item' => $result['parsed'], 'char' => $char); } /* Move along to start of next line */ $lineStart += $lineLen + 1; } - if ($lineBlockElement -> nestTags) { + if ($lineBlockElement->nestTags) { /* Hierachy-ify nestable lists */ - $list = self::makeList($list); + $list = $this->makeList($list); } - $parsed = self::$backend -> renderLineBlock($token, $list); + $parsed = $this->backend->renderLineBlock($token, $list); return array('parsed' => $parsed, 'remainderIdx' => $lineStart); } @@ -544,27 +529,27 @@ private function parseLineBlock(array $textChars, string $token, $fromIdx = 0) */ private function parseTable(array $textChars, $fromIdx = 0) { - $lineLen = self::getLineLen($textChars, $fromIdx); + $lineLen = $this->getLineLen($textChars, $fromIdx); $propertiesChars = array_slice($textChars, $fromIdx, $lineLen); $table['properties'] = implode($propertiesChars); $table['row'] = []; $lineStart = $lineLen + 1; - while (($lineLen = self::getLineLen($textChars, $lineStart)) !== false) { - if (self::tagIsAt(self::$tableStart -> endTag, $textChars, $lineStart)) { + while (($lineLen = $this->getLineLen($textChars, $lineStart)) !== false) { + if ($this->tagIsAt($this->tableStart->endTag, $textChars, $lineStart)) { $lineStart += $lineLen + 1; break; } $hit = false; - foreach (self::$tableBlock as $token => $block) { + foreach ($this->tableBlock as $token => $block) { /* Looking for matching per-line elements */ - if (!$hit && self::tagIsAt($block -> lineStart, $textChars, $lineStart)) { + if (!$hit && $this->tagIsAt($block->lineStart, $textChars, $lineStart)) { $hit = true; break; } } if ($hit) { /* Move cursor along to skip the token */ - $tokenLen = count($block -> lineStart); + $tokenLen = count($block->lineStart); $contentStart = $lineStart + $tokenLen; $contentLen = $lineLen - $tokenLen; @@ -574,7 +559,7 @@ private function parseTable(array $textChars, $fromIdx = 0) $tmpRow = array('properties' => '', 'col' => []); } /* Clobber the remaining text together and throw it to the cell parser */ - $result = $this -> parseTableCells($token, $textChars, $contentStart, $tmpRow['col']); + $result = $this->parseTableCells($token, $textChars, $contentStart, $tmpRow['col']); $lineStart = $result['remainderIdx']; $lineLen = -1; $tmpRow['col'] = $result['col']; @@ -598,11 +583,11 @@ private function parseTable(array $textChars, $fromIdx = 0) /* Tack on the last row */ $table['row'][] = $tmpRow; } - $parsed = self::$backend -> renderTable($table); + $parsed = $this->backend->renderTable($table); return array('parsed' => $parsed, 'remainderIdx' => $lineStart); } - private static function getLineLen(array $textChars, int $position) + private function getLineLen(array $textChars, int $position) { /* Return number of characters in line, or FALSE if the string is depleted */ for ($i = $position; $i < count($textChars); $i++) { @@ -623,7 +608,7 @@ private static function getLineLen(array $textChars, int $position) */ private function parseTableCells(string $token, array $textChars, int $from, array $colsSoFar) { - $tableElement = self::$tableBlock[$token]; + $tableElement = $this->tableBlock[$token]; $len = count($textChars); $tmpCol = array('arg' => [], 'content' => '', 'token' => $token); @@ -635,23 +620,23 @@ private function parseTableCells(string $token, array $textChars, int $from, arr $hit = false; /* We basically detect the start of any inline/lineblock/table elements and, knowing that the inline parser knows how to handle them, throw then wayward */ $c = $textChars[$i]; - if (isset(self::$inlineLookup[$c])) { + if (isset($this->inlineLookup[$c])) { /* There are inline elements which start with this character. Check each one,.. */ - foreach (self::$inlineLookup[$c] as $key => $child) { - if (!$hit && self::tagIsAt($child -> startTag, $textChars, $i)) { + foreach ($this->inlineLookup[$c] as $key => $child) { + if (!$hit && $this->tagIsAt($child->startTag, $textChars, $i)) { $hit = true; } } } if ($c == "\n") { - if (self::tagIsAt(self::$tableStart -> startTag, $textChars, $i + 1)) { + if ($this->tagIsAt($this->tableStart->startTag, $textChars, $i + 1)) { /* Table is coming up */ $hit = true; } else { - /* LineBlocks like lists and headings*/ + /* LineBlocks like lists and headings */ $next = $textChars[$i + 1]; - foreach (self::$lineBlock as $key => $block) { - foreach ($block -> startChar as $char) { + foreach ($this->lineBlock as $key => $block) { + foreach ($block->startChar as $char) { if (!$hit && $next == $char) { $hit = true; break 2; @@ -664,13 +649,13 @@ private function parseTableCells(string $token, array $textChars, int $from, arr if ($hit) { /* Parse whatever it is and return here */ $start = $i; - $result = $this -> parseInline($textChars, 'td', $start); + $result = $this->parseInline($textChars, 'td', $start); $buffer .= $result['parsed']; // TODO was -1 before, seems to work well though $i = $result['remainderIdx']; } - if (!$hit && self::tagIsAt($tableElement -> inlinesep, $textChars, $i)) { + if (!$hit && $this->tagIsAt($tableElement->inlinesep, $textChars, $i)) { /* Got column separator, so this column is now finished */ $tmpCol['content'] = $buffer; $colsSoFar[] = $tmpCol; @@ -679,16 +664,16 @@ private function parseTableCells(string $token, array $textChars, int $from, arr $tmpCol = array('arg' => [], 'content' => '', 'token' => $token); $buffer = ''; $hit = true; - $i += count($tableElement -> inlinesep) - 1; + $i += count($tableElement->inlinesep) - 1; $argCount = 0; } - if (!$hit && $argCount < ($tableElement -> limit) && self::tagIsAt($tableElement -> argsep, $textChars, $i)) { + if (!$hit && $argCount < ($tableElement->limit) && $this->tagIsAt($tableElement->argsep, $textChars, $i)) { /* Got argument separator. Shift off the last argument */ $tmpCol['arg'][] = $buffer; $buffer = ''; $hit = true; - $i += count($tableElement -> argsep) - 1; + $i += count($tableElement->argsep) - 1; $argCount++; } @@ -696,8 +681,8 @@ private function parseTableCells(string $token, array $textChars, int $from, arr $c = $textChars[$i]; if ($c == "\n") { /* Checking that the next line isn't starting a different element of the table */ - foreach (self::$tableBlock as $key => $block) { - if (self::tagIsAt($block -> lineStart, $textChars, $i + 1)) { + foreach ($this->tableBlock as $key => $block) { + if ($this->tagIsAt($block->lineStart, $textChars, $i + 1)) { /* Next line is more table syntax. bail otu and let something else handle it */ break 2; } @@ -713,7 +698,8 @@ private function parseTableCells(string $token, array $textChars, int $from, arr $start = $i + 1; return array('col' => $colsSoFar, 'remainderIdx' => $start); } - private static function countChar(array $chars, array $text, int $position, int $max = 0) + + private function countChar(array $chars, array $text, int $position, int $max = 0) { $i = 0; while ($i < $max && array_search($text[$position + $i], $chars) !== false) { @@ -722,7 +708,7 @@ private static function countChar(array $chars, array $text, int $position, int return $i; } - private static function countCharReverse(array $chars, array $text, int $min, int $position) + private function countCharReverse(array $chars, array $text, int $min, int $position) { $i = 0; while (($position - $i) > $min && array_search($text[$position - $i], $chars) !== false) { @@ -734,19 +720,19 @@ private static function countCharReverse(array $chars, array $text, int $min, in /** * Create a list from what we found in parseLineBlock(), returning all elements. */ - private static function makeList(array $lines) + private function makeList(array $lines) { - $list = self::findChildren($lines, 0, -1); + $list = $this->findChildren($lines, 0, -1); return $list['child']; } /** * Recursively nests list elements inside eachother, forming a hierachy to traverse when rendering */ - private static function findChildren(array $lines, $depth, $minKey) + private function findChildren(array $lines, $depth, $minKey) { - $children = []; - $not = []; + $children = []; + $not = []; foreach ($lines as $key => $line) { /* Loop through for candidates */ @@ -763,7 +749,7 @@ private static function findChildren(array $lines, $depth, $minKey) /* For each child, list its children */ foreach ($children as $key => $child) { if (isset($children[$key])) { - $result = self::findChildren($children, $child['depth'], $key); + $result = $this->findChildren($children, $child['depth'], $key); $children[$key]['child'] = $result['child']; /* We know that all of this list's children are NOT children of this item (directly), so remove them from our records. */ @@ -782,4 +768,5 @@ private static function findChildren(array $lines, $depth, $minKey) return array('child' => $children, 'not' => $not); } + } diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index 88935d9..02356d0 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -1,12 +1,30 @@ sut = new WikitextParser(); + } + + protected function assertParsingEquals(string $expected, string $wikitext): void + { + $this->assertEquals($expected, $this->sut->parse($wikitext)); + } + public function testParse() { - $expected = "

Testing 1 2 3

\n"; - $actual = WikitextParser::parse("Testing 1 2 3"); - $this -> assertEquals($expected, $actual); + $this->assertParsingEquals("

Testing 1 2 3

\n", "Testing 1 2 3"); + } + + public function testLink() + { + $this->assertParsingEquals("

Link : yolo

\n", "Link : [[yolo]]"); } + } From c36ca615be3d0de3c01a42f29f9627301196465d Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 18:56:36 +0100 Subject: [PATCH 02/20] abstracting html enderer --- src/Mike42/Wikitext/DefaultParserBackend.php | 92 ++++++++++---------- src/Mike42/Wikitext/HtmlRenderer.php | 18 ++++ 2 files changed, 66 insertions(+), 44 deletions(-) create mode 100644 src/Mike42/Wikitext/HtmlRenderer.php diff --git a/src/Mike42/Wikitext/DefaultParserBackend.php b/src/Mike42/Wikitext/DefaultParserBackend.php index ae69e6b..8375923 100644 --- a/src/Mike42/Wikitext/DefaultParserBackend.php +++ b/src/Mike42/Wikitext/DefaultParserBackend.php @@ -1,13 +1,16 @@ renderList($token, $list); + return $this->renderList($token, $list); } public function renderUl($token, $list) { - return $this -> renderList($token, $list); + return $this->renderList($token, $list); } public function renderDl($token, $list) { - return $this -> renderList($token, $list); + return $this->renderList($token, $list); } public function renderH($token, $headings) @@ -79,7 +82,7 @@ public function renderH($token, $headings) $outp = ""; foreach ($headings as $heading) { $tag = "h" . $heading['depth']; - $outp .= "<$tag>".$heading['item']."\n"; + $outp .= "<$tag>" . $heading['item'] . "\n"; } return $outp; } @@ -91,7 +94,7 @@ public function renderPre($token, $lines) $outpline[] = $line['item']; } - return "
".implode("\n", $outpline)."
"; + return "
" . implode("\n", $outpline) . "
"; } /** @@ -109,7 +112,7 @@ public function renderList($token, $list, $expectedDepth = 1) foreach ($list as $item) { if ($token == 'dl') { - $subtoken = $item['char'] == ";"? "dt": "dd"; + $subtoken = $item['char'] == ";" ? "dt" : "dd"; } $outp .= "<$subtoken>"; $diff = $item['depth'] - $expectedDepth; @@ -121,7 +124,7 @@ public function renderList($token, $list, $expectedDepth = 1) $outp .= $item['item']; if (count($item['child']) > 0) { /* Add children if applicable */ - $outp .= $this -> renderList($token, $item['child'], $item['depth'] + 1); + $outp .= $this->renderList($token, $item['child'], $item['depth'] + 1); } if ($diff > 0) { /* Close above extra encapsulation if applicable */ @@ -162,14 +165,14 @@ public function renderLinkInternal($arg) } } - $info = array( 'url' => $destination, /* You should override getInternalLinkInfo() to set this better according to your application. */ - 'title' => $destination, /* Eg [[foo:bar]] links to "foo:bar". */ - 'namespace' => '', /* Eg [[foo:bar]] is in namespace 'foo' */ - 'target' => $destination, /* Eg [[foo:bar]] has the target "bar" within the namespace. */ - 'namespaceignore' => false, /* eg [[:File:foo.png]], link to the image don't include it */ - 'caption' => $caption, /* The link caption eg [[foo:bar|baz]] has the caption 'baz' */ - 'exists' => true, /* Causes class="new" for making red-links */ - 'external' => false); + $info = array('url' => $destination, /* You should override getInternalLinkInfo() to set this better according to your application. */ + 'title' => $destination, /* Eg [[foo:bar]] links to "foo:bar". */ + 'namespace' => '', /* Eg [[foo:bar]] is in namespace 'foo' */ + 'target' => $destination, /* Eg [[foo:bar]] has the target "bar" within the namespace. */ + 'namespaceignore' => false, /* eg [[:File:foo.png]], link to the image don't include it */ + 'caption' => $caption, /* The link caption eg [[foo:bar|baz]] has the caption 'baz' */ + 'exists' => true, /* Causes class="new" for making red-links */ + 'external' => false); /* Attempt to deduce namespaces */ if ($destination == '') { @@ -191,26 +194,26 @@ public function renderLinkInternal($arg) $info['target'] = substr($destination, $split, strlen($destination) - $split); /* Look up in default interwiki table */ - if ($this -> interwiki == false) { + if ($this->interwiki == false) { /* Load as needed */ - $this -> loadInterwikiLinks(); + $this->loadInterwikiLinks(); } if ($info['namespace'] == 'file') { /* Render an image instead of a link if requested */ $info['url'] = $info['target']; $info['caption'] = ''; - return $this -> renderFile($info, $arg); - } else if (isset($this -> interwiki[$info['namespace']])) { + return $this->renderFile($info, $arg); + } else if (isset($this->interwiki[$info['namespace']])) { /* We have a known namespace */ - $site = $this -> interwiki[$info['namespace']]; + $site = $this->interwiki[$info['namespace']]; $info['url'] = str_replace("$1", $info['target'], $site); } } /* Allow the local app to contribute to link properties */ - $info = $this -> getInternalLinkInfo($info); - return "".$info['caption'].""; + $info = $this->getInternalLinkInfo($info); + return "" . $info['caption'] . ""; } public function renderFile($info, $arg) @@ -298,7 +301,7 @@ public function renderFile($info, $arg) } } - $info = $this -> getImageInfo($info); + $info = $this->getImageInfo($info); if ($info['namespaceignore'] || !$info['exists']) { /* Only link to the image, do not display it */ @@ -306,7 +309,7 @@ public function renderFile($info, $arg) $info['caption'] = $info['target']; } /* Construct link */ - return "".$info['caption'].""; + return "" . $info['caption'] . ""; } else { $dend = $dstart = ""; if (isset($info['thumbnail']) || isset($info['frame'])) { @@ -319,14 +322,14 @@ public function renderFile($info, $arg) } $dstart = "
"; if ($info['caption'] != '') { - $dend .= "
" . htmlspecialchars($info['caption']) . "
"; + $dend .= "
" . htmlspecialchars($info['caption']) . "
"; } $dend .= "
"; } /* Construct link */ - return "$dstart\"".htmlspecialchars($info['title']).$dend"; + return "$dstart\""$dend"; } - + break; default: /* Something unsupported */ @@ -352,18 +355,18 @@ public function getInternalLinkInfo($info) public function loadInterwikiLinks() { - if ($this -> interwiki != false) { + if ($this->interwiki != false) { /* Use loaded interwiki links if they exist */ return; } - $this -> interwiki = array(); + $this->interwiki = array(); $json = file_get_contents(__DIR__ . "/interwiki.json"); /* Unserialize data and load into associative array for easy lookup */ $arr = json_decode($json); - foreach ($arr -> query -> interwikimap as $site) { - if (isset($site -> prefix) && isset($site -> url)) { - $this -> interwiki[$site -> prefix] = $site -> url; + foreach ($arr->query->interwikimap as $site) { + if (isset($site->prefix) && isset($site->url)) { + $this->interwiki[$site->prefix] = $site->url; } } } @@ -381,7 +384,7 @@ public function renderLinkExternal($arg) if (isset($arg[1])) { $caption = $arg[1]; } - return "".$caption.""; + return "" . $caption . ""; } /** @@ -392,7 +395,7 @@ public function renderLinkExternal($arg) */ public function encapsulateBold($text) { - return "".$text.""; + return "" . $text . ""; } /** @@ -403,12 +406,12 @@ public function encapsulateBold($text) */ public function encapsulateItalic($text) { - return "".$text.""; + return "" . $text . ""; } public function encapsulateParagraph($text) { - return "

".$text."

\n"; + return "

" . $text . "

\n"; } /** @@ -419,14 +422,14 @@ public function renderTable($table) if ($table['properties'] == '') { $outp = "\n"; } else { - $outp = "
\n"; + $outp = "
\n"; } foreach ($table['row'] as $row) { - $outp .= $this -> renderRow($row); + $outp .= $this->renderRow($row); } - return $outp."
\n"; + return $outp . "\n"; } /** @@ -438,17 +441,17 @@ public function renderRow($row) if ($row['properties'] == '') { $outp = "\n"; } else { - $outp = "\n"; + $outp = "\n"; } foreach ($row['col'] as $col) { /* Show column with or without attributes */ if (count($col['arg']) != 0) { - $outp .= "<". $col['token']. " " . trim($col['arg'][0]) . ">"; + $outp .= "<" . $col['token'] . " " . trim($col['arg'][0]) . ">"; } else { - $outp .= "<". $col['token']. ">"; + $outp .= "<" . $col['token'] . ">"; } - $outp .= $col['content']."\n"; + $outp .= $col['content'] . "\n"; } return $outp . "\n"; @@ -463,4 +466,5 @@ public function getTemplateMarkup($template) { return "[[$template]]"; } + } diff --git a/src/Mike42/Wikitext/HtmlRenderer.php b/src/Mike42/Wikitext/HtmlRenderer.php new file mode 100644 index 0000000..7041fb2 --- /dev/null +++ b/src/Mike42/Wikitext/HtmlRenderer.php @@ -0,0 +1,18 @@ + Date: Tue, 9 Nov 2021 19:00:58 +0100 Subject: [PATCH 03/20] implementing design pattern : template method --- src/Mike42/Wikitext/DefaultParserBackend.php | 455 +------------------ src/Mike42/Wikitext/HtmlRenderer.php | 455 ++++++++++++++++++- 2 files changed, 459 insertions(+), 451 deletions(-) diff --git a/src/Mike42/Wikitext/DefaultParserBackend.php b/src/Mike42/Wikitext/DefaultParserBackend.php index 8375923..195fd1b 100644 --- a/src/Mike42/Wikitext/DefaultParserBackend.php +++ b/src/Mike42/Wikitext/DefaultParserBackend.php @@ -8,463 +8,18 @@ namespace Mike42\Wikitext; +/** + * Concrete html renderer example + */ class DefaultParserBackend extends HtmlRenderer { - private $interwiki; - - /** - * Process an element which has arguments. Links, lists and templates fall under this category - * - * @param string $elementName - * @param string $arg - */ - public function renderWithArgs($elementName, $arg) - { - $fn = array($this, 'render' . ucfirst($elementName)); - - if (is_callable($fn)) { - /* If a function is defined to handle this, use it */ - return call_user_func_array($fn, array($arg)); - } else { - return $arg[0]; - } - } - - /** - * Encapsulate inline elements - * - * @param string $text parsed text contained within this element - * @param string $elementName the name of the element - * @return string Correct markup for this element - */ - public function encapsulateElement($elementName, $text) - { - $fn = array($this, 'encapsulate' . ucfirst($elementName)); - - if (is_callable($fn)) { - /* If a function is defined to encapsulate this, use it */ - return call_user_func_array($fn, array($text)); - } else { - return $text; - } - } - - public function renderLineBlock($elementName, $list) - { - $fn = array($this, 'render' . ucfirst($elementName)); - - if (is_callable($fn)) { - /* If a function is defined to encapsulate this, use it */ - return call_user_func_array($fn, array($elementName, $list)); - } else { - return $elementName; - } - } - - public function renderOl($token, $list) - { - return $this->renderList($token, $list); - } - - public function renderUl($token, $list) - { - return $this->renderList($token, $list); - } - - public function renderDl($token, $list) - { - return $this->renderList($token, $list); - } - - public function renderH($token, $headings) - { - $outp = ""; - foreach ($headings as $heading) { - $tag = "h" . $heading['depth']; - $outp .= "<$tag>" . $heading['item'] . "\n"; - } - return $outp; - } - - public function renderPre($token, $lines) - { - $outpline = array(); - foreach ($lines as $line) { - $outpline[] = $line['item']; - } - - return "
" . implode("\n", $outpline) . "
"; - } - /** - * Render list and any sub-lists recursively - * - * @param string $token The type of list (expect ul, ol, dl) - * @param mixed $list The hierachy representing this list - * @return string HTML markup for the list + * @inheritDoc */ - public function renderList($token, $list, $expectedDepth = 1) - { - $outp = ''; - $subtoken = "li"; - $outp .= "<$token>\n"; - - foreach ($list as $item) { - if ($token == 'dl') { - $subtoken = $item['char'] == ";" ? "dt" : "dd"; - } - $outp .= "<$subtoken>"; - $diff = $item['depth'] - $expectedDepth; - /* Some items are undented unusually far .. */ - if ($diff > 0) { - $outp .= str_repeat("<$token><$subtoken>", $diff); - } - /* Caption of this item */ - $outp .= $item['item']; - if (count($item['child']) > 0) { - /* Add children if applicable */ - $outp .= $this->renderList($token, $item['child'], $item['depth'] + 1); - } - if ($diff > 0) { - /* Close above extra encapsulation if applicable */ - $outp .= str_repeat("", $diff); - } - $outp .= "\n"; - } - $outp .= "\n"; - return $outp; - } - - /** - * Default rendering of [[link]] or [[link|foo]] - * - * @param string $destination page name we are linking to - * @param string $caption Caption of this link (can inlude parsed wikitext) - * @return string HTML markup for the link - */ - public function renderLinkInternal($arg) - { - /* Figure out properties based on arguments */ - if (isset($arg[0])) { - $destination = $arg[0]; - } - if (isset($arg[1])) { - $caption = $arg[1]; - } - - /* Compensate for missing values */ - if (isset($destination) && !isset($caption)) { - $caption = $destination; // Fill in caption = destination as default - } - if (!isset($destination)) { - if (isset($caption)) { - $destination = ""; // Empty link - } else { - return ""; // Empty link to nowhere (so skip it) - } - } - - $info = array('url' => $destination, /* You should override getInternalLinkInfo() to set this better according to your application. */ - 'title' => $destination, /* Eg [[foo:bar]] links to "foo:bar". */ - 'namespace' => '', /* Eg [[foo:bar]] is in namespace 'foo' */ - 'target' => $destination, /* Eg [[foo:bar]] has the target "bar" within the namespace. */ - 'namespaceignore' => false, /* eg [[:File:foo.png]], link to the image don't include it */ - 'caption' => $caption, /* The link caption eg [[foo:bar|baz]] has the caption 'baz' */ - 'exists' => true, /* Causes class="new" for making red-links */ - 'external' => false); - - /* Attempt to deduce namespaces */ - if ($destination == '') { - $split = false; - } else { - $split = strpos($destination, ":", 1); - } - - if (!$split === false) { - /* We have namespace */ - if (substr($destination, 0, 1) == ":") { /* Eg [[:category:foo]] */ - $info['namespaceignore'] = true; - $info['namespace'] = strtolower(substr($destination, 1, $split - 1)); - } else { - $info['namespace'] = strtolower(substr($destination, 0, $split)); - } - - $split++; - $info['target'] = substr($destination, $split, strlen($destination) - $split); - - /* Look up in default interwiki table */ - if ($this->interwiki == false) { - /* Load as needed */ - $this->loadInterwikiLinks(); - } - - if ($info['namespace'] == 'file') { - /* Render an image instead of a link if requested */ - $info['url'] = $info['target']; - $info['caption'] = ''; - return $this->renderFile($info, $arg); - } else if (isset($this->interwiki[$info['namespace']])) { - /* We have a known namespace */ - $site = $this->interwiki[$info['namespace']]; - $info['url'] = str_replace("$1", $info['target'], $site); - } - } - - /* Allow the local app to contribute to link properties */ - $info = $this->getInternalLinkInfo($info); - return "" . $info['caption'] . ""; - } - - public function renderFile($info, $arg) - { - $info['thumb'] = $info['url']; /* Default no no server-side thumbs */ - $info['class'] = ''; - $info['page'] = ''; - $info['caption'] = ''; - - $target = $info['target']; - $pos = strrpos($target, "."); - if ($pos === false) { - $ext = ''; - } else { - $pos++; - $ext = substr($target, $pos, strlen($target) - $pos); - } - - switch ($ext) { - case 'jpg': - case 'jpeg': - case 'png': - case 'gif': - /* Image flags parsed. From: http://www.mediawiki.org/wiki/Help:Images */ - - /* Named arguments */ - if (isset($arg['link'])) { // |link= - $info['url'] = $arg['link']; - $info['link'] = $arg['link']; - unset($arg['link']); - } - if (isset($arg['class'])) { // |class= - $info['class'] = $arg['class']; - unset($arg['class']); - } - if (isset($arg['alt'])) { // |alt= - $info['title'] = $arg['alt']; - unset($arg['alt']); - } - if (isset($arg['page'])) { // |alt= - $info['page'] = $arg['page']; - unset($arg['page']); - } - - foreach ($arg as $key => $item) { - /* Figure out unnamed arguments */ - if (is_numeric($key)) { /* Any unsupported named arguments will be ignored */ - if (substr($item, 0, -2) == 'px') { - /* Size */ - // TODO - } else { - /* Load recognised switches */ - switch ($item) { - case "frameless": - $info['frameless'] = true; - break; - case "border": - $info['border'] = true; - break; - case "frame": - $info['frame'] = true; - break; - case "thumb": - $info['thumbnail'] = true; - break; - case "thumbnail": - $info['thumbnail'] = true; - break; - case "left": - $info['left'] = true; - break; - case "right": - $info['right'] = true; - break; - case "center": - $info['center'] = true; - break; - case "none": - $info['none'] = true; - break; - default: - $info['caption'] = $item; - } - } - } - } - - $info = $this->getImageInfo($info); - - if ($info['namespaceignore'] || !$info['exists']) { - /* Only link to the image, do not display it */ - if ($info['caption'] == '') { - $info['caption'] = $info['target']; - } - /* Construct link */ - return "" . $info['caption'] . ""; - } else { - $dend = $dstart = ""; - if (isset($info['thumbnail']) || isset($info['frame'])) { - if (isset($info['right'])) { - $align = " tright"; - } elseif (isset($info['left'])) { - $align = " tleft"; - } else { - $align = ""; - } - $dstart = "
"; - if ($info['caption'] != '') { - $dend .= "
" . htmlspecialchars($info['caption']) . "
"; - } - $dend .= "
"; - } - /* Construct link */ - return "$dstart\""$dend"; - } - - break; - default: - /* Something unsupported */ - return "(unsupported media file)"; - } - } - - /** - * Method to override when providing extra info about an image (basically external URL and thumbnail path) - */ - public function getImageInfo($info) - { - return $info; - } - - /** - * Method to override when providing extra info about a link - */ - public function getInternalLinkInfo($info) + public function getInternalLinkInfo($info): array { return $info; } - public function loadInterwikiLinks() - { - if ($this->interwiki != false) { - /* Use loaded interwiki links if they exist */ - return; - } - - $this->interwiki = array(); - $json = file_get_contents(__DIR__ . "/interwiki.json"); - /* Unserialize data and load into associative array for easy lookup */ - $arr = json_decode($json); - foreach ($arr->query->interwikimap as $site) { - if (isset($site->prefix) && isset($site->url)) { - $this->interwiki[$site->prefix] = $site->url; - } - } - } - - /** - * Default rendering of [http://... link] or [http://foo] - * - * @param string $destination page name we are linking to - * @param string $caption Caption of this link (can inlude parsed wikitext) - * @return string HTML markup for the link - */ - public function renderLinkExternal($arg) - { - $caption = $destination = $arg[0]; - if (isset($arg[1])) { - $caption = $arg[1]; - } - return "" . $caption . ""; - } - - /** - * Default encapsulation for '''bold''' - * - * @param string $text Text to make bold - * @return string - */ - public function encapsulateBold($text) - { - return "" . $text . ""; - } - - /** - * Default encapsulation for ''italic'' - * - * @param string $text Text to make bold - * @return string - */ - public function encapsulateItalic($text) - { - return "" . $text . ""; - } - - public function encapsulateParagraph($text) - { - return "

" . $text . "

\n"; - } - - /** - * Generate HTML for a table - */ - public function renderTable($table) - { - if ($table['properties'] == '') { - $outp = "\n"; - } else { - $outp = "
\n"; - } - - foreach ($table['row'] as $row) { - $outp .= $this->renderRow($row); - } - - return $outp . "
\n"; - } - - /** - * Render a single row of a table - */ - public function renderRow($row) - { - /* Show row with or without attributes */ - if ($row['properties'] == '') { - $outp = "\n"; - } else { - $outp = "\n"; - } - - foreach ($row['col'] as $col) { - /* Show column with or without attributes */ - if (count($col['arg']) != 0) { - $outp .= "<" . $col['token'] . " " . trim($col['arg'][0]) . ">"; - } else { - $outp .= "<" . $col['token'] . ">"; - } - $outp .= $col['content'] . "\n"; - } - - return $outp . "\n"; - } - - /** - * Function to over-ride if you want to provide a mechanism for getting templates - * - * @param string $template - */ - public function getTemplateMarkup($template) - { - return "[[$template]]"; - } - } diff --git a/src/Mike42/Wikitext/HtmlRenderer.php b/src/Mike42/Wikitext/HtmlRenderer.php index 7041fb2..c63d540 100644 --- a/src/Mike42/Wikitext/HtmlRenderer.php +++ b/src/Mike42/Wikitext/HtmlRenderer.php @@ -14,5 +14,458 @@ */ abstract class HtmlRenderer { - + + private $interwiki; + + /** + * Process an element which has arguments. Links, lists and templates fall under this category + * + * @param string $elementName + * @param string $arg + */ + public function renderWithArgs($elementName, $arg) + { + $fn = array($this, 'render' . ucfirst($elementName)); + + if (is_callable($fn)) { + /* If a function is defined to handle this, use it */ + return call_user_func_array($fn, array($arg)); + } else { + return $arg[0]; + } + } + + /** + * Encapsulate inline elements + * + * @param string $text parsed text contained within this element + * @param string $elementName the name of the element + * @return string Correct markup for this element + */ + public function encapsulateElement($elementName, $text) + { + $fn = array($this, 'encapsulate' . ucfirst($elementName)); + + if (is_callable($fn)) { + /* If a function is defined to encapsulate this, use it */ + return call_user_func_array($fn, array($text)); + } else { + return $text; + } + } + + public function renderLineBlock($elementName, $list) + { + $fn = array($this, 'render' . ucfirst($elementName)); + + if (is_callable($fn)) { + /* If a function is defined to encapsulate this, use it */ + return call_user_func_array($fn, array($elementName, $list)); + } else { + return $elementName; + } + } + + public function renderOl($token, $list) + { + return $this->renderList($token, $list); + } + + public function renderUl($token, $list) + { + return $this->renderList($token, $list); + } + + public function renderDl($token, $list) + { + return $this->renderList($token, $list); + } + + public function renderH($token, $headings) + { + $outp = ""; + foreach ($headings as $heading) { + $tag = "h" . $heading['depth']; + $outp .= "<$tag>" . $heading['item'] . "\n"; + } + return $outp; + } + + public function renderPre($token, $lines) + { + $outpline = array(); + foreach ($lines as $line) { + $outpline[] = $line['item']; + } + + return "
" . implode("\n", $outpline) . "
"; + } + + /** + * Render list and any sub-lists recursively + * + * @param string $token The type of list (expect ul, ol, dl) + * @param mixed $list The hierachy representing this list + * @return string HTML markup for the list + */ + public function renderList($token, $list, $expectedDepth = 1) + { + $outp = ''; + $subtoken = "li"; + $outp .= "<$token>\n"; + + foreach ($list as $item) { + if ($token == 'dl') { + $subtoken = $item['char'] == ";" ? "dt" : "dd"; + } + $outp .= "<$subtoken>"; + $diff = $item['depth'] - $expectedDepth; + /* Some items are undented unusually far .. */ + if ($diff > 0) { + $outp .= str_repeat("<$token><$subtoken>", $diff); + } + /* Caption of this item */ + $outp .= $item['item']; + if (count($item['child']) > 0) { + /* Add children if applicable */ + $outp .= $this->renderList($token, $item['child'], $item['depth'] + 1); + } + if ($diff > 0) { + /* Close above extra encapsulation if applicable */ + $outp .= str_repeat("", $diff); + } + $outp .= "\n"; + } + $outp .= "\n"; + return $outp; + } + + /** + * Default rendering of [[link]] or [[link|foo]] + * + * @param string $destination page name we are linking to + * @param string $caption Caption of this link (can inlude parsed wikitext) + * @return string HTML markup for the link + */ + public function renderLinkInternal($arg) + { + /* Figure out properties based on arguments */ + if (isset($arg[0])) { + $destination = $arg[0]; + } + if (isset($arg[1])) { + $caption = $arg[1]; + } + + /* Compensate for missing values */ + if (isset($destination) && !isset($caption)) { + $caption = $destination; // Fill in caption = destination as default + } + if (!isset($destination)) { + if (isset($caption)) { + $destination = ""; // Empty link + } else { + return ""; // Empty link to nowhere (so skip it) + } + } + + $info = array('url' => $destination, /* You should override getInternalLinkInfo() to set this better according to your application. */ + 'title' => $destination, /* Eg [[foo:bar]] links to "foo:bar". */ + 'namespace' => '', /* Eg [[foo:bar]] is in namespace 'foo' */ + 'target' => $destination, /* Eg [[foo:bar]] has the target "bar" within the namespace. */ + 'namespaceignore' => false, /* eg [[:File:foo.png]], link to the image don't include it */ + 'caption' => $caption, /* The link caption eg [[foo:bar|baz]] has the caption 'baz' */ + 'exists' => true, /* Causes class="new" for making red-links */ + 'external' => false); + + /* Attempt to deduce namespaces */ + if ($destination == '') { + $split = false; + } else { + $split = strpos($destination, ":", 1); + } + + if (!$split === false) { + /* We have namespace */ + if (substr($destination, 0, 1) == ":") { /* Eg [[:category:foo]] */ + $info['namespaceignore'] = true; + $info['namespace'] = strtolower(substr($destination, 1, $split - 1)); + } else { + $info['namespace'] = strtolower(substr($destination, 0, $split)); + } + + $split++; + $info['target'] = substr($destination, $split, strlen($destination) - $split); + + /* Look up in default interwiki table */ + if ($this->interwiki == false) { + /* Load as needed */ + $this->loadInterwikiLinks(); + } + + if ($info['namespace'] == 'file') { + /* Render an image instead of a link if requested */ + $info['url'] = $info['target']; + $info['caption'] = ''; + return $this->renderFile($info, $arg); + } else if (isset($this->interwiki[$info['namespace']])) { + /* We have a known namespace */ + $site = $this->interwiki[$info['namespace']]; + $info['url'] = str_replace("$1", $info['target'], $site); + } + } + + /* Allow the local app to contribute to link properties */ + $info = $this->getInternalLinkInfo($info); + return "" . $info['caption'] . ""; + } + + public function renderFile($info, $arg) + { + $info['thumb'] = $info['url']; /* Default no no server-side thumbs */ + $info['class'] = ''; + $info['page'] = ''; + $info['caption'] = ''; + + $target = $info['target']; + $pos = strrpos($target, "."); + if ($pos === false) { + $ext = ''; + } else { + $pos++; + $ext = substr($target, $pos, strlen($target) - $pos); + } + + switch ($ext) { + case 'jpg': + case 'jpeg': + case 'png': + case 'gif': + /* Image flags parsed. From: http://www.mediawiki.org/wiki/Help:Images */ + + /* Named arguments */ + if (isset($arg['link'])) { // |link= + $info['url'] = $arg['link']; + $info['link'] = $arg['link']; + unset($arg['link']); + } + if (isset($arg['class'])) { // |class= + $info['class'] = $arg['class']; + unset($arg['class']); + } + if (isset($arg['alt'])) { // |alt= + $info['title'] = $arg['alt']; + unset($arg['alt']); + } + if (isset($arg['page'])) { // |alt= + $info['page'] = $arg['page']; + unset($arg['page']); + } + + foreach ($arg as $key => $item) { + /* Figure out unnamed arguments */ + if (is_numeric($key)) { /* Any unsupported named arguments will be ignored */ + if (substr($item, 0, -2) == 'px') { + /* Size */ + // TODO + } else { + /* Load recognised switches */ + switch ($item) { + case "frameless": + $info['frameless'] = true; + break; + case "border": + $info['border'] = true; + break; + case "frame": + $info['frame'] = true; + break; + case "thumb": + $info['thumbnail'] = true; + break; + case "thumbnail": + $info['thumbnail'] = true; + break; + case "left": + $info['left'] = true; + break; + case "right": + $info['right'] = true; + break; + case "center": + $info['center'] = true; + break; + case "none": + $info['none'] = true; + break; + default: + $info['caption'] = $item; + } + } + } + } + + $info = $this->getImageInfo($info); + + if ($info['namespaceignore'] || !$info['exists']) { + /* Only link to the image, do not display it */ + if ($info['caption'] == '') { + $info['caption'] = $info['target']; + } + /* Construct link */ + return "" . $info['caption'] . ""; + } else { + $dend = $dstart = ""; + if (isset($info['thumbnail']) || isset($info['frame'])) { + if (isset($info['right'])) { + $align = " tright"; + } elseif (isset($info['left'])) { + $align = " tleft"; + } else { + $align = ""; + } + $dstart = "
"; + if ($info['caption'] != '') { + $dend .= "
" . htmlspecialchars($info['caption']) . "
"; + } + $dend .= "
"; + } + /* Construct link */ + return "$dstart\""$dend"; + } + + break; + default: + /* Something unsupported */ + return "(unsupported media file)"; + } + } + + /** + * Method to override when providing extra info about an image (basically external URL and thumbnail path) + */ + public function getImageInfo($info) + { + return $info; + } + + /** + * Method to override when providing extra info about a link + */ + abstract public function getInternalLinkInfo($info): array; + + public function loadInterwikiLinks() + { + if ($this->interwiki != false) { + /* Use loaded interwiki links if they exist */ + return; + } + + $this->interwiki = array(); + $json = file_get_contents(__DIR__ . "/interwiki.json"); + /* Unserialize data and load into associative array for easy lookup */ + $arr = json_decode($json); + foreach ($arr->query->interwikimap as $site) { + if (isset($site->prefix) && isset($site->url)) { + $this->interwiki[$site->prefix] = $site->url; + } + } + } + + /** + * Default rendering of [http://... link] or [http://foo] + * + * @param string $destination page name we are linking to + * @param string $caption Caption of this link (can inlude parsed wikitext) + * @return string HTML markup for the link + */ + public function renderLinkExternal($arg) + { + $caption = $destination = $arg[0]; + if (isset($arg[1])) { + $caption = $arg[1]; + } + return "" . $caption . ""; + } + + /** + * Default encapsulation for '''bold''' + * + * @param string $text Text to make bold + * @return string + */ + public function encapsulateBold($text) + { + return "" . $text . ""; + } + + /** + * Default encapsulation for ''italic'' + * + * @param string $text Text to make bold + * @return string + */ + public function encapsulateItalic($text) + { + return "" . $text . ""; + } + + public function encapsulateParagraph($text) + { + return "

" . $text . "

\n"; + } + + /** + * Generate HTML for a table + */ + public function renderTable($table) + { + if ($table['properties'] == '') { + $outp = "\n"; + } else { + $outp = "
\n"; + } + + foreach ($table['row'] as $row) { + $outp .= $this->renderRow($row); + } + + return $outp . "
\n"; + } + + /** + * Render a single row of a table + */ + public function renderRow($row) + { + /* Show row with or without attributes */ + if ($row['properties'] == '') { + $outp = "\n"; + } else { + $outp = "\n"; + } + + foreach ($row['col'] as $col) { + /* Show column with or without attributes */ + if (count($col['arg']) != 0) { + $outp .= "<" . $col['token'] . " " . trim($col['arg'][0]) . ">"; + } else { + $outp .= "<" . $col['token'] . ">"; + } + $outp .= $col['content'] . "\n"; + } + + return $outp . "\n"; + } + + /** + * Function to over-ride if you want to provide a mechanism for getting templates + * + * @param string $template + */ + public function getTemplateMarkup($template) + { + return "[[$template]]"; + } + } From 423b9e50784139f7fd3a40ca36cbed6ea4c6251f Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 19:13:43 +0100 Subject: [PATCH 04/20] updating phpunit --- .phpunit.result.cache | 1 + composer.json | 2 +- composer.lock | 1203 +++++++++++++++++++++--------- phpunit.xml | 34 +- test/bootstrap.php | 6 - test/unit/WikitextParserTest.php | 2 +- 6 files changed, 870 insertions(+), 378 deletions(-) create mode 100644 .phpunit.result.cache delete mode 100644 test/bootstrap.php diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 0000000..bc55239 --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +{"version":1,"defects":[],"times":{"Mike42\\Wikitext\\DefaultParserBackendTest::testGetTemplateMarkup":0.003,"Mike42\\Wikitext\\WikitextParserTest::testParse":0.001,"Mike42\\Wikitext\\WikitextParserTest::testLink":0}} \ No newline at end of file diff --git a/composer.json b/composer.json index d89265a..b5b883a 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require-dev": { - "phpunit/phpunit": "^6.5", + "phpunit/phpunit": "^9.5", "squizlabs/php_codesniffer": "^3.1" }, "autoload": { diff --git a/composer.lock b/composer.lock index b419e74..802308d 100644 --- a/composer.lock +++ b/composer.lock @@ -1,43 +1,39 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "hash": "a00913e749d90ea2b813d6ec0576fe2a", - "content-hash": "93dc72667f8a7515d381a91ace710b0b", + "content-hash": "7283074a4ce86d5ef2f256a125fe6fb7", "packages": [], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1 || ^8.0" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^8.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" @@ -51,38 +47,55 @@ { "name": "Marco Pivetta", "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "homepage": "https://ocramius.github.io/" } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2015-06-14 21:17:01" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1 || ^8.0" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -105,32 +118,91 @@ "object", "object graph" ], - "time": "2017-10-19 19:58:43" + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.13.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "reference": "63a79e8daa781cac14e5195e63ed8ae231dd10fd", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2021-11-03T20:52:16+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", - "php": "^5.6 || ^7.0" + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -160,24 +232,24 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05 18:14:27" + "time": "2021-07-20T11:28:43+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "bae7c545bef187884426f042434e561ab1ddb182" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -207,39 +279,34 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05 17:38:23" + "time": "2021-02-23T14:00:09+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-2.x": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -261,44 +328,42 @@ "reflection", "static analysis" ], - "time": "2017-09-11 18:02:19" + "time": "2020-06-27T09:03:43+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.2.0", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "66465776cfc249844bde6d117abff1d22e06c2da" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/66465776cfc249844bde6d117abff1d22e06c2da", - "reference": "66465776cfc249844bde6d117abff1d22e06c2da", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.4" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -309,44 +374,46 @@ { "name": "Mike van Riel", "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-27 17:38:31" + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "*", + "psalm/phar": "^4.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -359,42 +426,43 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14 14:27:02" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2021-10-02T14:08:47+00:00" }, { "name": "phpspec/prophecy", - "version": "1.7.3", + "version": "1.14.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf" + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", - "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7" + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -422,44 +490,48 @@ "spy", "stub" ], - "time": "2017-11-24 13:59:53" + "time": "2021-09-10T09:02:12+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "5.3.0", + "version": "9.2.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1" + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/661f34d0bd3f1a7225ef491a70a020ad23a057a1", - "reference": "661f34d0bd3f1a7225ef491a70a020ad23a057a1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", + "reference": "cf04e88a2e3c56fc1a65488afd493325b4c1bc3e", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1" + "nikic/php-parser": "^4.13.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-xdebug": "^2.5.5" + "ext-pcov": "*", + "ext-xdebug": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "9.2-dev" } }, "autoload": { @@ -485,29 +557,38 @@ "testing", "xunit" ], - "time": "2017-12-06 09:29:45" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-10-30T08:01:38+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", + "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -522,7 +603,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -532,26 +613,44 @@ "filesystem", "iterator" ], - "time": "2017-11-27 13:52:08" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:57:25+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "phpunit/php-invoker", + "version": "3.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -568,37 +667,43 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", "keywords": [ - "template" + "process" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2015-06-21 13:50:34" + "time": "2020-09-28T05:58:55+00:00" }, { - "name": "phpunit/php-timer", - "version": "1.0.9", + "name": "phpunit/php-text-template", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -613,42 +718,47 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "timer" + "template" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-02-26 11:10:40" + "time": "2020-10-26T05:33:50+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "2.0.2", + "name": "phpunit/php-timer", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", "shasum": "" }, "require": { - "ext-tokenizer": "*", - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -663,65 +773,74 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "tokenizer" + "timer" + ], + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-11-27 05:48:46" + "time": "2020-10-26T13:16:10+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.4", + "version": "9.5.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c" + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b2f933d5775f9237369deaa2d2bfbf9d652be4c", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.3.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^2.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", - "sebastian/version": "^2.0.1" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3.4", + "sebastian/version": "^3.0.2" }, "require-dev": { - "ext-pdo": "*" + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" }, "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "ext-soap": "*", + "ext-xdebug": "*" }, "bin": [ "phpunit" @@ -729,12 +848,15 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "9.5-dev" } }, "autoload": { "classmap": [ "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -755,41 +877,94 @@ "testing", "xunit" ], - "time": "2017-12-10 08:06:19" + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-09-25T07:38:51+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.5", + "name": "sebastian/cli-parser", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "283b9f4f670e3a6fd6c4ff95c51a952eb5c75933" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/283b9f4f670e3a6fd6c4ff95c51a952eb5c75933", - "reference": "283b9f4f670e3a6fd6c4ff95c51a952eb5c75933", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.5" + "phpunit/phpunit": "^9.3" }, - "suggest": { - "ext-soap": "*" + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -808,38 +983,40 @@ "role": "lead" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "time": "2017-12-10 08:01:53" + "time": "2020-10-26T13:08:54+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -859,34 +1036,40 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04 06:30:41" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" }, { "name": "sebastian/comparator", - "version": "2.1.0", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158" + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0", - "sebastian/exporter": "^3.1" + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -899,6 +1082,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -910,10 +1097,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -923,27 +1106,34 @@ "compare", "equality" ], - "time": "2017-11-03 07:16:52" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:49:45+00:00" }, { - "name": "sebastian/diff", - "version": "2.0.1", + "name": "sebastian/complexity", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", "shasum": "" }, "require": { - "php": "^7.0" + "nikic/php-parser": "^4.7", + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { @@ -962,45 +1152,110 @@ ], "authors": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-08-03 08:09:46" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" }, { "name": "sebastian/environment", - "version": "3.1.0", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -1025,34 +1280,40 @@ "environment", "hhvm" ], - "time": "2017-07-01 08:51:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:52:38+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1065,6 +1326,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1073,17 +1338,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -1092,27 +1353,36 @@ "export", "exporter" ], - "time": "2017-04-03 13:19:02" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:24:23+00:00" }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "ext-dom": "*", + "phpunit/phpunit": "^9.3" }, "suggest": { "ext-uopz": "*" @@ -1120,7 +1390,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1143,34 +1413,93 @@ "keywords": [ "global state" ], - "time": "2017-04-27 15:39:26" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-06-11T13:31:12+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1190,32 +1519,38 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03 12:35:26" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1235,32 +1570,38 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29 09:07:27" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1273,14 +1614,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -1288,29 +1629,38 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03 06:23:57" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" }, { "name": "sebastian/resource-operations", - "version": "1.0.0", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1330,29 +1680,87 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28 20:34:47" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "2.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-06-15T12:49:02+00:00" }, { "name": "sebastian/version", - "version": "2.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "reference": "c6c1022351a901512170118436c764e473f6de8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", "shasum": "" }, "require": { - "php": ">=5.6" + "php": ">=7.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1373,20 +1781,26 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03 07:35:21" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.1.1", + "version": "3.6.1", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "d667e245d5dcd4d7bf80f26f2c947d476b66213e" + "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d667e245d5dcd4d7bf80f26f2c947d476b66213e", - "reference": "d667e245d5dcd4d7bf80f26f2c947d476b66213e", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/f268ca40d54617c6e06757f83f699775c9b3ff2e", + "reference": "f268ca40d54617c6e06757f83f699775c9b3ff2e", "shasum": "" }, "require": { @@ -1396,7 +1810,7 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "bin": [ "bin/phpcs", @@ -1419,32 +1833,108 @@ } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", "standards" ], - "time": "2017-10-16 22:40:25" + "time": "2021-10-11T04:00:11+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.0" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { @@ -1464,33 +1954,43 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07 12:08:54" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.10.0", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.2 || ^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.10-dev" } }, "autoload": { @@ -1514,7 +2014,7 @@ "check", "validate" ], - "time": "2016-11-23 20:04:58" + "time": "2021-03-09T10:59:23+00:00" } ], "aliases": [], @@ -1523,5 +2023,6 @@ "prefer-stable": false, "prefer-lowest": false, "platform": [], - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "1.1.0" } diff --git a/phpunit.xml b/phpunit.xml index 029e75c..f5c4934 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,20 +1,16 @@ - - - - test/unit - - - test/integration - - - - - src - - + + + + + src + + + + + test/unit + + diff --git a/test/bootstrap.php b/test/bootstrap.php deleted file mode 100644 index e13fcba..0000000 --- a/test/bootstrap.php +++ /dev/null @@ -1,6 +0,0 @@ -sut = new WikitextParser(); } From 21f874c8a7ca268399f6e30b23ff41b006b6ee17 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 19:17:36 +0100 Subject: [PATCH 05/20] Everytime you use the 'new' keyword in a class that is not a factory, god kills a kitten : Injecting the renderer (following D.I.P) --- src/Mike42/Wikitext/WikitextParser.php | 4 ++-- test/unit/WikitextParserTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mike42/Wikitext/WikitextParser.php b/src/Mike42/Wikitext/WikitextParser.php index 6f71a9b..5a9a047 100644 --- a/src/Mike42/Wikitext/WikitextParser.php +++ b/src/Mike42/Wikitext/WikitextParser.php @@ -47,7 +47,7 @@ class WikitextParser /** * Definitions for tokens with special meaning to the parser */ - public function __construct(array $sharedVars = []) + public function __construct(HtmlRenderer $render) { /* Table elements. These are parsed separately to the other elements */ $this->tableStart = new ParserInlineElement("{|", "|}"); @@ -85,7 +85,7 @@ public function __construct(array $sharedVars = []) /* Create lookup table for efficiency */ $this->inlineLookup = $this->elementLookupTable($this->inline); - $this->backend = new DefaultParserBackend(); + $this->backend = $render; /* Line-block elements. These are characters which have a special meaning at the start of lines, and use the next end-line as a close tag. */ $this->lineBlock = array( diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index 78ad01e..03d6d93 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -9,7 +9,7 @@ class WikitextParserTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $this->sut = new WikitextParser(); + $this->sut = new WikitextParser(new DefaultParserBackend()); } protected function assertParsingEquals(string $expected, string $wikitext): void From 83180b62e3de4e206b509b6dbc7f322c390259be Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 19:22:38 +0100 Subject: [PATCH 06/20] test for table --- .phpunit.result.cache | 2 +- test/unit/WikitextParserTest.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.phpunit.result.cache b/.phpunit.result.cache index bc55239..3dcdaca 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":1,"defects":[],"times":{"Mike42\\Wikitext\\DefaultParserBackendTest::testGetTemplateMarkup":0.003,"Mike42\\Wikitext\\WikitextParserTest::testParse":0.001,"Mike42\\Wikitext\\WikitextParserTest::testLink":0}} \ No newline at end of file +{"version":1,"defects":{"Mike42\\Wikitext\\WikitextParserTest::testTable":3},"times":{"Mike42\\Wikitext\\DefaultParserBackendTest::testGetTemplateMarkup":0.004,"Mike42\\Wikitext\\WikitextParserTest::testParse":0.002,"Mike42\\Wikitext\\WikitextParserTest::testLink":0,"Mike42\\Wikitext\\WikitextParserTest::testTable":0}} \ No newline at end of file diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index 03d6d93..d922226 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -27,4 +27,9 @@ public function testLink() $this->assertParsingEquals("

Link : yolo

\n", "Link : [[yolo]]"); } + public function testTable() + { + $this->assertParsingEquals("\n\n\n\n
aaa
\n", "{|\n|aaa\n|}"); + } + } From ed3a56fb049ac8cadbe4397ec8a50845410405ee Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 19:42:21 +0100 Subject: [PATCH 07/20] more tests for img --- .phpunit.result.cache | 2 +- examples/Images/images.php | 56 +++++++++++++------------- src/Mike42/Wikitext/WikitextParser.php | 2 +- test/unit/WikitextParserTest.php | 5 +++ 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/.phpunit.result.cache b/.phpunit.result.cache index 3dcdaca..4c2d0b4 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":1,"defects":{"Mike42\\Wikitext\\WikitextParserTest::testTable":3},"times":{"Mike42\\Wikitext\\DefaultParserBackendTest::testGetTemplateMarkup":0.004,"Mike42\\Wikitext\\WikitextParserTest::testParse":0.002,"Mike42\\Wikitext\\WikitextParserTest::testLink":0,"Mike42\\Wikitext\\WikitextParserTest::testTable":0}} \ No newline at end of file +{"version":1,"defects":{"Mike42\\Wikitext\\WikitextParserTest::testTable":3,"Mike42\\Wikitext\\WikitextParserTest::testImage":3},"times":{"Mike42\\Wikitext\\DefaultParserBackendTest::testGetTemplateMarkup":0.003,"Mike42\\Wikitext\\WikitextParserTest::testParse":0.001,"Mike42\\Wikitext\\WikitextParserTest::testLink":0,"Mike42\\Wikitext\\WikitextParserTest::testTable":0,"Mike42\\Wikitext\\WikitextParserTest::testImage":0.002}} \ No newline at end of file diff --git a/examples/Images/images.php b/examples/Images/images.php index aff4df4..40b31a1 100644 --- a/examples/Images/images.php +++ b/examples/Images/images.php @@ -1,39 +1,39 @@ - - - - - + /* @noflip */ + div.tright,div.floatright,table.floatright { + clear: right; + float: right; + } + /* @noflip */ + div.tleft,div.floatleft,table.floatleft { + float: left; + clear: left; + } - + + - $parser = new WikitextParser($input); - echo $parser -> result; + - + $parser = new WikitextParser(new \Mike42\Wikitext\DefaultParserBackend()); + echo $parser->parse($input); + ?> + diff --git a/src/Mike42/Wikitext/WikitextParser.php b/src/Mike42/Wikitext/WikitextParser.php index 5a9a047..be07c45 100644 --- a/src/Mike42/Wikitext/WikitextParser.php +++ b/src/Mike42/Wikitext/WikitextParser.php @@ -225,7 +225,7 @@ private function preprocessText(array $textChars, array $arg = [], bool $include } } else if ($key == 'template') { /* Load wikitext of template, and preprocess it */ - if ($this->MAX_INCLUDE_DEPTH < 0 || $depth < $this->MAX_INCLUDE_DEPTH) { + if (self::MAX_INCLUDE_DEPTH < 0 || $depth < self::MAX_INCLUDE_DEPTH) { $markup = trim($this->backend->getTemplateMarkup($innerCurKey)); $parsed .= $this->preprocessText($this->explodeString($markup), $innerArg, true, $depth + 1); } diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index d922226..a6f0073 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -32,4 +32,9 @@ public function testTable() $this->assertParsingEquals("\n\n\n\n
aaa
\n", "{|\n|aaa\n|}"); } + public function testImage() + { + $this->assertParsingEquals("

\"yolo\"

\n", "[[File:Flag of Hungary vertical.jpg|bottom|8px|link=hungary|alt=yolo]]"); + } + } From 78819f0afa2c400404f7842d7252127196256802 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 19:46:46 +0100 Subject: [PATCH 08/20] d.r.y --- .gitignore | 2 +- .phpunit.result.cache | 1 - test/unit/WikitextParserTest.php | 24 ++++++++++-------------- 3 files changed, 11 insertions(+), 16 deletions(-) delete mode 100644 .phpunit.result.cache diff --git a/.gitignore b/.gitignore index b4af13f..0f4e05f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .buildpath .settings/* .project - +.phpunit.result.cache /vendor/ /nbproject/ diff --git a/.phpunit.result.cache b/.phpunit.result.cache deleted file mode 100644 index 4c2d0b4..0000000 --- a/.phpunit.result.cache +++ /dev/null @@ -1 +0,0 @@ -{"version":1,"defects":{"Mike42\\Wikitext\\WikitextParserTest::testTable":3,"Mike42\\Wikitext\\WikitextParserTest::testImage":3},"times":{"Mike42\\Wikitext\\DefaultParserBackendTest::testGetTemplateMarkup":0.003,"Mike42\\Wikitext\\WikitextParserTest::testParse":0.001,"Mike42\\Wikitext\\WikitextParserTest::testLink":0,"Mike42\\Wikitext\\WikitextParserTest::testTable":0,"Mike42\\Wikitext\\WikitextParserTest::testImage":0.002}} \ No newline at end of file diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index a6f0073..7fabba1 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -17,24 +17,20 @@ protected function assertParsingEquals(string $expected, string $wikitext): void $this->assertEquals($expected, $this->sut->parse($wikitext)); } - public function testParse() + public function example(): array { - $this->assertParsingEquals("

Testing 1 2 3

\n", "Testing 1 2 3"); + return [ + ["

Testing 1 2 3

\n", "Testing 1 2 3"], + ["

Link : yolo

\n", "Link : [[yolo]]"], + ["\n\n\n\n
aaa
\n", "{|\n|aaa\n|}"], + ["

\"yolo\"

\n", "[[File:Flag of Hungary vertical.jpg|bottom|8px|link=hungary|alt=yolo]]"] + ]; } - public function testLink() + /** @dataProvider example */ + public function testExample($expected, $wikitext) { - $this->assertParsingEquals("

Link : yolo

\n", "Link : [[yolo]]"); - } - - public function testTable() - { - $this->assertParsingEquals("\n\n\n\n
aaa
\n", "{|\n|aaa\n|}"); - } - - public function testImage() - { - $this->assertParsingEquals("

\"yolo\"

\n", "[[File:Flag of Hungary vertical.jpg|bottom|8px|link=hungary|alt=yolo]]"); + $this->assertParsingEquals($expected, $wikitext); } } From 0ba54abfcb534e9f7acc1896ce400e3d9c0c28b5 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Tue, 9 Nov 2021 19:54:39 +0100 Subject: [PATCH 09/20] test for external link --- src/Mike42/Wikitext/DefaultParserBackend.php | 5 +++++ src/Mike42/Wikitext/HtmlRenderer.php | 10 ++++------ src/Mike42/Wikitext/WikitextParser.php | 2 +- test/unit/WikitextParserTest.php | 1 + 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Mike42/Wikitext/DefaultParserBackend.php b/src/Mike42/Wikitext/DefaultParserBackend.php index 195fd1b..fb9a354 100644 --- a/src/Mike42/Wikitext/DefaultParserBackend.php +++ b/src/Mike42/Wikitext/DefaultParserBackend.php @@ -22,4 +22,9 @@ public function getInternalLinkInfo($info): array return $info; } + public function getImageInfo($info): array + { + return $info; + } + } diff --git a/src/Mike42/Wikitext/HtmlRenderer.php b/src/Mike42/Wikitext/HtmlRenderer.php index c63d540..18fb19b 100644 --- a/src/Mike42/Wikitext/HtmlRenderer.php +++ b/src/Mike42/Wikitext/HtmlRenderer.php @@ -108,7 +108,7 @@ public function renderPre($token, $lines) * @param mixed $list The hierachy representing this list * @return string HTML markup for the list */ - public function renderList($token, $list, $expectedDepth = 1) + public function renderList($token, $list, $expectedDepth = 1): string { $outp = ''; $subtoken = "li"; @@ -147,7 +147,7 @@ public function renderList($token, $list, $expectedDepth = 1) * @param string $caption Caption of this link (can inlude parsed wikitext) * @return string HTML markup for the link */ - public function renderLinkInternal($arg) + public function renderLinkInternal($arg): string { /* Figure out properties based on arguments */ if (isset($arg[0])) { @@ -217,6 +217,7 @@ public function renderLinkInternal($arg) /* Allow the local app to contribute to link properties */ $info = $this->getInternalLinkInfo($info); + return "" . $info['caption'] . ""; } @@ -344,10 +345,7 @@ public function renderFile($info, $arg) /** * Method to override when providing extra info about an image (basically external URL and thumbnail path) */ - public function getImageInfo($info) - { - return $info; - } + abstract public function getImageInfo($info): array; /** * Method to override when providing extra info about a link diff --git a/src/Mike42/Wikitext/WikitextParser.php b/src/Mike42/Wikitext/WikitextParser.php index be07c45..11e7e3c 100644 --- a/src/Mike42/Wikitext/WikitextParser.php +++ b/src/Mike42/Wikitext/WikitextParser.php @@ -111,7 +111,7 @@ public function __construct(HtmlRenderer $render) $this->initialised = true; } - private function elementLookupTable(array $elements) + private function elementLookupTable(array $elements): array { $lookup = []; foreach ($elements as $key => $token) { diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index 7fabba1..e53e783 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -22,6 +22,7 @@ public function example(): array return [ ["

Testing 1 2 3

\n", "Testing 1 2 3"], ["

Link : yolo

\n", "Link : [[yolo]]"], + ["

Link : Github

\n", "Link : [https://github.com Github]"], ["\n\n\n\n
aaa
\n", "{|\n|aaa\n|}"], ["

\"yolo\"

\n", "[[File:Flag of Hungary vertical.jpg|bottom|8px|link=hungary|alt=yolo]]"] ]; From 0e883dd8c66fb104d735476220b973b19fd27cc3 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sun, 14 Nov 2021 15:39:07 +0100 Subject: [PATCH 10/20] SRP : externalizing interwiki functions --- .../Wikitext/FileInterwikiRepository.php | 39 +++++++++++++++++++ src/Mike42/Wikitext/InterwikiRepository.php | 28 +++++++++++++ .../Wikitext/NullInterwikiRepository.php | 25 ++++++++++++ test/unit/NullInterwikiRepositoryTest.php | 30 ++++++++++++++ test/unit/WikitextParserTest.php | 14 +++++++ 5 files changed, 136 insertions(+) create mode 100644 src/Mike42/Wikitext/FileInterwikiRepository.php create mode 100644 src/Mike42/Wikitext/InterwikiRepository.php create mode 100644 src/Mike42/Wikitext/NullInterwikiRepository.php create mode 100644 test/unit/NullInterwikiRepositoryTest.php diff --git a/src/Mike42/Wikitext/FileInterwikiRepository.php b/src/Mike42/Wikitext/FileInterwikiRepository.php new file mode 100644 index 0000000..7209262 --- /dev/null +++ b/src/Mike42/Wikitext/FileInterwikiRepository.php @@ -0,0 +1,39 @@ +query->interwikimap as $site) { + if (isset($site->prefix) && isset($site->url)) { + $this->interwiki[$site->prefix] = $site->url; + } + } + } + + public function getTargetUrl(string $namespace): string + { + return $this->interwiki[$info['namespace']]; + } + + public function hasNamespace(string $ns): bool + { + return array_key_exists($ns, $this->interwiki); + } + +} diff --git a/src/Mike42/Wikitext/InterwikiRepository.php b/src/Mike42/Wikitext/InterwikiRepository.php new file mode 100644 index 0000000..23c07ff --- /dev/null +++ b/src/Mike42/Wikitext/InterwikiRepository.php @@ -0,0 +1,28 @@ +sut = new \Mike42\Wikitext\NullInterwikiRepository(); + } + + public function testEmpty() + { + $this->assertFalse($this->sut->hasNamespace('yolo')); + } + + public function testSecurlyImplementingNullObject() + { + $this->expectException(\LogicException::class); + $this->sut->getTargetUrl('yoolo'); + } + +} diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index e53e783..95fc7ce 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -34,4 +34,18 @@ public function testExample($expected, $wikitext) $this->assertParsingEquals($expected, $wikitext); } + public function namespaced(): array + { + return [ + ["

kitty:purr

\n", "[[kitty:purr]]"], + ["

en:kitty

\n", "[[en:kitty]]"], + ]; + } + + /** @dataProvider namespaced */ + public function testNamespacedLink($expected, $wikitext) + { + $this->assertParsingEquals($expected, $wikitext); + } + } From 6dd64bd80b2d651172d17dc805dca0374d09f65d Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sun, 14 Nov 2021 15:47:07 +0100 Subject: [PATCH 11/20] testing fileinterwiki repo --- src/Mike42/Wikitext/FileInterwikiRepository.php | 2 +- test/unit/sample_interwiki.json | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/unit/sample_interwiki.json diff --git a/src/Mike42/Wikitext/FileInterwikiRepository.php b/src/Mike42/Wikitext/FileInterwikiRepository.php index 7209262..b50854c 100644 --- a/src/Mike42/Wikitext/FileInterwikiRepository.php +++ b/src/Mike42/Wikitext/FileInterwikiRepository.php @@ -28,7 +28,7 @@ public function __construct(string $filename) public function getTargetUrl(string $namespace): string { - return $this->interwiki[$info['namespace']]; + return $this->interwiki[$namespace]; } public function hasNamespace(string $ns): bool diff --git a/test/unit/sample_interwiki.json b/test/unit/sample_interwiki.json new file mode 100644 index 0000000..9d45cbf --- /dev/null +++ b/test/unit/sample_interwiki.json @@ -0,0 +1,13 @@ +{ + "batchcomplete": "", + "query": { + "interwikimap": [ + { + "prefix": "en", + "local": "", + "language": "English", + "url": "https://en.wikipedia.org/wiki/$1" + } + ] + } +} From 73db368e51be4e2f63b2bc39b34107c5c7363376 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sun, 14 Nov 2021 15:48:58 +0100 Subject: [PATCH 12/20] bad naming --- ...ileInterwikiRepository.php => JsonInterwikiRepository.php} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/Mike42/Wikitext/{FileInterwikiRepository.php => JsonInterwikiRepository.php} (85%) diff --git a/src/Mike42/Wikitext/FileInterwikiRepository.php b/src/Mike42/Wikitext/JsonInterwikiRepository.php similarity index 85% rename from src/Mike42/Wikitext/FileInterwikiRepository.php rename to src/Mike42/Wikitext/JsonInterwikiRepository.php index b50854c..004ed4b 100644 --- a/src/Mike42/Wikitext/FileInterwikiRepository.php +++ b/src/Mike42/Wikitext/JsonInterwikiRepository.php @@ -7,9 +7,9 @@ namespace Mike42\Wikitext; /** - * InterwikiRepository implemented when the source is a file + * InterwikiRepository implemented when the source is a json file */ -class FileInterwikiRepository implements InterwikiRepository +class JsonInterwikiRepository implements InterwikiRepository { protected $interwiki = []; From 54da6cadebbc8c69c4c7b5e43b81cdda78cb74df Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sun, 14 Nov 2021 15:50:00 +0100 Subject: [PATCH 13/20] renaming test --- test/unit/JsonInterwikiRepositoryTest.php | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 test/unit/JsonInterwikiRepositoryTest.php diff --git a/test/unit/JsonInterwikiRepositoryTest.php b/test/unit/JsonInterwikiRepositoryTest.php new file mode 100644 index 0000000..5c054c4 --- /dev/null +++ b/test/unit/JsonInterwikiRepositoryTest.php @@ -0,0 +1,28 @@ +sut = new \Mike42\Wikitext\JsonInterwikiRepository(__DIR__ . '/sample_interwiki.json'); + } + + public function testEmpty() + { + $this->assertFalse($this->sut->hasNamespace('yolo')); + $this->assertTrue($this->sut->hasNamespace('en')); + } + + public function testGetTargetUrl() + { + $this->assertEquals('https://en.wikipedia.org/wiki/$1', $this->sut->getTargetUrl('en')); + } + +} From 21d160e9d1c9da82084738360c4a6d8b756f89a8 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sun, 14 Nov 2021 15:56:54 +0100 Subject: [PATCH 14/20] DIP : injecting interwiki repo into the renderer --- src/Mike42/Wikitext/HtmlRenderer.php | 37 +++++++++------------------- test/unit/WikitextParserTest.php | 2 +- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/Mike42/Wikitext/HtmlRenderer.php b/src/Mike42/Wikitext/HtmlRenderer.php index 18fb19b..e00c2d4 100644 --- a/src/Mike42/Wikitext/HtmlRenderer.php +++ b/src/Mike42/Wikitext/HtmlRenderer.php @@ -17,6 +17,15 @@ abstract class HtmlRenderer private $interwiki; + public function __construct(?InterwikiRepository $repo = null) + { + if (is_null($repo)) { + $repo = new NullInterwikiRepository(); + } + + $this->interwiki = $repo; + } + /** * Process an element which has arguments. Links, lists and templates fall under this category * @@ -197,20 +206,14 @@ public function renderLinkInternal($arg): string $split++; $info['target'] = substr($destination, $split, strlen($destination) - $split); - /* Look up in default interwiki table */ - if ($this->interwiki == false) { - /* Load as needed */ - $this->loadInterwikiLinks(); - } - if ($info['namespace'] == 'file') { /* Render an image instead of a link if requested */ $info['url'] = $info['target']; $info['caption'] = ''; return $this->renderFile($info, $arg); - } else if (isset($this->interwiki[$info['namespace']])) { + } else if ($this->interwiki->hasNamespace($info['namespace'])) { /* We have a known namespace */ - $site = $this->interwiki[$info['namespace']]; + $site = $this->interwiki->getTargetUrl($info['namespace']); $info['url'] = str_replace("$1", $info['target'], $site); } } @@ -352,24 +355,6 @@ abstract public function getImageInfo($info): array; */ abstract public function getInternalLinkInfo($info): array; - public function loadInterwikiLinks() - { - if ($this->interwiki != false) { - /* Use loaded interwiki links if they exist */ - return; - } - - $this->interwiki = array(); - $json = file_get_contents(__DIR__ . "/interwiki.json"); - /* Unserialize data and load into associative array for easy lookup */ - $arr = json_decode($json); - foreach ($arr->query->interwikimap as $site) { - if (isset($site->prefix) && isset($site->url)) { - $this->interwiki[$site->prefix] = $site->url; - } - } - } - /** * Default rendering of [http://... link] or [http://foo] * diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index 95fc7ce..e66c465 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -9,7 +9,7 @@ class WikitextParserTest extends \PHPUnit\Framework\TestCase protected function setUp(): void { - $this->sut = new WikitextParser(new DefaultParserBackend()); + $this->sut = new WikitextParser(new DefaultParserBackend(new JsonInterwikiRepository(__DIR__ . '/sample_interwiki.json'))); } protected function assertParsingEquals(string $expected, string $wikitext): void From 4d8b057ee6640d981175a4f2cc7665c30e5bba48 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sun, 14 Nov 2021 16:00:27 +0100 Subject: [PATCH 15/20] interwiki links are external --- src/Mike42/Wikitext/HtmlRenderer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mike42/Wikitext/HtmlRenderer.php b/src/Mike42/Wikitext/HtmlRenderer.php index e00c2d4..33a59d9 100644 --- a/src/Mike42/Wikitext/HtmlRenderer.php +++ b/src/Mike42/Wikitext/HtmlRenderer.php @@ -215,6 +215,7 @@ public function renderLinkInternal($arg): string /* We have a known namespace */ $site = $this->interwiki->getTargetUrl($info['namespace']); $info['url'] = str_replace("$1", $info['target'], $site); + $info['external'] = true; } } From 3239d9bf588417f404963c97c3766a6dc2009347 Mon Sep 17 00:00:00 2001 From: Florent Genette Date: Fri, 26 Nov 2021 00:31:30 +0100 Subject: [PATCH 16/20] Notes abou this fork --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c49ac9b..310ae43 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,7 @@ This library can be used to add basic wikitext (Mediawiki-style) support to a PH Code may be re-mixed and re-used under the MIT licence. See 'examples' folder for usage. Information about currently supported markup elements can be found [on the project web-page](http://mike42.me/wikitext/). +# Notes about this fork +* Abstracting +* Removing anti-patterns +* unit testing From 8cc01a9e2201ba17be69168875dda4b645d9e696 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sat, 11 Dec 2021 02:46:57 +0100 Subject: [PATCH 17/20] adding webp format --- src/Mike42/Wikitext/HtmlRenderer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mike42/Wikitext/HtmlRenderer.php b/src/Mike42/Wikitext/HtmlRenderer.php index 33a59d9..2794eff 100644 --- a/src/Mike42/Wikitext/HtmlRenderer.php +++ b/src/Mike42/Wikitext/HtmlRenderer.php @@ -246,6 +246,7 @@ public function renderFile($info, $arg) case 'jpeg': case 'png': case 'gif': + case 'webp': /* Image flags parsed. From: http://www.mediawiki.org/wiki/Help:Images */ /* Named arguments */ From 1a7b1e72d38f6baa9b3e26fe16b52793696cd810 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Sun, 17 Apr 2022 20:32:35 +0200 Subject: [PATCH 18/20] Officially rescuing this project --- README.md | 8 +++++++- composer.json | 44 +++++++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 310ae43..f65fbe4 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,17 @@ # Wikitext parser [![Build Status](https://travis-ci.org/mike42/wikitext.svg?branch=master)](https://travis-ci.org/mike42/wikitext) This library can be used to add basic wikitext (Mediawiki-style) support to a PHP app. +Its role is, by no means, to replace the Parsoid library from Wikimedia foundation, but to use the +same syntaxic core of wikitext. Furthermore, you can extend this parser very easily to your project +specifications (specially for url generation). + +This repository was forked from the [abandoned project mike42.me/wikitext](http://mike42.me/wikitext/), +that's why I've created a new package on Packagist. Code may be re-mixed and re-used under the MIT licence. See 'examples' folder for usage. -Information about currently supported markup elements can be found [on the project web-page](http://mike42.me/wikitext/). # Notes about this fork * Abstracting * Removing anti-patterns * unit testing +* Template method design pattern for extending the html rendering diff --git a/composer.json b/composer.json index b5b883a..5026157 100644 --- a/composer.json +++ b/composer.json @@ -1,21 +1,27 @@ { - "name": "mike42/wikitext", - "description": "PHP wikitext parser", - "type": "library", - "license": "MIT", - "authors": [ - { - "name": "Michael Billington", - "email": "michael.billington@gmail.com" - } - ], - "require-dev": { - "phpunit/phpunit": "^9.5", - "squizlabs/php_codesniffer": "^3.1" - }, - "autoload": { - "psr-4": { - "Mike42\\" : "src/Mike42" - } - } + "name": "trismegiste/wikitext", + "description": "PHP wikitext parser", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Michael Billington", + "email": "michael.billington@gmail.com", + "role": "creator" + }, + { + "name": "Trismegiste", + "homepage": "https://github.com/Trismegiste", + "role": "rescuer" + } + ], + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.1" + }, + "autoload": { + "psr-4": { + "Mike42\\": "src/Mike42" + } + } } From 41aa148683bd0906bd6115aa9f2107a2a01e0c10 Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Fri, 3 Jun 2022 14:27:12 +0200 Subject: [PATCH 19/20] implemeting class argument on file tag --- src/Mike42/Wikitext/HtmlRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mike42/Wikitext/HtmlRenderer.php b/src/Mike42/Wikitext/HtmlRenderer.php index 2794eff..d5db13c 100644 --- a/src/Mike42/Wikitext/HtmlRenderer.php +++ b/src/Mike42/Wikitext/HtmlRenderer.php @@ -330,7 +330,7 @@ public function renderFile($info, $arg) } else { $align = ""; } - $dstart = "
"; + $dstart = "
"; if ($info['caption'] != '') { $dend .= "
" . htmlspecialchars($info['caption']) . "
"; } From dba72199b5d96907dc6251d565dc8d8532f8471c Mon Sep 17 00:00:00 2001 From: Trismegiste Date: Wed, 26 Apr 2023 11:15:03 +0200 Subject: [PATCH 20/20] bug fix for template --- README.md | 8 ++++---- src/Mike42/Wikitext/WikitextParser.php | 5 +++++ test/unit/WikitextParserTest.php | 10 ++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f65fbe4..7b133a7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Wikitext parser [![Build Status](https://travis-ci.org/mike42/wikitext.svg?branch=master)](https://travis-ci.org/mike42/wikitext) This library can be used to add basic wikitext (Mediawiki-style) support to a PHP app. -Its role is, by no means, to replace the Parsoid library from Wikimedia foundation, but to use the +Its role is NOT, by any means, to replace the Parsoid library from Wikimedia foundation, but to use the same syntaxic core of wikitext. Furthermore, you can extend this parser very easily to your project -specifications (specially for url generation). +specifications (specially for url generation and templates). This repository was forked from the [abandoned project mike42.me/wikitext](http://mike42.me/wikitext/), that's why I've created a new package on Packagist. @@ -13,5 +13,5 @@ Code may be re-mixed and re-used under the MIT licence. See 'examples' folder fo # Notes about this fork * Abstracting * Removing anti-patterns -* unit testing -* Template method design pattern for extending the html rendering +* Unit testing +* Template Method Design Pattern for extending the html rendering diff --git a/src/Mike42/Wikitext/WikitextParser.php b/src/Mike42/Wikitext/WikitextParser.php index 11e7e3c..a1716bb 100644 --- a/src/Mike42/Wikitext/WikitextParser.php +++ b/src/Mike42/Wikitext/WikitextParser.php @@ -278,10 +278,15 @@ private function preprocessText(array $textChars, array $arg = [], bool $include private function tagIsAt(array $tag, array $textChars, int $position) { + if ($position >= count($textChars)) { + // Fast exit for common case + return false; + } if ($textChars[$position] != $tag[0]) { // Fast exit for common case return false; } + // More detailed checks for other cases $tagLen = count($tag); $strLen = count($textChars); diff --git a/test/unit/WikitextParserTest.php b/test/unit/WikitextParserTest.php index e66c465..4081e06 100644 --- a/test/unit/WikitextParserTest.php +++ b/test/unit/WikitextParserTest.php @@ -48,4 +48,14 @@ public function testNamespacedLink($expected, $wikitext) $this->assertParsingEquals($expected, $wikitext); } + public function testTemplate() + { + $this->assertParsingEquals("

test

\n", '{{test}}'); + } + + public function testTemplateWithArg() + { + $this->assertParsingEquals("

test

\n", '{{test|arg=arf}}'); + } + }