├── tests ├── testFile.unknown ├── testFile.odt ├── testFile.rtf ├── testFile.html └── DocumentParserTest.php ├── .gitignore ├── phpunit.xml ├── composer.json ├── LICENSE ├── README.md ├── src └── DocumentParser.php └── composer.lock /tests/testFile.unknown: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/testFile.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LukeMadhanga/PHPDocumentParser/HEAD/tests/testFile.odt -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | 3 | # Composer # 4 | /vendor/ 5 | 6 | # Tests cache # 7 | /.phpunit.* 8 | /tests/.* -------------------------------------------------------------------------------- /tests/testFile.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard 2 | Test string for lukemadhanga/php-document-parser.\par 3 | } -------------------------------------------------------------------------------- /tests/testFile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

header

4 |

Test html for lukemadhanga/php-document-parser.

5 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests/ 6 | 7 | 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lukemadhanga/php-document-parser", 3 | "description": "A PHP parser for getting the text from a .doc, .docx, .rtf or .txt file", 4 | "require": { 5 | "php": ">=5.3.3" 6 | }, 7 | "require-dev": { 8 | "phpunit/phpunit": "^8.2" 9 | }, 10 | "authors": [ 11 | { 12 | "name": "Luke Madhanga", 13 | "email": "luke@madhan.ga", 14 | "homepage": "http://madhan.ga", 15 | "role": "Developer" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "LukeMadhanga\\": "src/" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Luke Madhanga 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/DocumentParserTest.php: -------------------------------------------------------------------------------- 1 | expectException(\Exception::class); 15 | 16 | } catch (\Exception $e) { 17 | $this->assertIsObject($e); 18 | $this->assertEquals('Failed to read file: unknown mimetype: inode/x-empty', $e->getMessage()); 19 | } 20 | } 21 | 22 | /** 23 | * Check that content extracted from html remains 24 | */ 25 | public function testParseFromFileReadsHTML() { 26 | $html = \LukeMadhanga\DocumentParser::parseFromFile('tests/testFile.html'); 27 | 28 | $this->assertIsString($html); 29 | $this->assertEquals('

header

30 |

Test html for lukemadhanga/php-document-parser.

', $html); 31 | } 32 | 33 | public function testParseFromFileReadsODT() { 34 | $odt = \LukeMadhanga\DocumentParser::parseFromFile('tests/testFile.odt'); 35 | 36 | $this->assertIsString($odt); 37 | $this->assertEquals("\n

Header 1

Header 2

Styled text

  1. ol1

  2. ol2

  1. ul1

  2. ul2

