Changeset 888 for trunk/HTML/BBCodeParser2.php
- Timestamp:
- Dec 27, 2022, 7:50:23 PM (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/HTML/BBCodeParser2.php
r887 r888 84 84 * @var array 85 85 */ 86 var$_definedTags = array();86 private array $_definedTags = array(); 87 87 88 88 /** … … 92 92 * @var string 93 93 */ 94 var $_text= '';94 public string $_text = ''; 95 95 96 96 /** … … 100 100 * @var string 101 101 */ 102 var $_preparsed= '';102 private string $_preparsed = ''; 103 103 104 104 /** … … 108 108 * @var array 109 109 */ 110 var $_tagArray= array();110 private array $_tagArray = array(); 111 111 112 112 /** … … 116 116 * @var string 117 117 */ 118 var $_parsed= '';118 private string $_parsed = ''; 119 119 120 120 /** … … 124 124 * @var array 125 125 */ 126 var$_options = array(126 public array $_options = array( 127 127 'quotestyle' => 'double', 128 128 'quotewhat' => 'all', … … 139 139 * @var array 140 140 */ 141 var $_filters= array();141 private array $_filters = array(); 142 142 143 143 /** … … 155 155 * @author Stijn de Reede <sjr@gmx.co.uk> 156 156 */ 157 function __construct( $options = array())157 function __construct(array $options = array()) 158 158 { 159 159 // set the options passed as an argument 160 foreach ($options as $k => $v ) { 160 foreach ($options as $k => $v ) 161 { 161 162 $this->_options[$k] = $v; 162 163 } … … 166 167 if ($this->_options['open'] != '' && strpos($preg_escape, $this->_options['open'])) { 167 168 $this->_options['open_esc'] = "\\".$this->_options['open']; 168 } else { 169 } else 170 { 169 171 $this->_options['open_esc'] = $this->_options['open']; 170 172 } 171 173 if ($this->_options['close'] != '' && strpos($preg_escape, $this->_options['close'])) { 172 174 $this->_options['close_esc'] = "\\".$this->_options['close']; 173 } else { 175 } else 176 { 174 177 $this->_options['close_esc'] = $this->_options['close']; 175 178 } … … 180 183 181 184 // return if this is a subclass 182 if (is_subclass_of($this, 'HTML_BBCodeParser2_Filter')) { 185 if (is_subclass_of($this, 'HTML_BBCodeParser2_Filter')) 186 { 183 187 return; 184 188 } … … 195 199 * @author Lorenzo Alberton <l.alberton@quipo.it> 196 200 */ 197 function setOption( $name, $value)201 function setOption(string $name, $value): void 198 202 { 199 203 $this->_options[$name] = $value; … … 206 210 * @author Lorenzo Alberton <l.alberton@quipo.it> 207 211 */ 208 function addFilter( $filter)212 function addFilter(string $filter) 209 213 { 210 214 $filter = ucfirst($filter); 211 if (!array_key_exists($filter, $this->_filters)) { 215 if (!array_key_exists($filter, $this->_filters)) 216 { 212 217 $class = 'HTML_BBCodeParser2_Filter_'.$filter; 213 218 @include_once 'HTML/BBCodeParser2/Filter/'.$filter.'.php'; 214 if (!class_exists($class)) { 219 if (!class_exists($class)) 220 { 215 221 throw new InvalidArgumentException("Failed to load filter $filter"); 216 222 } … … 229 235 * @author Lorenzo Alberton <l.alberton@quipo.it> 230 236 */ 231 function removeFilter($filter) 237 function removeFilter($filter): void 232 238 { 233 239 $filter = ucfirst(trim($filter)); 234 if (!empty($filter) && array_key_exists($filter, $this->_filters)) { 240 if (!empty($filter) && array_key_exists($filter, $this->_filters)) 241 { 235 242 unset($this->_filters[$filter]); 236 243 } … … 238 245 // preserving the others 239 246 $this->_definedTags = array(); 240 foreach (array_keys($this->_filters) as $filter) { 247 foreach (array_keys($this->_filters) as $filter) 248 { 241 249 $this->_definedTags = array_merge( 242 250 $this->_definedTags, … … 253 261 * @author Lorenzo Alberton <l.alberton@quipo.it> 254 262 */ 255 function addFilters($filters) 256 { 257 if (is_string($filters)) { 263 function addFilters($filters): bool 264 { 265 if (is_string($filters)) 266 { 258 267 //comma-separated list 259 if (strpos($filters, ',') !== false) { 268 if (strpos($filters, ',') !== false) 269 { 260 270 $filters = explode(',', $filters); 261 } else { 271 } else 272 { 262 273 $filters = array($filters); 263 274 } 264 275 } 265 if (!is_array($filters)) { 276 if (!is_array($filters)) 277 { 266 278 //invalid format 267 279 return false; 268 280 } 269 foreach ($filters as $filter) { 270 if (trim($filter)){ 281 foreach ($filters as $filter) 282 { 283 if (trim($filter)) 284 { 271 285 $this->addFilter($filter); 272 286 } … … 291 305 * @author Stijn de Reede <sjr@gmx.co.uk> 292 306 */ 293 function _preparse() 307 function _preparse(): void 294 308 { 295 309 // default: assign _text to _preparsed, to be overwritten by filters … … 297 311 298 312 // return if this is a subclass 299 if (is_subclass_of($this, 'HTML_BBCodeParser2')) { 313 if (is_subclass_of($this, 'HTML_BBCodeParser2')) 314 { 300 315 return; 301 316 } 302 317 303 318 // walk through the filters and execute _preparse 304 foreach ($this->_filters as $filter) { 319 foreach ($this->_filters as $filter) 320 { 305 321 $filter->setText($this->_preparsed); 306 322 $filter->_preparse(); … … 326 342 * @author Stijn de Reede <sjr@gmx.co.uk> 327 343 */ 328 function _buildTagArray() 344 function _buildTagArray(): void 329 345 { 330 346 $this->_tagArray = array(); … … 333 349 $strLength = strlen($str); 334 350 335 while (($strPos < $strLength)) { 351 while (($strPos < $strLength)) 352 { 336 353 $tag = array(); 337 354 $openPos = strpos($str, $this->_options['open'], $strPos); 338 if ($openPos === false) { 355 if ($openPos === false) 356 { 339 357 $openPos = $strLength; 340 358 $nextOpenPos = $strLength; 341 359 } 342 if ($openPos + 1 > $strLength) { 360 if ($openPos + 1 > $strLength) 361 { 343 362 $nextOpenPos = $strLength; 344 } else { 363 } else 364 { 345 365 $nextOpenPos = strpos($str, $this->_options['open'], $openPos + 1); 346 if ($nextOpenPos === false) { 366 if ($nextOpenPos === false) 367 { 347 368 $nextOpenPos = $strLength; 348 369 } 349 370 } 350 371 $closePos = strpos($str, $this->_options['close'], $strPos); 351 if ($closePos === false) { 372 if ($closePos === false) 373 { 352 374 $closePos = $strLength + 1; 353 375 } 354 376 355 if ($openPos == $strPos) { 356 if (($nextOpenPos < $closePos)) { 377 if ($openPos == $strPos) 378 { 379 if (($nextOpenPos < $closePos)) 380 { 357 381 // new open tag before closing tag: treat as text 358 382 $newPos = $nextOpenPos; 359 383 $tag['text'] = substr($str, $strPos, $nextOpenPos - $strPos); 360 384 $tag['type'] = 0; 361 } else { 385 } else 386 { 362 387 // possible valid tag 363 388 $newPos = $closePos + 1; 364 389 $newTag = $this->_buildTag(substr($str, $strPos, $closePos - $strPos + 1)); 365 if (($newTag !== false)) { 390 if (($newTag !== false)) 391 { 366 392 $tag = $newTag; 367 } else { 393 } else 394 { 368 395 // no valid tag after all 369 396 $tag['text'] = substr($str, $strPos, $closePos - $strPos + 1); … … 371 398 } 372 399 } 373 } else { 400 } else 401 { 374 402 // just text 375 403 $newPos = $openPos; … … 379 407 380 408 // join 2 following text elements 381 if ($tag['type'] === 0 && isset($prev) && $prev['type'] === 0) { 409 if ($tag['type'] === 0 && isset($prev) && $prev['type'] === 0) 410 { 382 411 $tag['text'] = $prev['text'].$tag['text']; 383 412 array_pop($this->_tagArray); … … 404 433 * @author Stijn de Reede <sjr@gmx.co.uk> 405 434 */ 406 function _buildTag( $str)435 function _buildTag(string $str): bool|array 407 436 { 408 437 $tag = array('text' => $str, 'attributes' => array()); 409 438 410 if (substr($str, 1, 1) == '/') { // closing tag411 439 if (substr($str, 1, 1) == '/') 440 { // closing tag 412 441 $tag['tag'] = strtolower(substr($str, 2, strlen($str) - 3)); 413 if (!in_array($tag['tag'], array_keys($this->_definedTags))) { 442 if (!in_array($tag['tag'], array_keys($this->_definedTags))) 443 { 414 444 return false; // nope, it's not valid 415 } else { 445 } else 446 { 416 447 $tag['type'] = 2; 417 448 return $tag; 418 449 } 419 } else { // opening tag420 450 } else 451 { // opening tag 421 452 $tag['type'] = 1; 422 if (strpos($str, ' ') && (strpos($str, '=') === false)) { 453 if (strpos($str, ' ') && (strpos($str, '=') === false)) 454 { 423 455 return false; // nope, it's not valid 424 456 } … … 429 461 $ce = $this->_options['close_esc']; 430 462 $tagArray = array(); 431 if (preg_match("!$oe([a-z0-9]+)[^$ce]*$ce!i", $str, $tagArray) == 0) { 463 if (preg_match("!$oe([a-z0-9]+)[^$ce]*$ce!i", $str, $tagArray) == 0) 464 { 432 465 return false; 433 466 } 434 467 $tag['tag'] = strtolower($tagArray[1]); 435 if (!in_array($tag['tag'], array_keys($this->_definedTags))) { 468 if (!in_array($tag['tag'], array_keys($this->_definedTags))) 469 { 436 470 return false; // nope, it's not valid 437 471 } … … 441 475 $attributeArray = array(); 442 476 $regex = "![\s$oe]([a-z0-9]+)=(\"[^\s$ce]+\"|[^\s$ce]"; 443 if ($tag['tag'] != 'url') { 477 if ($tag['tag'] != 'url') 478 { 444 479 $regex .= "[^=]"; 445 480 } 446 481 $regex .= "+)(?=[\s$ce])!i"; 447 482 preg_match_all($regex, $str, $attributeArray, PREG_SET_ORDER); 448 foreach ($attributeArray as $attribute) { 483 foreach ($attributeArray as $attribute) 484 { 449 485 $attNam = strtolower($attribute[1]); 450 if (in_array($attNam, array_keys($this->_definedTags[$tag['tag']]['attributes']))) { 451 if ($attribute[2][0] == '"' && $attribute[2][strlen($attribute[2])-1] == '"') { 486 if (in_array($attNam, array_keys($this->_definedTags[$tag['tag']]['attributes']))) 487 { 488 if ($attribute[2][0] == '"' && $attribute[2][strlen($attribute[2])-1] == '"') 489 { 452 490 $tag['attributes'][$attNam] = substr($attribute[2], 1, -1); 453 } else { 491 } else 492 { 454 493 $tag['attributes'][$attNam] = $attribute[2]; 455 494 } … … 476 515 * @author Stijn de Reede <sjr@gmx.co.uk>, Seth Price <seth@pricepages.org> 477 516 */ 478 function _validateTagArray() 517 function _validateTagArray(): void 479 518 { 480 519 $newTagArray = array(); 481 520 $openTags = array(); 482 foreach ($this->_tagArray as $tag) { 521 foreach ($this->_tagArray as $tag) 522 { 523 echo('newTagArray: '); 524 print_r($newTagArray); 483 525 $prevTag = end($newTagArray); 484 526 485 527 // TODO: why prevTag cann by type bool? 486 if (!is_array($prevTag)) continue; 528 if (!is_array($prevTag)) 529 { 530 echo('tag: '); 531 print_r($tag); 532 continue; 533 } 487 534 488 535 switch ($tag['type']) { … … 497 544 $child !== true ) 498 545 { 499 if (trim($tag['text']) == '') { 546 if (trim($tag['text']) == '') 547 { 500 548 //just an empty indentation or newline without value? 501 continue ;549 continue 2; 502 550 } 503 551 $newTagArray[] = $child; … … 505 553 } 506 554 507 if ($prevTag['type'] === 0) { 555 if ($prevTag['type'] === 0) 556 { 508 557 $tag['text'] = $prevTag['text'].$tag['text']; 509 558 array_pop($newTagArray); … … 515 564 if (!$this->_isAllowed(end($openTags), $tag['tag']) || 516 565 ($parent = $this->_parentNeeded(end($openTags), $tag['tag'])) === true || 517 ($child = $this->_childNeeded(end($openTags), $tag['tag'])) === true) { 566 ($child = $this->_childNeeded(end($openTags), $tag['tag'])) === true) 567 { 518 568 $tag['type'] = 0; 519 if ($prevTag['type'] === 0) { 569 if ($prevTag['type'] === 0) 570 { 520 571 $tag['text'] = $prevTag['text'].$tag['text']; 521 572 array_pop($newTagArray); 522 573 } 523 } else { 524 if ($parent) { 574 } else 575 { 576 if ($parent) 577 { 525 578 /* 526 579 * Avoid use of parent if we can help it. If we are … … 531 584 * current tag. 532 585 */ 533 if ($tag['tag'] == end($openTags)){ 586 if ($tag['tag'] == end($openTags)) 587 { 534 588 $newTagArray[] = $this->_buildTag('[/'.$tag['tag'].']'); 535 589 array_pop($openTags); 536 } else { 590 } else 591 { 537 592 $newTagArray[] = $parent; 538 593 $openTags[] = $parent['tag']; … … 549 604 550 605 case 2: 551 if (($tag['tag'] == end($openTags) || $this->_isAllowed(end($openTags), $tag['tag']))) { 552 if (in_array($tag['tag'], $openTags)) { 606 if (($tag['tag'] == end($openTags) || $this->_isAllowed(end($openTags), $tag['tag']))) 607 { 608 if (in_array($tag['tag'], $openTags)) 609 { 553 610 $tmpOpenTags = array(); 554 while (end($openTags) != $tag['tag']) { 611 while (end($openTags) != $tag['tag']) 612 { 555 613 $newTagArray[] = $this->_buildTag('[/'.end($openTags).']'); 556 614 $tmpOpenTags[] = end($openTags); … … 569 627 }*/ 570 628 } 571 } else { 629 } else 630 { 572 631 $tag['type'] = 0; 573 if ($prevTag['type'] === 0) { 632 if ($prevTag['type'] === 0) 633 { 574 634 $tag['text'] = $prevTag['text'].$tag['text']; 575 635 array_pop($newTagArray); … … 580 640 } 581 641 } 582 while (end($openTags)) { 642 while (end($openTags)) 643 { 583 644 $newTagArray[] = $this->_buildTag('[/'.end($openTags).']'); 584 645 array_pop($openTags); … … 605 666 { 606 667 if (!isset($this->_definedTags[$in]['parent']) || 607 ($this->_definedTags[$in]['parent'] == 'all') 608 ){668 ($this->_definedTags[$in]['parent'] == 'all')) 669 { 609 670 return false; 610 671 } … … 612 673 $ar = explode('^', $this->_definedTags[$in]['parent']); 613 674 $tags = explode(',', $ar[1]); 614 if ($ar[0] == 'none'){ 615 if ($out && in_array($out, $tags)) { 675 if ($ar[0] == 'none') 676 { 677 if ($out && in_array($out, $tags)) 678 { 616 679 return false; 617 680 } … … 619 682 return $this->_buildTag('['.$tags[0].']'); 620 683 } 621 if ($ar[0] == 'all' && $out && !in_array($out, $tags)) { 684 if ($ar[0] == 'all' && $out && !in_array($out, $tags)) 685 { 622 686 return false; 623 687 } … … 642 706 * @author Seth Price <seth@pricepages.org> 643 707 */ 644 function _childNeeded($out, $in) 708 function _childNeeded($out, $in): bool 645 709 { 646 710 if (!isset($this->_definedTags[$out]['child']) || 647 ($this->_definedTags[$out]['child'] == 'all') 648 ){711 ($this->_definedTags[$out]['child'] == 'all')) 712 { 649 713 return false; 650 714 } … … 652 716 $ar = explode('^', $this->_definedTags[$out]['child']); 653 717 $tags = explode(',', $ar[1]); 654 if ($ar[0] == 'none'){ 655 if ($in && in_array($in, $tags)) { 718 if ($ar[0] == 'none') 719 { 720 if ($in && in_array($in, $tags)) 721 { 656 722 return false; 657 723 } … … 659 725 return $this->_buildTag('['.$tags[0].']'); 660 726 } 661 if ($ar[0] == 'all' && $in && !in_array($in, $tags)) { 727 if ($ar[0] == 'all' && $in && !in_array($in, $tags)) 728 { 662 729 return false; 663 730 } … … 680 747 * @author Stijn de Reede <sjr@gmx.co.uk> 681 748 */ 682 function _isAllowed($out, $in) 683 { 684 if (!$out || ($this->_definedTags[$out]['allowed'] == 'all')) { 749 function _isAllowed($out, $in): bool 750 { 751 if (!$out || ($this->_definedTags[$out]['allowed'] == 'all')) 752 { 685 753 return true; 686 754 } 687 if ($this->_definedTags[$out]['allowed'] == 'none') { 755 if ($this->_definedTags[$out]['allowed'] == 'none') 756 { 688 757 return false; 689 758 } … … 691 760 $ar = explode('^', $this->_definedTags[$out]['allowed']); 692 761 $tags = explode(',', $ar[1]); 693 if ($ar[0] == 'none' && in_array($in, $tags)) { 762 if ($ar[0] == 'none' && in_array($in, $tags)) 763 { 694 764 return true; 695 765 } 696 if ($ar[0] == 'all' && in_array($in, $tags)) { 766 if ($ar[0] == 'all' && in_array($in, $tags)) 767 { 697 768 return false; 698 769 } … … 712 783 * @author Stijn de Reede <sjr@gmx.co.uk> 713 784 */ 714 function _buildParsedString() 785 function _buildParsedString(): void 715 786 { 716 787 $this->_parsed = ''; 717 foreach ($this->_tagArray as $tag) { 718 switch ($tag['type']) { 788 foreach ($this->_tagArray as $tag) 789 { 790 switch ($tag['type']) 791 { 719 792 720 793 // just text … … 728 801 if ($this->_options['quotestyle'] == 'single') $q = "'"; 729 802 if ($this->_options['quotestyle'] == 'double') $q = '"'; 730 foreach ($tag['attributes'] as $a => $v) { 803 foreach ($tag['attributes'] as $a => $v) 804 { 731 805 //prevent XSS attacks. IMHO this is not enough, though... 732 806 //@see http://pear.php.net/bugs/bug.php?id=5609 … … 736 810 737 811 if (($this->_options['quotewhat'] == 'nothing') || 738 (($this->_options['quotewhat'] == 'strings') && is_numeric($v)) 739 ){812 (($this->_options['quotewhat'] == 'strings') && is_numeric($v))) 813 { 740 814 $this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, ''); 741 } else { 815 } else 816 { 742 817 $this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, $q); 743 818 } 744 819 } 745 if ($this->_definedTags[$tag['tag']]['htmlclose'] == '' && $this->_options['xmlclose']) { 820 if ($this->_definedTags[$tag['tag']]['htmlclose'] == '' && $this->_options['xmlclose']) 821 { 746 822 $this->_parsed .= ' /'; 747 823 } … … 751 827 // closing tag 752 828 case 2: 753 if ($this->_definedTags[$tag['tag']]['htmlclose'] != '') { 829 if ($this->_definedTags[$tag['tag']]['htmlclose'] != '') 830 { 754 831 $this->_parsed .= '</'.$this->_definedTags[$tag['tag']]['htmlclose'].'>'; 755 832 } … … 769 846 * @author Stijn de Reede <sjr@gmx.co.uk> 770 847 */ 771 function setText( $str)848 function setText(string $str): void 772 849 { 773 850 $this->_text = $str; … … 783 860 * @author Stijn de Reede <sjr@gmx.co.uk> 784 861 */ 785 function getText() 862 function getText(): string 786 863 { 787 864 return $this->_text; … … 797 874 * @author Stijn de Reede <sjr@gmx.co.uk> 798 875 */ 799 function getPreparsed() 876 function getPreparsed(): string 800 877 { 801 878 return $this->_preparsed; … … 811 888 * @author Stijn de Reede <sjr@gmx.co.uk> 812 889 */ 813 function getParsed() 890 function getParsed(): string 814 891 { 815 892 return $this->_parsed; … … 827 904 * @author Stijn de Reede <sjr@gmx.co.uk> 828 905 */ 829 function parse() 906 function parse(): void 830 907 { 831 908 $this->_preparse(); … … 838 915 * Quick method to do setText(), parse() and getParsed at once 839 916 * 917 * @return string 918 * @access public 919 * @see parse() 920 * @see $_text 921 * @author Stijn de Reede <sjr@gmx.co.uk> 922 */ 923 function qparse(string $str): string 924 { 925 $this->_text = $str; 926 $this->parse(); 927 return $this->_parsed; 928 } 929 930 /** 931 * Quick static method to do setText(), parse() and getParsed at once 932 * 840 933 * @return none 841 934 * @access public … … 844 937 * @author Stijn de Reede <sjr@gmx.co.uk> 845 938 */ 846 function qparse($str) 847 { 848 $this->_text = $str; 849 $this->parse(); 850 return $this->_parsed; 851 } 852 853 /** 854 * Quick static method to do setText(), parse() and getParsed at once 855 * 856 * @return none 857 * @access public 858 * @see parse() 859 * @see $_text 860 * @author Stijn de Reede <sjr@gmx.co.uk> 861 */ 862 function staticQparse($str) 863 { 864 $p = new HTML_BBCodeParser2(); 865 $str = $p->qparse($str); 866 unset($p); 939 function staticQparse(string $str): string 940 { 941 $Parser = new HTML_BBCodeParser2(); 942 $str = $Parser->qparse($str); 943 unset($Parser); 867 944 return $str; 868 945 } 869 946 } 870 ?>
Note:
See TracChangeset
for help on using the changeset viewer.