\n", $odt); 38 | } 39 | 40 | /** 41 | * Check that content extracted from RTF is valid 42 | */ 43 | public function testParseFromFileReadsRTF() { 44 | $rtf = \LukeMadhanga\DocumentParser::parseFromFile('tests/testFile.rtf', 'application/rtf'); 45 | 46 | $this->assertIsString($rtf); 47 | $this->assertStringMatchesFormat('%a', $rtf); 48 | $this->assertEquals('Test string for lukemadhanga/php-document-parser.', $rtf); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP DocumentParser # 2 | *A PHP parser for getting the text from a .doc, .docx, .rtf or .txt file* 3 | 4 | 5 | ---------- 6 | 7 | 8 | Authors 9 | - @facuonline 10 | - Luke Madhanga @LukeMadhanga 11 | 12 | 13 | ---------- 14 | 15 | 16 | This library is perfect if you want users to be able to upload word documents to your content management system, instead of forcing them to copy and paste. Supported file types are **.doc**, **.docx**, **.txt** and **.rtf**. 17 | 18 | > composer require lukemadhanga/php-document-parser 19 | 20 | May require you to install PHP Zip 21 | 22 | > sudo apt-get install php7.0-zip 23 | 24 | The above `Ubuntu` command will vary depending on your version of PHP and what OS is running on your server 25 | 26 | ---------- 27 | 28 | 29 | ## Methods 30 | 31 | #### parseFromFile 32 | *Parse a document from a file* 33 | 34 | Arguments 35 | 36 | string `$filename` The path to the file to parse 37 | 38 | string `$mimetype` The mimetype of the file. This will be used to determine which algorithm to use when decoding 39 | 40 | **returns** string The text from the file 41 | 42 | --- 43 | 44 | #### parseFromString 45 | *Parse a file from a string* 46 | 47 | Arguments 48 | 49 | string `$string` The contents of the file to parse 50 | 51 | string `$mimetype` The mimetype of the file. This will be used to determine which algorithm to use when decoding 52 | 53 | **returns** string The text in the document 54 | 55 | --- 56 | 57 | ## Change log 58 | 59 | #### September 21 2019 (0.1.4) 60 | **Better ODT Support** 61 | Merged in PR#13 for better ODT support. Author: facuonline 62 | 63 | #### August 1 2019 (0.1.3) 64 | **PHP Unit** 65 | Merged in PR#12 for PHP Unit testing. Author: facuonline 66 | 67 | #### March 21 2019 (0.1.2) 68 | **DOCX Handling** 69 | Merged in PR#10 For better DOCX handling. Includes bug fixes for exception handling. Author: facuonline 70 | 71 | 72 | #### September 13th 2017 73 | **Added composer** 74 | 75 | > composer require lukemadhanga/php-document-parser 76 | 77 | #### April 29th 2016 78 | **Improved .doc process** 79 | 80 | The script to parse .doc files is unreliable: it breaks on complicated documents. I would suggest installing the `antiword` command line utility as that works almost perfectly for the larger majority of documents. 81 | -------------------------------------------------------------------------------- /src/DocumentParser.php: -------------------------------------------------------------------------------- 1 |

    1. '; 16 | 17 | /** 18 | * Parse a document from the contents of the string 19 | * @param mixed $string The contents of the file to process 20 | * @param string $mimetype [optional] The mimetype of the file. Defaults to text/plain 21 | * @return type 22 | * @throws Exception 23 | */ 24 | static function parseFromString($string, $mimetype = 'text/plain') { 25 | if (preg_match("/^text\/*/", $mimetype)) { 26 | return $string; 27 | } 28 | 29 | $tmpfilename = 'temp/' . time() . sha1($string) . '.tmp'; 30 | file_put_contents($tmpfilename, $string); 31 | 32 | $contents = self::parseFromFile($tmpfilename, $mimetype); 33 | 34 | unlink($tmpfilename); 35 | return $contents; 36 | } 37 | 38 | /** 39 | * Parse a document and get the text 40 | * @param string $filename The name of the file to read 41 | * @param string $mimetype The mimetype of the file. Used to decide which algorithm to use 42 | * @return string|html The extracted document. For DOC(X) and ODT documents, the content is returned in a HTML format 43 | * @throws Exception 44 | */ 45 | static function parseFromFile($filename, $mimetype = null) { 46 | if (!is_readable($filename)) { 47 | throw new \Exception("Failed to read file: cannot read file: $filename"); 48 | } 49 | 50 | if (!$mimetype) { 51 | $mimetype = mime_content_type($filename); 52 | } 53 | 54 | if (preg_match("/^text\/*/", $mimetype)) { 55 | if ($mimetype === 'text/html') { 56 | return self::parseHtml($filename); 57 | } else { 58 | return file_get_contents($filename); 59 | } 60 | } else if ($mimetype === 'application/rtf') { 61 | return self::parseRtf($filename); 62 | } else if ($mimetype === 'application/msword') { 63 | return self::parseDoc($filename); 64 | } else if ($mimetype === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { 65 | return self::parseZipped($filename, 'word/document.xml'); 66 | } else if ($mimetype === 'application/vnd.oasis.opendocument.text') { 67 | return self::parseZipped($filename, 'content.xml'); 68 | } else if ($mimetype === 'application/octet-stream') { 69 | return self::parseZipped($filename, 'word/document.xml'); 70 | } else { 71 | throw new \Exception("Failed to read file: unknown mimetype: $mimetype"); 72 | } 73 | } 74 | 75 | /** 76 | * Parse zipped document, i.e. .docx or .odt (adapted from http://goo.gl/usI7PF) 77 | * @param string $filename The path to the document 78 | * @param string $datafile .odt and .docx documents are just zipped folders with an XML file. 79 | * This variable is the path to the main xml file which holds the text for the document 80 | * @return html 81 | * @throws Exception 82 | */ 83 | private static function parseZipped($filename, $datafile) { 84 | // Zip function requires a read from a file 85 | $zip = new \ZipArchive(); 86 | $content = ''; 87 | 88 | if ($zip->open($filename)) { 89 | // If done, search for the data file in the archive 90 | if (($index = $zip->locateName($datafile)) !== false) { 91 | // If the data file can be found, read it to the string 92 | $data = $zip->getFromIndex($index); 93 | $xmldoc = new \DOMDocument(); 94 | $xmldoc->loadXML($data, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING); 95 | 96 | if ($datafile === 'word/document.xml') { 97 | // Perform docx specific processing 98 | self::convertWordToHtml($xmldoc); 99 | } else { 100 | self::convertOdtToHtml($xmldoc); 101 | } 102 | 103 | $content = $xmldoc->saveXML(); 104 | } 105 | 106 | $zip->close(); 107 | } else { 108 | throw new \Exception("Failed to read file. Failed zip extraction."); 109 | } 110 | 111 | return strip_tags($content, self::$allowed_tags); 112 | } 113 | 114 | /** 115 | * Convert a DOCX XMLDocument to html 116 | * @param \DOMDocument $xmldoc The xml document describing the word file 117 | */ 118 | private static function convertWordToHtml(\DOMDocument $xmldoc) { 119 | // To make processing easier, remove the 'w' namespace from all elements 120 | self::removeDomNamespace($xmldoc, 'w'); 121 | $xpath = new \DOMXPath($xmldoc); 122 | 123 | // Get all 'r' elements 124 | foreach ($xpath->query("//r") as $node) { 125 | /*@var $node DOMElement*/ 126 | /*@var $stylenodes DOMNodeList*/ 127 | /*@var $textnode DOMNodeList*/ 128 | // The rPr tag defines style elements 129 | $stylenodes = $node->getElementsByTagName('rPr'); 130 | $textnode = $node->getElementsByTagName('t')->item(0); 131 | 132 | if ($stylenodes && $textnode && $stylenodes->length) { 133 | $stylenode = $stylenodes->item(0); 134 | $itags = $stylenode->getElementsByTagName('i'); 135 | $btags = $stylenode->getElementsByTagName('b'); 136 | 137 | if ($itags->length) { 138 | self::renameTag($textnode, 'em'); 139 | } else if ($btags->length) { 140 | self::renameTag($textnode, 'strong'); 141 | } 142 | 143 | $toremove = array_merge(iterator_to_array($itags), iterator_to_array($btags)); 144 | 145 | foreach ($toremove as $nodetoremove) { 146 | $nodetoremove->parentNode->removeChild($nodetoremove); 147 | } 148 | } 149 | } 150 | 151 | // If there is an intetionally blank paragraph to add space, should we delete it? 152 | // self::removeEmptyTag($xmldoc, 'p'); 153 | } 154 | 155 | /** 156 | * Convert an ODT XMLDocument to html 157 | * @param \DOMDocument $xmldoc The XML document to manipulate 158 | */ 159 | private static function convertOdtToHtml(\DOMDocument $xmldoc) { 160 | $xpath = new \DOMXPath($xmldoc); 161 | // Unfortanately OASIS standard for ODT does not specify a consistent naming for style properties 162 | // Bolds and Italic can't be parsed since their stylings are dynamically set by the office suite 163 | $spans = $xpath->query("//text:p"); 164 | $ols = $xpath->query("//text:list"); 165 | $links = $xpath->query("//text:a"); 166 | $tables = $xpath->query("//table:table"); 167 | $headings = $xpath->query("//text:h"); 168 | // It's possible to parse images and serve them encoding their data in base64 169 | // Should this be done? 170 | // $images = $xpath->query("draw:image"); 171 | 172 | foreach ($spans as $span) { 173 | self::renameTag($span, 'p'); 174 | } 175 | 176 | foreach ($ols as $ol) { 177 | self::renameTag($ol, 'ol'); 178 | /** 179 | * It might be possible to distinguish ordered lists from unordered lists via the attribute: text:list-level-style-number 180 | */ 181 | foreach ($xpath->query('//text:list-item') as $li) { 182 | self::renameTag($li, 'li'); 183 | } 184 | } 185 | 186 | foreach ($links as $link) { 187 | $href = $link->getAttribute('xlink:href'); 188 | self::renameTag($link, 'a href="' . $href . "'"); 189 | } 190 | 191 | foreach ($tables as $table) { 192 | self::renameTag($table, 'table'); 193 | 194 | foreach ($xpath->query('//table:table-row') as $row) { 195 | self::renameTag($row, 'tr'); 196 | } 197 | foreach ($xpath->query('//table:table-cell') as $coll) { 198 | self::renameTag($coll, 'td'); 199 | } 200 | } 201 | 202 | foreach ($headings as $heading) { 203 | // Find heading level 204 | $level = $heading->getAttribute('text:outline-level'); 205 | if ($level > 6) { 206 | $level = 6; 207 | } 208 | 209 | self::renameTag($heading, 'h' . $level); 210 | } 211 | 212 | // If there is an intetionally blank paragraph to add space, should we delete it? 213 | // self::removeEmptyTag($xmldoc, 'p'); 214 | } 215 | 216 | /** 217 | * Remove empty tags from a document 218 | * @param \DOMDocument $xmldoc The document to look in 219 | * @param string $tagname The name of the tag to test for emptiness 220 | */ 221 | private static function removeEmptyTag(\DOMDocument $xmldoc, $tagname) { 222 | $xpath = new \DOMXPath($xmldoc); 223 | 224 | foreach ($xpath->query("//$tagname") as $node) { 225 | /*@var $node DOMElement*/ 226 | if (trim($node->textContent) === '') { 227 | // Remove empty tags 228 | $node->parentNode->removeChild($node); 229 | } 230 | } 231 | } 232 | 233 | /** 234 | * Remove the namespace from an XML document (adapted from http://goo.gl/RBlUPU) 235 | * @param \DOMDocument $xmldoc The document to process 236 | * @param string $namespace The namespace to remove 237 | */ 238 | private static function removeDomNamespace(\DOMDocument $xmldoc, $namespace) { 239 | $xpath = new \DOMXPath($xmldoc); 240 | $nodes = $xpath->query("//*[namespace::{$namespace} and not(../namespace::{$namespace})]"); 241 | 242 | foreach ($nodes as $n) { 243 | $namespaceuri = $n->lookupNamespaceURI($namespace); 244 | $n->removeAttributeNS($namespaceuri, $namespace); 245 | } 246 | } 247 | 248 | /** 249 | * Rename a DOMElement (i.e. rename a tag, e.g. i -> em). (adapted from http://goo.gl/Crll0b) 250 | * @param \DOMElement $tag The tag to rename 251 | * @param string $newtagname The name of the new tag 252 | * @return \DOMElement 253 | */ 254 | private static function renameTag(\DOMElement $tag, $newtagname) { 255 | $document = $tag->ownerDocument; 256 | $newtag = $document->createElement($newtagname); 257 | $tag->parentNode->replaceChild($newtag, $tag); 258 | 259 | foreach (iterator_to_array($tag->childNodes) as $child) { 260 | $newtag->appendChild($tag->removeChild($child)); 261 | } 262 | 263 | return $newtag; 264 | } 265 | 266 | /** 267 | * Remove nodes from a given document by their tag names 268 | * @param \DOMDocument $xml The XML document from which to remove nodes 269 | * @param string|array $tagnames A comma separated list of tagnames to remove 270 | */ 271 | private static function removeNodesByTagname(\DOMDocument $xml, $tagnames) { 272 | if (!is_array($tagnames)) { 273 | $tagnames = explode(',', $tagnames); 274 | } 275 | $xpath = new \DOMXPath($xml); 276 | 277 | foreach ($tagnames as $tagname) { 278 | $nodes = $xpath->query("//{$tagname}"); 279 | 280 | foreach ($nodes as $node) { 281 | /*@var $node DOMNode*/ 282 | /*@var $parentnode DOMNode*/ 283 | $parentnode = $node->parentNode; 284 | 285 | if ($parentnode) { 286 | $parentnode->removeChild($node); 287 | } 288 | } 289 | } 290 | } 291 | 292 | /** 293 | * Parse an .html file 294 | * @param string $filename The path to the html file 295 | * @return html Without non markup supported tags 296 | */ 297 | public static function parseHtml($filename) { 298 | $content = file_get_contents($filename); 299 | 300 | $content = strip_tags($content, self::$allowed_tags); 301 | return trim($content); 302 | } 303 | 304 | /** 305 | * Parse a .doc file (adapted from http://goo.gl/Wm29Aj) 306 | * @param string $filename The path to the word document 307 | * @return html 308 | */ 309 | private static function parseDoc($filename) { 310 | if (shell_exec('which antiword')) { 311 | // The antiword programme is installed 312 | // Start off by attempting to see if the command line programme antiword is available. If so, process the document in XML 313 | // docbook (db) format. This is to preserve paragraph formatting 314 | $outtext = self::parseDocAntiword(shell_exec('antiword -x db ' . escapeshellarg($filename))); 315 | } else { 316 | // Fallback to a version that is somewhat buggy. Works for most .doc documents, but the tests are nowhere near good enough. 317 | // Contrast this script which is a couple of lines to the sourcecode of Antiword, which is several thousand 318 | $contents = mb_convert_encoding(file_get_contents($filename), 'utf8', 'binary'); 319 | $paragraphs = mb_split("\r", $contents); 320 | $outtext = ""; 321 | foreach ($paragraphs as $thisparagraph) { 322 | // 0x00 is the null value. Note, this test is buggy. If a paragraph has, for whatever reason, a null character, this 323 | // paragraph will be missing 324 | if (strpos($thisparagraph, chr(0x00)) === false && strlen($thisparagraph) !== 0) { 325 | $outtext .= "

      {$thisparagraph}

      "; 326 | } 327 | } 328 | } 329 | return $outtext; 330 | } 331 | 332 | /** 333 | * Parse the result of an antiword .doc parse 334 | * @param string $contents The contents of the word document 335 | * @return html Simplified HTML with just p, em and strong tags 336 | */ 337 | private static function parseDocAntiword($contents) { 338 | $xml = new \DOMDocument; 339 | $xml->preserveWhiteSpace = false; 340 | $xml->loadXML($contents); 341 | self::removeNodesByTagname($xml, 'title,bookinfo'); 342 | $xpath = new \DOMXPath($xml); 343 | // Find all of the 'para' nodes 344 | $paranodes = $xpath->query('//para'); 345 | 346 | foreach ($paranodes as $node) { 347 | /*@var $node \DOMElement*/ 348 | self::renameTag($node, 'p'); 349 | } 350 | 351 | // Find all of the 'emphasis' nodes 352 | $emnodes = $xpath->query('//emphasis'); 353 | 354 | foreach ($emnodes as $node) { 355 | /*@var $node \DOMElement*/ 356 | $newtagname = 'em'; 357 | 358 | if ($node->getAttribute('role') === 'bold') { 359 | $newtagname = 'strong'; 360 | 361 | } 362 | self::renameTag($node, $newtagname); 363 | } 364 | 365 | self::removeEmptyTag($xml, 'p'); 366 | // White space management. Required if output is being in JS Code editors like tinyMCE 367 | return str_replace("\n", '', trim(strip_tags($xml->saveXML(), '

      '))); 368 | } 369 | 370 | /** 371 | * Determine whether a line in a .rtf string is plain text (adapted from http://goo.gl/yVojUP) 372 | * @param string $string The string to parse 373 | * @return boolean True if the rtf string is plain text 374 | */ 375 | private static function rtfIsPlainText($string) { 376 | $arrfailat = ["*", "fonttbl", "colortbl", "datastore", "themedata"]; 377 | 378 | for ($i = 0; $i < count($arrfailat); $i++) { 379 | if (!empty($string[$arrfailat[$i]])) { 380 | return false; 381 | } 382 | } 383 | 384 | return true; 385 | } 386 | 387 | /** 388 | * Parse a .rtf file (adapted from http://goo.gl/yVojUP). 389 | * @todo Parse properly. Returning a peculiar result. 390 | * @param string $filename The path to the .rtf file 391 | * @return string The plain text .rtf contents 392 | */ 393 | private static function parseRtf($filename) { 394 | // Read the data from the input file. 395 | $text = file_get_contents($filename); 396 | 397 | if (!strlen($text)) { 398 | return ""; 399 | } 400 | 401 | // Create empty stack array. 402 | $document = ""; 403 | $stack = []; 404 | $j = -1; 405 | 406 | // Read the data character by character... 407 | for ($i = 0; $i < strlen($text); $i++) { 408 | $c = $text[$i]; 409 | 410 | if (isset($stack[$j])) { 411 | $isplaintext = self::rtfIsPlainText($stack[$j]); 412 | } else { 413 | $isplaintext = false; 414 | } 415 | 416 | // Depending on current character select the further actions. 417 | switch ($c) { 418 | // the most important key word backslash 419 | case "\\": 420 | // read next character 421 | $nc = $text[$i + 1]; 422 | 423 | // If it is another backslash or nonbreaking space or hyphen, 424 | // then the character is plain text and add it to the output stream. 425 | if ($nc == '\\' && $isplaintext) { 426 | $document .= '\\'; 427 | } else if ($nc == '~' && $isplaintext) { 428 | $document .= ' '; 429 | } else if ($nc == '_' && $isplaintext) { 430 | $document .= '-'; 431 | } else if ($nc == '*') { 432 | // If it is an asterisk mark, add it to the stack. 433 | $stack[$j]["*"] = true; 434 | } 435 | // If it is a single quote, read next two characters that are the hexadecimal notation 436 | // of a character we should add to the output stream. 437 | elseif ($nc == "'") { 438 | $hex = substr($text, $i + 2, 2); 439 | 440 | if ($isplaintext) { 441 | $document .= html_entity_decode("&#" . hexdec($hex) . ";"); 442 | } 443 | //Shift the pointer. 444 | $i += 2; 445 | // Since, we’ve found the alphabetic character, the next characters are control word 446 | // and, possibly, some digit parameter. 447 | } elseif ($nc >= 'a' && $nc <= 'z' || $nc >= 'A' && $nc <= 'Z') { 448 | $word = ""; 449 | $param = null; 450 | 451 | // Start reading characters after the backslash. 452 | for ($k = $i + 1, $m = 0; $k < strlen($text); $k++, $m++) { 453 | $nc = $text[$k]; 454 | 455 | // If the current character is a letter and there were no digits before it, 456 | // then we’re still reading the control word. If there were digits, we should stop 457 | // since we reach the end of the control word. 458 | if ($nc >= 'a' && $nc <= 'z' || $nc >= 'A' && $nc <= 'Z') { 459 | if (empty($param)) { 460 | $word .= $nc; 461 | } else { 462 | break; 463 | } 464 | // If it is a digit, store the parameter. 465 | } else if ($nc >= '0' && $nc <= '9') { 466 | $param .= $nc; 467 | } 468 | // Since minus sign may occur only before a digit parameter, check whether 469 | // $param is empty. Otherwise, we reach the end of the control word. 470 | elseif ($nc == '-') { 471 | if (empty($param)) { 472 | $param .= $nc; 473 | } else { 474 | break; 475 | } 476 | } else { 477 | break; 478 | } 479 | } 480 | // Shift the pointer on the number of read characters. 481 | $i += $m - 1; 482 | 483 | // Start analyzing what we’ve read. We are interested mostly in control words. 484 | $totext = ""; 485 | 486 | switch (strtolower($word)) { 487 | // If the control word is "u", then its parameter is the decimal notation of the 488 | // Unicode character that should be added to the output stream. 489 | // We need to check whether the stack contains \ucN control word. If it does, 490 | // we should remove the N characters from the output stream. 491 | case "u": 492 | $totext .= html_entity_decode("&#x" . dechex($param) . ";"); 493 | $ucdelata = @$stack[$j]["uc"]; 494 | 495 | if ($ucdelata > 0) { 496 | $i += $ucdelata; 497 | } 498 | break; 499 | // Select line feeds, spaces and tabs. 500 | case "par": case "page": case "column": case "line": case "lbr": 501 | $totext .= "\n"; 502 | break; 503 | case "emspace": case "enspace": case "qmspace": 504 | $totext .= " "; 505 | break; 506 | case "tab": $totext .= "\t"; 507 | break; 508 | // Add current date and time instead of corresponding labels. 509 | case "chdate": $totext .= date("m.d.Y"); 510 | break; 511 | case "chdpl": $totext .= date("l, j F Y"); 512 | break; 513 | case "chdpa": $totext .= date("D, j M Y"); 514 | break; 515 | case "chtime": $totext .= date("H:i:s"); 516 | break; 517 | // Replace some reserved characters to their html analogs. 518 | case "emdash": $totext .= html_entity_decode("—"); 519 | break; 520 | case "endash": $totext .= html_entity_decode("–"); 521 | break; 522 | case "bullet": $totext .= html_entity_decode("•"); 523 | break; 524 | case "lquote": $totext .= html_entity_decode("‘"); 525 | break; 526 | case "rquote": $totext .= html_entity_decode("’"); 527 | break; 528 | case "ldblquote": $totext .= html_entity_decode("«"); 529 | break; 530 | case "rdblquote": $totext .= html_entity_decode("»"); 531 | break; 532 | // Add all other to the control words stack. If a control word 533 | // does not include parameters, set ¶m to true. 534 | default: 535 | $stack[$j][strtolower($word)] = empty($param) ? true : $param; 536 | break; 537 | } 538 | // Add data to the output stream if required. 539 | if ($isplaintext) { 540 | $document .= $totext; 541 | } 542 | } 543 | $i++; 544 | break; 545 | // If we read the opening brace {, then new subgroup starts and we add 546 | // new array stack element and write the data from previous stack element to it. 547 | case "{": 548 | if ($stack) { 549 | array_push($stack, $stack[$j++]); 550 | } 551 | break; 552 | // If we read the closing brace }, then we reach the end of subgroup and should remove 553 | // the last stack element. 554 | case "}": 555 | array_pop($stack); 556 | $j--; 557 | break; 558 | // Skip “trash”. 559 | case '\0': 560 | case '\r': 561 | case '\f': 562 | case '\n': 563 | break; 564 | // Add other data to the output stream if required. 565 | default: 566 | if ($isplaintext) { 567 | $document .= $c; 568 | } 569 | break; 570 | } 571 | } 572 | 573 | // Return result. 574 | return trim($document); 575 | } 576 | 577 | } 578 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "717bb3eb18d64e23f0df366cd0c17196", 8 | "packages": [ 9 | { 10 | "name": "doctrine/instantiator", 11 | "version": "1.2.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/doctrine/instantiator.git", 15 | "reference": "a2c590166b2133a4633738648b6b064edae0814a" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", 20 | "reference": "a2c590166b2133a4633738648b6b064edae0814a", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^7.1" 25 | }, 26 | "require-dev": { 27 | "doctrine/coding-standard": "^6.0", 28 | "ext-pdo": "*", 29 | "ext-phar": "*", 30 | "phpbench/phpbench": "^0.13", 31 | "phpstan/phpstan-phpunit": "^0.11", 32 | "phpstan/phpstan-shim": "^0.11", 33 | "phpunit/phpunit": "^7.0" 34 | }, 35 | "type": "library", 36 | "extra": { 37 | "branch-alias": { 38 | "dev-master": "1.2.x-dev" 39 | } 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 44 | } 45 | }, 46 | "notification-url": "https://packagist.org/downloads/", 47 | "license": [ 48 | "MIT" 49 | ], 50 | "authors": [ 51 | { 52 | "name": "Marco Pivetta", 53 | "email": "ocramius@gmail.com", 54 | "homepage": "http://ocramius.github.com/" 55 | } 56 | ], 57 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 58 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html", 59 | "keywords": [ 60 | "constructor", 61 | "instantiate" 62 | ], 63 | "time": "2019-03-17T17:37:11+00:00" 64 | }, 65 | { 66 | "name": "myclabs/deep-copy", 67 | "version": "1.9.1", 68 | "source": { 69 | "type": "git", 70 | "url": "https://github.com/myclabs/DeepCopy.git", 71 | "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" 72 | }, 73 | "dist": { 74 | "type": "zip", 75 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", 76 | "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", 77 | "shasum": "" 78 | }, 79 | "require": { 80 | "php": "^7.1" 81 | }, 82 | "replace": { 83 | "myclabs/deep-copy": "self.version" 84 | }, 85 | "require-dev": { 86 | "doctrine/collections": "^1.0", 87 | "doctrine/common": "^2.6", 88 | "phpunit/phpunit": "^7.1" 89 | }, 90 | "type": "library", 91 | "autoload": { 92 | "psr-4": { 93 | "DeepCopy\\": "src/DeepCopy/" 94 | }, 95 | "files": [ 96 | "src/DeepCopy/deep_copy.php" 97 | ] 98 | }, 99 | "notification-url": "https://packagist.org/downloads/", 100 | "license": [ 101 | "MIT" 102 | ], 103 | "description": "Create deep copies (clones) of your objects", 104 | "keywords": [ 105 | "clone", 106 | "copy", 107 | "duplicate", 108 | "object", 109 | "object graph" 110 | ], 111 | "time": "2019-04-07T13:18:21+00:00" 112 | }, 113 | { 114 | "name": "phar-io/manifest", 115 | "version": "1.0.3", 116 | "source": { 117 | "type": "git", 118 | "url": "https://github.com/phar-io/manifest.git", 119 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" 120 | }, 121 | "dist": { 122 | "type": "zip", 123 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 124 | "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", 125 | "shasum": "" 126 | }, 127 | "require": { 128 | "ext-dom": "*", 129 | "ext-phar": "*", 130 | "phar-io/version": "^2.0", 131 | "php": "^5.6 || ^7.0" 132 | }, 133 | "type": "library", 134 | "extra": { 135 | "branch-alias": { 136 | "dev-master": "1.0.x-dev" 137 | } 138 | }, 139 | "autoload": { 140 | "classmap": [ 141 | "src/" 142 | ] 143 | }, 144 | "notification-url": "https://packagist.org/downloads/", 145 | "license": [ 146 | "BSD-3-Clause" 147 | ], 148 | "authors": [ 149 | { 150 | "name": "Arne Blankerts", 151 | "email": "arne@blankerts.de", 152 | "role": "Developer" 153 | }, 154 | { 155 | "name": "Sebastian Heuer", 156 | "email": "sebastian@phpeople.de", 157 | "role": "Developer" 158 | }, 159 | { 160 | "name": "Sebastian Bergmann", 161 | "email": "sebastian@phpunit.de", 162 | "role": "Developer" 163 | } 164 | ], 165 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", 166 | "time": "2018-07-08T19:23:20+00:00" 167 | }, 168 | { 169 | "name": "phar-io/version", 170 | "version": "2.0.1", 171 | "source": { 172 | "type": "git", 173 | "url": "https://github.com/phar-io/version.git", 174 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" 175 | }, 176 | "dist": { 177 | "type": "zip", 178 | "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", 179 | "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", 180 | "shasum": "" 181 | }, 182 | "require": { 183 | "php": "^5.6 || ^7.0" 184 | }, 185 | "type": "library", 186 | "autoload": { 187 | "classmap": [ 188 | "src/" 189 | ] 190 | }, 191 | "notification-url": "https://packagist.org/downloads/", 192 | "license": [ 193 | "BSD-3-Clause" 194 | ], 195 | "authors": [ 196 | { 197 | "name": "Arne Blankerts", 198 | "email": "arne@blankerts.de", 199 | "role": "Developer" 200 | }, 201 | { 202 | "name": "Sebastian Heuer", 203 | "email": "sebastian@phpeople.de", 204 | "role": "Developer" 205 | }, 206 | { 207 | "name": "Sebastian Bergmann", 208 | "email": "sebastian@phpunit.de", 209 | "role": "Developer" 210 | } 211 | ], 212 | "description": "Library for handling version information and constraints", 213 | "time": "2018-07-08T19:19:57+00:00" 214 | }, 215 | { 216 | "name": "phpdocumentor/reflection-common", 217 | "version": "1.0.1", 218 | "source": { 219 | "type": "git", 220 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git", 221 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" 222 | }, 223 | "dist": { 224 | "type": "zip", 225 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 226 | "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", 227 | "shasum": "" 228 | }, 229 | "require": { 230 | "php": ">=5.5" 231 | }, 232 | "require-dev": { 233 | "phpunit/phpunit": "^4.6" 234 | }, 235 | "type": "library", 236 | "extra": { 237 | "branch-alias": { 238 | "dev-master": "1.0.x-dev" 239 | } 240 | }, 241 | "autoload": { 242 | "psr-4": { 243 | "phpDocumentor\\Reflection\\": [ 244 | "src" 245 | ] 246 | } 247 | }, 248 | "notification-url": "https://packagist.org/downloads/", 249 | "license": [ 250 | "MIT" 251 | ], 252 | "authors": [ 253 | { 254 | "name": "Jaap van Otterdijk", 255 | "email": "opensource@ijaap.nl" 256 | } 257 | ], 258 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure", 259 | "homepage": "http://www.phpdoc.org", 260 | "keywords": [ 261 | "FQSEN", 262 | "phpDocumentor", 263 | "phpdoc", 264 | "reflection", 265 | "static analysis" 266 | ], 267 | "time": "2017-09-11T18:02:19+00:00" 268 | }, 269 | { 270 | "name": "phpdocumentor/reflection-docblock", 271 | "version": "4.3.1", 272 | "source": { 273 | "type": "git", 274 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 275 | "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" 276 | }, 277 | "dist": { 278 | "type": "zip", 279 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", 280 | "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", 281 | "shasum": "" 282 | }, 283 | "require": { 284 | "php": "^7.0", 285 | "phpdocumentor/reflection-common": "^1.0.0", 286 | "phpdocumentor/type-resolver": "^0.4.0", 287 | "webmozart/assert": "^1.0" 288 | }, 289 | "require-dev": { 290 | "doctrine/instantiator": "~1.0.5", 291 | "mockery/mockery": "^1.0", 292 | "phpunit/phpunit": "^6.4" 293 | }, 294 | "type": "library", 295 | "extra": { 296 | "branch-alias": { 297 | "dev-master": "4.x-dev" 298 | } 299 | }, 300 | "autoload": { 301 | "psr-4": { 302 | "phpDocumentor\\Reflection\\": [ 303 | "src/" 304 | ] 305 | } 306 | }, 307 | "notification-url": "https://packagist.org/downloads/", 308 | "license": [ 309 | "MIT" 310 | ], 311 | "authors": [ 312 | { 313 | "name": "Mike van Riel", 314 | "email": "me@mikevanriel.com" 315 | } 316 | ], 317 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", 318 | "time": "2019-04-30T17:48:53+00:00" 319 | }, 320 | { 321 | "name": "phpdocumentor/type-resolver", 322 | "version": "0.4.0", 323 | "source": { 324 | "type": "git", 325 | "url": "https://github.com/phpDocumentor/TypeResolver.git", 326 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" 327 | }, 328 | "dist": { 329 | "type": "zip", 330 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", 331 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", 332 | "shasum": "" 333 | }, 334 | "require": { 335 | "php": "^5.5 || ^7.0", 336 | "phpdocumentor/reflection-common": "^1.0" 337 | }, 338 | "require-dev": { 339 | "mockery/mockery": "^0.9.4", 340 | "phpunit/phpunit": "^5.2||^4.8.24" 341 | }, 342 | "type": "library", 343 | "extra": { 344 | "branch-alias": { 345 | "dev-master": "1.0.x-dev" 346 | } 347 | }, 348 | "autoload": { 349 | "psr-4": { 350 | "phpDocumentor\\Reflection\\": [ 351 | "src/" 352 | ] 353 | } 354 | }, 355 | "notification-url": "https://packagist.org/downloads/", 356 | "license": [ 357 | "MIT" 358 | ], 359 | "authors": [ 360 | { 361 | "name": "Mike van Riel", 362 | "email": "me@mikevanriel.com" 363 | } 364 | ], 365 | "time": "2017-07-14T14:27:02+00:00" 366 | }, 367 | { 368 | "name": "phpspec/prophecy", 369 | "version": "1.8.1", 370 | "source": { 371 | "type": "git", 372 | "url": "https://github.com/phpspec/prophecy.git", 373 | "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" 374 | }, 375 | "dist": { 376 | "type": "zip", 377 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", 378 | "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", 379 | "shasum": "" 380 | }, 381 | "require": { 382 | "doctrine/instantiator": "^1.0.2", 383 | "php": "^5.3|^7.0", 384 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 385 | "sebastian/comparator": "^1.1|^2.0|^3.0", 386 | "sebastian/recursion-context": "^1.0|^2.0|^3.0" 387 | }, 388 | "require-dev": { 389 | "phpspec/phpspec": "^2.5|^3.2", 390 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" 391 | }, 392 | "type": "library", 393 | "extra": { 394 | "branch-alias": { 395 | "dev-master": "1.8.x-dev" 396 | } 397 | }, 398 | "autoload": { 399 | "psr-4": { 400 | "Prophecy\\": "src/Prophecy" 401 | } 402 | }, 403 | "notification-url": "https://packagist.org/downloads/", 404 | "license": [ 405 | "MIT" 406 | ], 407 | "authors": [ 408 | { 409 | "name": "Konstantin Kudryashov", 410 | "email": "ever.zet@gmail.com", 411 | "homepage": "http://everzet.com" 412 | }, 413 | { 414 | "name": "Marcello Duarte", 415 | "email": "marcello.duarte@gmail.com" 416 | } 417 | ], 418 | "description": "Highly opinionated mocking framework for PHP 5.3+", 419 | "homepage": "https://github.com/phpspec/prophecy", 420 | "keywords": [ 421 | "Double", 422 | "Dummy", 423 | "fake", 424 | "mock", 425 | "spy", 426 | "stub" 427 | ], 428 | "time": "2019-06-13T12:50:23+00:00" 429 | }, 430 | { 431 | "name": "phpunit/php-code-coverage", 432 | "version": "7.0.7", 433 | "source": { 434 | "type": "git", 435 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 436 | "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800" 437 | }, 438 | "dist": { 439 | "type": "zip", 440 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7743bbcfff2a907e9ee4a25be13d0f8ec5e73800", 441 | "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800", 442 | "shasum": "" 443 | }, 444 | "require": { 445 | "ext-dom": "*", 446 | "ext-xmlwriter": "*", 447 | "php": "^7.2", 448 | "phpunit/php-file-iterator": "^2.0.2", 449 | "phpunit/php-text-template": "^1.2.1", 450 | "phpunit/php-token-stream": "^3.1.0", 451 | "sebastian/code-unit-reverse-lookup": "^1.0.1", 452 | "sebastian/environment": "^4.2.2", 453 | "sebastian/version": "^2.0.1", 454 | "theseer/tokenizer": "^1.1.3" 455 | }, 456 | "require-dev": { 457 | "phpunit/phpunit": "^8.2.2" 458 | }, 459 | "suggest": { 460 | "ext-xdebug": "^2.7.2" 461 | }, 462 | "type": "library", 463 | "extra": { 464 | "branch-alias": { 465 | "dev-master": "7.0-dev" 466 | } 467 | }, 468 | "autoload": { 469 | "classmap": [ 470 | "src/" 471 | ] 472 | }, 473 | "notification-url": "https://packagist.org/downloads/", 474 | "license": [ 475 | "BSD-3-Clause" 476 | ], 477 | "authors": [ 478 | { 479 | "name": "Sebastian Bergmann", 480 | "role": "lead", 481 | "email": "sebastian@phpunit.de" 482 | } 483 | ], 484 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 485 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 486 | "keywords": [ 487 | "coverage", 488 | "testing", 489 | "xunit" 490 | ], 491 | "time": "2019-07-25T05:31:54+00:00" 492 | }, 493 | { 494 | "name": "phpunit/php-file-iterator", 495 | "version": "2.0.2", 496 | "source": { 497 | "type": "git", 498 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 499 | "reference": "050bedf145a257b1ff02746c31894800e5122946" 500 | }, 501 | "dist": { 502 | "type": "zip", 503 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", 504 | "reference": "050bedf145a257b1ff02746c31894800e5122946", 505 | "shasum": "" 506 | }, 507 | "require": { 508 | "php": "^7.1" 509 | }, 510 | "require-dev": { 511 | "phpunit/phpunit": "^7.1" 512 | }, 513 | "type": "library", 514 | "extra": { 515 | "branch-alias": { 516 | "dev-master": "2.0.x-dev" 517 | } 518 | }, 519 | "autoload": { 520 | "classmap": [ 521 | "src/" 522 | ] 523 | }, 524 | "notification-url": "https://packagist.org/downloads/", 525 | "license": [ 526 | "BSD-3-Clause" 527 | ], 528 | "authors": [ 529 | { 530 | "name": "Sebastian Bergmann", 531 | "email": "sebastian@phpunit.de", 532 | "role": "lead" 533 | } 534 | ], 535 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 536 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 537 | "keywords": [ 538 | "filesystem", 539 | "iterator" 540 | ], 541 | "time": "2018-09-13T20:33:42+00:00" 542 | }, 543 | { 544 | "name": "phpunit/php-text-template", 545 | "version": "1.2.1", 546 | "source": { 547 | "type": "git", 548 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 549 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 550 | }, 551 | "dist": { 552 | "type": "zip", 553 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 554 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 555 | "shasum": "" 556 | }, 557 | "require": { 558 | "php": ">=5.3.3" 559 | }, 560 | "type": "library", 561 | "autoload": { 562 | "classmap": [ 563 | "src/" 564 | ] 565 | }, 566 | "notification-url": "https://packagist.org/downloads/", 567 | "license": [ 568 | "BSD-3-Clause" 569 | ], 570 | "authors": [ 571 | { 572 | "name": "Sebastian Bergmann", 573 | "email": "sebastian@phpunit.de", 574 | "role": "lead" 575 | } 576 | ], 577 | "description": "Simple template engine.", 578 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 579 | "keywords": [ 580 | "template" 581 | ], 582 | "time": "2015-06-21T13:50:34+00:00" 583 | }, 584 | { 585 | "name": "phpunit/php-timer", 586 | "version": "2.1.2", 587 | "source": { 588 | "type": "git", 589 | "url": "https://github.com/sebastianbergmann/php-timer.git", 590 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" 591 | }, 592 | "dist": { 593 | "type": "zip", 594 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", 595 | "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", 596 | "shasum": "" 597 | }, 598 | "require": { 599 | "php": "^7.1" 600 | }, 601 | "require-dev": { 602 | "phpunit/phpunit": "^7.0" 603 | }, 604 | "type": "library", 605 | "extra": { 606 | "branch-alias": { 607 | "dev-master": "2.1-dev" 608 | } 609 | }, 610 | "autoload": { 611 | "classmap": [ 612 | "src/" 613 | ] 614 | }, 615 | "notification-url": "https://packagist.org/downloads/", 616 | "license": [ 617 | "BSD-3-Clause" 618 | ], 619 | "authors": [ 620 | { 621 | "name": "Sebastian Bergmann", 622 | "email": "sebastian@phpunit.de", 623 | "role": "lead" 624 | } 625 | ], 626 | "description": "Utility class for timing", 627 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 628 | "keywords": [ 629 | "timer" 630 | ], 631 | "time": "2019-06-07T04:22:29+00:00" 632 | }, 633 | { 634 | "name": "phpunit/php-token-stream", 635 | "version": "3.1.0", 636 | "source": { 637 | "type": "git", 638 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 639 | "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a" 640 | }, 641 | "dist": { 642 | "type": "zip", 643 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a", 644 | "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a", 645 | "shasum": "" 646 | }, 647 | "require": { 648 | "ext-tokenizer": "*", 649 | "php": "^7.1" 650 | }, 651 | "require-dev": { 652 | "phpunit/phpunit": "^7.0" 653 | }, 654 | "type": "library", 655 | "extra": { 656 | "branch-alias": { 657 | "dev-master": "3.1-dev" 658 | } 659 | }, 660 | "autoload": { 661 | "classmap": [ 662 | "src/" 663 | ] 664 | }, 665 | "notification-url": "https://packagist.org/downloads/", 666 | "license": [ 667 | "BSD-3-Clause" 668 | ], 669 | "authors": [ 670 | { 671 | "name": "Sebastian Bergmann", 672 | "email": "sebastian@phpunit.de" 673 | } 674 | ], 675 | "description": "Wrapper around PHP's tokenizer extension.", 676 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 677 | "keywords": [ 678 | "tokenizer" 679 | ], 680 | "time": "2019-07-25T05:29:42+00:00" 681 | }, 682 | { 683 | "name": "phpunit/phpunit", 684 | "version": "8.2.5", 685 | "source": { 686 | "type": "git", 687 | "url": "https://github.com/sebastianbergmann/phpunit.git", 688 | "reference": "c1b8534b3730f20f58600124129197bf1183dc92" 689 | }, 690 | "dist": { 691 | "type": "zip", 692 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c1b8534b3730f20f58600124129197bf1183dc92", 693 | "reference": "c1b8534b3730f20f58600124129197bf1183dc92", 694 | "shasum": "" 695 | }, 696 | "require": { 697 | "doctrine/instantiator": "^1.2.0", 698 | "ext-dom": "*", 699 | "ext-json": "*", 700 | "ext-libxml": "*", 701 | "ext-mbstring": "*", 702 | "ext-xml": "*", 703 | "ext-xmlwriter": "*", 704 | "myclabs/deep-copy": "^1.9.1", 705 | "phar-io/manifest": "^1.0.3", 706 | "phar-io/version": "^2.0.1", 707 | "php": "^7.2", 708 | "phpspec/prophecy": "^1.8.1", 709 | "phpunit/php-code-coverage": "^7.0.5", 710 | "phpunit/php-file-iterator": "^2.0.2", 711 | "phpunit/php-text-template": "^1.2.1", 712 | "phpunit/php-timer": "^2.1.2", 713 | "sebastian/comparator": "^3.0.2", 714 | "sebastian/diff": "^3.0.2", 715 | "sebastian/environment": "^4.2.2", 716 | "sebastian/exporter": "^3.1.0", 717 | "sebastian/global-state": "^3.0.0", 718 | "sebastian/object-enumerator": "^3.0.3", 719 | "sebastian/resource-operations": "^2.0.1", 720 | "sebastian/type": "^1.1.3", 721 | "sebastian/version": "^2.0.1" 722 | }, 723 | "require-dev": { 724 | "ext-pdo": "*" 725 | }, 726 | "suggest": { 727 | "ext-soap": "*", 728 | "ext-xdebug": "*", 729 | "phpunit/php-invoker": "^2.0.0" 730 | }, 731 | "bin": [ 732 | "phpunit" 733 | ], 734 | "type": "library", 735 | "extra": { 736 | "branch-alias": { 737 | "dev-master": "8.2-dev" 738 | } 739 | }, 740 | "autoload": { 741 | "classmap": [ 742 | "src/" 743 | ] 744 | }, 745 | "notification-url": "https://packagist.org/downloads/", 746 | "license": [ 747 | "BSD-3-Clause" 748 | ], 749 | "authors": [ 750 | { 751 | "name": "Sebastian Bergmann", 752 | "role": "lead", 753 | "email": "sebastian@phpunit.de" 754 | } 755 | ], 756 | "description": "The PHP Unit Testing framework.", 757 | "homepage": "https://phpunit.de/", 758 | "keywords": [ 759 | "phpunit", 760 | "testing", 761 | "xunit" 762 | ], 763 | "time": "2019-07-15T06:26:24+00:00" 764 | }, 765 | { 766 | "name": "sebastian/code-unit-reverse-lookup", 767 | "version": "1.0.1", 768 | "source": { 769 | "type": "git", 770 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", 771 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" 772 | }, 773 | "dist": { 774 | "type": "zip", 775 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 776 | "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", 777 | "shasum": "" 778 | }, 779 | "require": { 780 | "php": "^5.6 || ^7.0" 781 | }, 782 | "require-dev": { 783 | "phpunit/phpunit": "^5.7 || ^6.0" 784 | }, 785 | "type": "library", 786 | "extra": { 787 | "branch-alias": { 788 | "dev-master": "1.0.x-dev" 789 | } 790 | }, 791 | "autoload": { 792 | "classmap": [ 793 | "src/" 794 | ] 795 | }, 796 | "notification-url": "https://packagist.org/downloads/", 797 | "license": [ 798 | "BSD-3-Clause" 799 | ], 800 | "authors": [ 801 | { 802 | "name": "Sebastian Bergmann", 803 | "email": "sebastian@phpunit.de" 804 | } 805 | ], 806 | "description": "Looks up which function or method a line of code belongs to", 807 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", 808 | "time": "2017-03-04T06:30:41+00:00" 809 | }, 810 | { 811 | "name": "sebastian/comparator", 812 | "version": "3.0.2", 813 | "source": { 814 | "type": "git", 815 | "url": "https://github.com/sebastianbergmann/comparator.git", 816 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" 817 | }, 818 | "dist": { 819 | "type": "zip", 820 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 821 | "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", 822 | "shasum": "" 823 | }, 824 | "require": { 825 | "php": "^7.1", 826 | "sebastian/diff": "^3.0", 827 | "sebastian/exporter": "^3.1" 828 | }, 829 | "require-dev": { 830 | "phpunit/phpunit": "^7.1" 831 | }, 832 | "type": "library", 833 | "extra": { 834 | "branch-alias": { 835 | "dev-master": "3.0-dev" 836 | } 837 | }, 838 | "autoload": { 839 | "classmap": [ 840 | "src/" 841 | ] 842 | }, 843 | "notification-url": "https://packagist.org/downloads/", 844 | "license": [ 845 | "BSD-3-Clause" 846 | ], 847 | "authors": [ 848 | { 849 | "name": "Jeff Welch", 850 | "email": "whatthejeff@gmail.com" 851 | }, 852 | { 853 | "name": "Volker Dusch", 854 | "email": "github@wallbash.com" 855 | }, 856 | { 857 | "name": "Bernhard Schussek", 858 | "email": "bschussek@2bepublished.at" 859 | }, 860 | { 861 | "name": "Sebastian Bergmann", 862 | "email": "sebastian@phpunit.de" 863 | } 864 | ], 865 | "description": "Provides the functionality to compare PHP values for equality", 866 | "homepage": "https://github.com/sebastianbergmann/comparator", 867 | "keywords": [ 868 | "comparator", 869 | "compare", 870 | "equality" 871 | ], 872 | "time": "2018-07-12T15:12:46+00:00" 873 | }, 874 | { 875 | "name": "sebastian/diff", 876 | "version": "3.0.2", 877 | "source": { 878 | "type": "git", 879 | "url": "https://github.com/sebastianbergmann/diff.git", 880 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" 881 | }, 882 | "dist": { 883 | "type": "zip", 884 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", 885 | "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", 886 | "shasum": "" 887 | }, 888 | "require": { 889 | "php": "^7.1" 890 | }, 891 | "require-dev": { 892 | "phpunit/phpunit": "^7.5 || ^8.0", 893 | "symfony/process": "^2 || ^3.3 || ^4" 894 | }, 895 | "type": "library", 896 | "extra": { 897 | "branch-alias": { 898 | "dev-master": "3.0-dev" 899 | } 900 | }, 901 | "autoload": { 902 | "classmap": [ 903 | "src/" 904 | ] 905 | }, 906 | "notification-url": "https://packagist.org/downloads/", 907 | "license": [ 908 | "BSD-3-Clause" 909 | ], 910 | "authors": [ 911 | { 912 | "name": "Kore Nordmann", 913 | "email": "mail@kore-nordmann.de" 914 | }, 915 | { 916 | "name": "Sebastian Bergmann", 917 | "email": "sebastian@phpunit.de" 918 | } 919 | ], 920 | "description": "Diff implementation", 921 | "homepage": "https://github.com/sebastianbergmann/diff", 922 | "keywords": [ 923 | "diff", 924 | "udiff", 925 | "unidiff", 926 | "unified diff" 927 | ], 928 | "time": "2019-02-04T06:01:07+00:00" 929 | }, 930 | { 931 | "name": "sebastian/environment", 932 | "version": "4.2.2", 933 | "source": { 934 | "type": "git", 935 | "url": "https://github.com/sebastianbergmann/environment.git", 936 | "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" 937 | }, 938 | "dist": { 939 | "type": "zip", 940 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", 941 | "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", 942 | "shasum": "" 943 | }, 944 | "require": { 945 | "php": "^7.1" 946 | }, 947 | "require-dev": { 948 | "phpunit/phpunit": "^7.5" 949 | }, 950 | "suggest": { 951 | "ext-posix": "*" 952 | }, 953 | "type": "library", 954 | "extra": { 955 | "branch-alias": { 956 | "dev-master": "4.2-dev" 957 | } 958 | }, 959 | "autoload": { 960 | "classmap": [ 961 | "src/" 962 | ] 963 | }, 964 | "notification-url": "https://packagist.org/downloads/", 965 | "license": [ 966 | "BSD-3-Clause" 967 | ], 968 | "authors": [ 969 | { 970 | "name": "Sebastian Bergmann", 971 | "email": "sebastian@phpunit.de" 972 | } 973 | ], 974 | "description": "Provides functionality to handle HHVM/PHP environments", 975 | "homepage": "http://www.github.com/sebastianbergmann/environment", 976 | "keywords": [ 977 | "Xdebug", 978 | "environment", 979 | "hhvm" 980 | ], 981 | "time": "2019-05-05T09:05:15+00:00" 982 | }, 983 | { 984 | "name": "sebastian/exporter", 985 | "version": "3.1.0", 986 | "source": { 987 | "type": "git", 988 | "url": "https://github.com/sebastianbergmann/exporter.git", 989 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" 990 | }, 991 | "dist": { 992 | "type": "zip", 993 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", 994 | "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", 995 | "shasum": "" 996 | }, 997 | "require": { 998 | "php": "^7.0", 999 | "sebastian/recursion-context": "^3.0" 1000 | }, 1001 | "require-dev": { 1002 | "ext-mbstring": "*", 1003 | "phpunit/phpunit": "^6.0" 1004 | }, 1005 | "type": "library", 1006 | "extra": { 1007 | "branch-alias": { 1008 | "dev-master": "3.1.x-dev" 1009 | } 1010 | }, 1011 | "autoload": { 1012 | "classmap": [ 1013 | "src/" 1014 | ] 1015 | }, 1016 | "notification-url": "https://packagist.org/downloads/", 1017 | "license": [ 1018 | "BSD-3-Clause" 1019 | ], 1020 | "authors": [ 1021 | { 1022 | "name": "Jeff Welch", 1023 | "email": "whatthejeff@gmail.com" 1024 | }, 1025 | { 1026 | "name": "Volker Dusch", 1027 | "email": "github@wallbash.com" 1028 | }, 1029 | { 1030 | "name": "Bernhard Schussek", 1031 | "email": "bschussek@2bepublished.at" 1032 | }, 1033 | { 1034 | "name": "Sebastian Bergmann", 1035 | "email": "sebastian@phpunit.de" 1036 | }, 1037 | { 1038 | "name": "Adam Harvey", 1039 | "email": "aharvey@php.net" 1040 | } 1041 | ], 1042 | "description": "Provides the functionality to export PHP variables for visualization", 1043 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1044 | "keywords": [ 1045 | "export", 1046 | "exporter" 1047 | ], 1048 | "time": "2017-04-03T13:19:02+00:00" 1049 | }, 1050 | { 1051 | "name": "sebastian/global-state", 1052 | "version": "3.0.0", 1053 | "source": { 1054 | "type": "git", 1055 | "url": "https://github.com/sebastianbergmann/global-state.git", 1056 | "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" 1057 | }, 1058 | "dist": { 1059 | "type": "zip", 1060 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", 1061 | "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", 1062 | "shasum": "" 1063 | }, 1064 | "require": { 1065 | "php": "^7.2", 1066 | "sebastian/object-reflector": "^1.1.1", 1067 | "sebastian/recursion-context": "^3.0" 1068 | }, 1069 | "require-dev": { 1070 | "ext-dom": "*", 1071 | "phpunit/phpunit": "^8.0" 1072 | }, 1073 | "suggest": { 1074 | "ext-uopz": "*" 1075 | }, 1076 | "type": "library", 1077 | "extra": { 1078 | "branch-alias": { 1079 | "dev-master": "3.0-dev" 1080 | } 1081 | }, 1082 | "autoload": { 1083 | "classmap": [ 1084 | "src/" 1085 | ] 1086 | }, 1087 | "notification-url": "https://packagist.org/downloads/", 1088 | "license": [ 1089 | "BSD-3-Clause" 1090 | ], 1091 | "authors": [ 1092 | { 1093 | "name": "Sebastian Bergmann", 1094 | "email": "sebastian@phpunit.de" 1095 | } 1096 | ], 1097 | "description": "Snapshotting of global state", 1098 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1099 | "keywords": [ 1100 | "global state" 1101 | ], 1102 | "time": "2019-02-01T05:30:01+00:00" 1103 | }, 1104 | { 1105 | "name": "sebastian/object-enumerator", 1106 | "version": "3.0.3", 1107 | "source": { 1108 | "type": "git", 1109 | "url": "https://github.com/sebastianbergmann/object-enumerator.git", 1110 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" 1111 | }, 1112 | "dist": { 1113 | "type": "zip", 1114 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1115 | "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", 1116 | "shasum": "" 1117 | }, 1118 | "require": { 1119 | "php": "^7.0", 1120 | "sebastian/object-reflector": "^1.1.1", 1121 | "sebastian/recursion-context": "^3.0" 1122 | }, 1123 | "require-dev": { 1124 | "phpunit/phpunit": "^6.0" 1125 | }, 1126 | "type": "library", 1127 | "extra": { 1128 | "branch-alias": { 1129 | "dev-master": "3.0.x-dev" 1130 | } 1131 | }, 1132 | "autoload": { 1133 | "classmap": [ 1134 | "src/" 1135 | ] 1136 | }, 1137 | "notification-url": "https://packagist.org/downloads/", 1138 | "license": [ 1139 | "BSD-3-Clause" 1140 | ], 1141 | "authors": [ 1142 | { 1143 | "name": "Sebastian Bergmann", 1144 | "email": "sebastian@phpunit.de" 1145 | } 1146 | ], 1147 | "description": "Traverses array structures and object graphs to enumerate all referenced objects", 1148 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/", 1149 | "time": "2017-08-03T12:35:26+00:00" 1150 | }, 1151 | { 1152 | "name": "sebastian/object-reflector", 1153 | "version": "1.1.1", 1154 | "source": { 1155 | "type": "git", 1156 | "url": "https://github.com/sebastianbergmann/object-reflector.git", 1157 | "reference": "773f97c67f28de00d397be301821b06708fca0be" 1158 | }, 1159 | "dist": { 1160 | "type": "zip", 1161 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", 1162 | "reference": "773f97c67f28de00d397be301821b06708fca0be", 1163 | "shasum": "" 1164 | }, 1165 | "require": { 1166 | "php": "^7.0" 1167 | }, 1168 | "require-dev": { 1169 | "phpunit/phpunit": "^6.0" 1170 | }, 1171 | "type": "library", 1172 | "extra": { 1173 | "branch-alias": { 1174 | "dev-master": "1.1-dev" 1175 | } 1176 | }, 1177 | "autoload": { 1178 | "classmap": [ 1179 | "src/" 1180 | ] 1181 | }, 1182 | "notification-url": "https://packagist.org/downloads/", 1183 | "license": [ 1184 | "BSD-3-Clause" 1185 | ], 1186 | "authors": [ 1187 | { 1188 | "name": "Sebastian Bergmann", 1189 | "email": "sebastian@phpunit.de" 1190 | } 1191 | ], 1192 | "description": "Allows reflection of object attributes, including inherited and non-public ones", 1193 | "homepage": "https://github.com/sebastianbergmann/object-reflector/", 1194 | "time": "2017-03-29T09:07:27+00:00" 1195 | }, 1196 | { 1197 | "name": "sebastian/recursion-context", 1198 | "version": "3.0.0", 1199 | "source": { 1200 | "type": "git", 1201 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1202 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" 1203 | }, 1204 | "dist": { 1205 | "type": "zip", 1206 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1207 | "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", 1208 | "shasum": "" 1209 | }, 1210 | "require": { 1211 | "php": "^7.0" 1212 | }, 1213 | "require-dev": { 1214 | "phpunit/phpunit": "^6.0" 1215 | }, 1216 | "type": "library", 1217 | "extra": { 1218 | "branch-alias": { 1219 | "dev-master": "3.0.x-dev" 1220 | } 1221 | }, 1222 | "autoload": { 1223 | "classmap": [ 1224 | "src/" 1225 | ] 1226 | }, 1227 | "notification-url": "https://packagist.org/downloads/", 1228 | "license": [ 1229 | "BSD-3-Clause" 1230 | ], 1231 | "authors": [ 1232 | { 1233 | "name": "Jeff Welch", 1234 | "email": "whatthejeff@gmail.com" 1235 | }, 1236 | { 1237 | "name": "Sebastian Bergmann", 1238 | "email": "sebastian@phpunit.de" 1239 | }, 1240 | { 1241 | "name": "Adam Harvey", 1242 | "email": "aharvey@php.net" 1243 | } 1244 | ], 1245 | "description": "Provides functionality to recursively process PHP variables", 1246 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1247 | "time": "2017-03-03T06:23:57+00:00" 1248 | }, 1249 | { 1250 | "name": "sebastian/resource-operations", 1251 | "version": "2.0.1", 1252 | "source": { 1253 | "type": "git", 1254 | "url": "https://github.com/sebastianbergmann/resource-operations.git", 1255 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" 1256 | }, 1257 | "dist": { 1258 | "type": "zip", 1259 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1260 | "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", 1261 | "shasum": "" 1262 | }, 1263 | "require": { 1264 | "php": "^7.1" 1265 | }, 1266 | "type": "library", 1267 | "extra": { 1268 | "branch-alias": { 1269 | "dev-master": "2.0-dev" 1270 | } 1271 | }, 1272 | "autoload": { 1273 | "classmap": [ 1274 | "src/" 1275 | ] 1276 | }, 1277 | "notification-url": "https://packagist.org/downloads/", 1278 | "license": [ 1279 | "BSD-3-Clause" 1280 | ], 1281 | "authors": [ 1282 | { 1283 | "name": "Sebastian Bergmann", 1284 | "email": "sebastian@phpunit.de" 1285 | } 1286 | ], 1287 | "description": "Provides a list of PHP built-in functions that operate on resources", 1288 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations", 1289 | "time": "2018-10-04T04:07:39+00:00" 1290 | }, 1291 | { 1292 | "name": "sebastian/type", 1293 | "version": "1.1.3", 1294 | "source": { 1295 | "type": "git", 1296 | "url": "https://github.com/sebastianbergmann/type.git", 1297 | "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" 1298 | }, 1299 | "dist": { 1300 | "type": "zip", 1301 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", 1302 | "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", 1303 | "shasum": "" 1304 | }, 1305 | "require": { 1306 | "php": "^7.2" 1307 | }, 1308 | "require-dev": { 1309 | "phpunit/phpunit": "^8.2" 1310 | }, 1311 | "type": "library", 1312 | "extra": { 1313 | "branch-alias": { 1314 | "dev-master": "1.1-dev" 1315 | } 1316 | }, 1317 | "autoload": { 1318 | "classmap": [ 1319 | "src/" 1320 | ] 1321 | }, 1322 | "notification-url": "https://packagist.org/downloads/", 1323 | "license": [ 1324 | "BSD-3-Clause" 1325 | ], 1326 | "authors": [ 1327 | { 1328 | "name": "Sebastian Bergmann", 1329 | "email": "sebastian@phpunit.de", 1330 | "role": "lead" 1331 | } 1332 | ], 1333 | "description": "Collection of value objects that represent the types of the PHP type system", 1334 | "homepage": "https://github.com/sebastianbergmann/type", 1335 | "time": "2019-07-02T08:10:15+00:00" 1336 | }, 1337 | { 1338 | "name": "sebastian/version", 1339 | "version": "2.0.1", 1340 | "source": { 1341 | "type": "git", 1342 | "url": "https://github.com/sebastianbergmann/version.git", 1343 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" 1344 | }, 1345 | "dist": { 1346 | "type": "zip", 1347 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", 1348 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", 1349 | "shasum": "" 1350 | }, 1351 | "require": { 1352 | "php": ">=5.6" 1353 | }, 1354 | "type": "library", 1355 | "extra": { 1356 | "branch-alias": { 1357 | "dev-master": "2.0.x-dev" 1358 | } 1359 | }, 1360 | "autoload": { 1361 | "classmap": [ 1362 | "src/" 1363 | ] 1364 | }, 1365 | "notification-url": "https://packagist.org/downloads/", 1366 | "license": [ 1367 | "BSD-3-Clause" 1368 | ], 1369 | "authors": [ 1370 | { 1371 | "name": "Sebastian Bergmann", 1372 | "email": "sebastian@phpunit.de", 1373 | "role": "lead" 1374 | } 1375 | ], 1376 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1377 | "homepage": "https://github.com/sebastianbergmann/version", 1378 | "time": "2016-10-03T07:35:21+00:00" 1379 | }, 1380 | { 1381 | "name": "symfony/polyfill-ctype", 1382 | "version": "v1.11.0", 1383 | "source": { 1384 | "type": "git", 1385 | "url": "https://github.com/symfony/polyfill-ctype.git", 1386 | "reference": "82ebae02209c21113908c229e9883c419720738a" 1387 | }, 1388 | "dist": { 1389 | "type": "zip", 1390 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", 1391 | "reference": "82ebae02209c21113908c229e9883c419720738a", 1392 | "shasum": "" 1393 | }, 1394 | "require": { 1395 | "php": ">=5.3.3" 1396 | }, 1397 | "suggest": { 1398 | "ext-ctype": "For best performance" 1399 | }, 1400 | "type": "library", 1401 | "extra": { 1402 | "branch-alias": { 1403 | "dev-master": "1.11-dev" 1404 | } 1405 | }, 1406 | "autoload": { 1407 | "psr-4": { 1408 | "Symfony\\Polyfill\\Ctype\\": "" 1409 | }, 1410 | "files": [ 1411 | "bootstrap.php" 1412 | ] 1413 | }, 1414 | "notification-url": "https://packagist.org/downloads/", 1415 | "license": [ 1416 | "MIT" 1417 | ], 1418 | "authors": [ 1419 | { 1420 | "name": "Symfony Community", 1421 | "homepage": "https://symfony.com/contributors" 1422 | }, 1423 | { 1424 | "name": "Gert de Pagter", 1425 | "email": "BackEndTea@gmail.com" 1426 | } 1427 | ], 1428 | "description": "Symfony polyfill for ctype functions", 1429 | "homepage": "https://symfony.com", 1430 | "keywords": [ 1431 | "compatibility", 1432 | "ctype", 1433 | "polyfill", 1434 | "portable" 1435 | ], 1436 | "time": "2019-02-06T07:57:58+00:00" 1437 | }, 1438 | { 1439 | "name": "theseer/tokenizer", 1440 | "version": "1.1.3", 1441 | "source": { 1442 | "type": "git", 1443 | "url": "https://github.com/theseer/tokenizer.git", 1444 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" 1445 | }, 1446 | "dist": { 1447 | "type": "zip", 1448 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 1449 | "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", 1450 | "shasum": "" 1451 | }, 1452 | "require": { 1453 | "ext-dom": "*", 1454 | "ext-tokenizer": "*", 1455 | "ext-xmlwriter": "*", 1456 | "php": "^7.0" 1457 | }, 1458 | "type": "library", 1459 | "autoload": { 1460 | "classmap": [ 1461 | "src/" 1462 | ] 1463 | }, 1464 | "notification-url": "https://packagist.org/downloads/", 1465 | "license": [ 1466 | "BSD-3-Clause" 1467 | ], 1468 | "authors": [ 1469 | { 1470 | "name": "Arne Blankerts", 1471 | "email": "arne@blankerts.de", 1472 | "role": "Developer" 1473 | } 1474 | ], 1475 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", 1476 | "time": "2019-06-13T22:48:21+00:00" 1477 | }, 1478 | { 1479 | "name": "webmozart/assert", 1480 | "version": "1.4.0", 1481 | "source": { 1482 | "type": "git", 1483 | "url": "https://github.com/webmozart/assert.git", 1484 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" 1485 | }, 1486 | "dist": { 1487 | "type": "zip", 1488 | "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", 1489 | "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", 1490 | "shasum": "" 1491 | }, 1492 | "require": { 1493 | "php": "^5.3.3 || ^7.0", 1494 | "symfony/polyfill-ctype": "^1.8" 1495 | }, 1496 | "require-dev": { 1497 | "phpunit/phpunit": "^4.6", 1498 | "sebastian/version": "^1.0.1" 1499 | }, 1500 | "type": "library", 1501 | "extra": { 1502 | "branch-alias": { 1503 | "dev-master": "1.3-dev" 1504 | } 1505 | }, 1506 | "autoload": { 1507 | "psr-4": { 1508 | "Webmozart\\Assert\\": "src/" 1509 | } 1510 | }, 1511 | "notification-url": "https://packagist.org/downloads/", 1512 | "license": [ 1513 | "MIT" 1514 | ], 1515 | "authors": [ 1516 | { 1517 | "name": "Bernhard Schussek", 1518 | "email": "bschussek@gmail.com" 1519 | } 1520 | ], 1521 | "description": "Assertions to validate method input/output with nice error messages.", 1522 | "keywords": [ 1523 | "assert", 1524 | "check", 1525 | "validate" 1526 | ], 1527 | "time": "2018-12-25T11:19:39+00:00" 1528 | } 1529 | ], 1530 | "packages-dev": [], 1531 | "aliases": [], 1532 | "minimum-stability": "stable", 1533 | "stability-flags": [], 1534 | "prefer-stable": false, 1535 | "prefer-lowest": false, 1536 | "platform": { 1537 | "php": ">=5.3.3" 1538 | }, 1539 | "platform-dev": [] 1540 | } 1541 | --------------------------------------------------------------------------------