1<?php 2/** 3 * Copyright (c) 2021. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4 * 5 * This source code is licensed under the GPL license found in the 6 * COPYING file in the root directory of this source tree. 7 * 8 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9 * @author ComboStrap <support@combostrap.com> 10 * 11 */ 12 13namespace ComboStrap; 14 15use dokuwiki\Extension\SyntaxPlugin; 16use syntax_plugin_combo_media; 17 18 19/** 20 * Class Call 21 * @package ComboStrap 22 * 23 * A wrapper around what's called a call 24 * which is an array of information such 25 * the mode, the data 26 * 27 * The {@link CallStack} is the only syntax representation that 28 * is available in DokuWiki 29 */ 30class Call 31{ 32 33 const INLINE_DISPLAY = "inline"; 34 const BlOCK_DISPLAY = "block"; 35 /** 36 * List of inline components 37 * Used to manage white space before an unmatched string. 38 * The syntax tree of Dokuwiki (ie {@link \Doku_Handler::$calls}) 39 * has only data and no class, for now, we create this 40 * lists manually because this is a hassle to retrieve this information from {@link \DokuWiki_Syntax_Plugin::getType()} 41 */ 42 const INLINE_DOKUWIKI_COMPONENTS = array( 43 /** 44 * Formatting https://www.dokuwiki.org/devel:syntax_plugins#syntax_types 45 * Comes from the {@link \dokuwiki\Parsing\ParserMode\Formatting} class 46 */ 47 "cdata", 48 "unformatted", // ie %% or nowiki 49 "doublequoteclosing", // https://www.dokuwiki.org/config:typography / https://www.dokuwiki.org/wiki:syntax#text_to_html_conversions 50 "doublequoteopening", 51 "singlequoteopening", 52 "singlequoteclosing", 53 "quote_open", 54 "quote_close", 55 "interwikilink", 56 "multiplyentity", 57 "apostrophe", 58 "deleted_open", 59 "deleted_close", 60 "emaillink", 61 "strong", 62 "emphasis", 63 "emphasis_open", 64 "emphasis_close", 65 "underline", 66 "underline_close", 67 "underline_open", 68 "monospace", 69 "subscript", 70 "subscript_open", 71 "subscript_close", 72 "superscript_open", 73 "superscript_close", 74 "superscript", 75 "deleted", 76 "footnote", 77 /** 78 * Others 79 */ 80 "acronym", // abbr 81 "strong_close", 82 "strong_open", 83 "monospace_open", 84 "monospace_close", 85 "doublequoteopening", // ie the character " in "The" 86 "entity", // for instance `...` are transformed in character 87 "linebreak", 88 "externallink", 89 "internallink", 90 "smiley", 91 MediaMarkup::INTERNAL_MEDIA_CALL_NAME, 92 MediaMarkup::EXTERNAL_MEDIA_CALL_NAME, 93 /** 94 * The inline of combo 95 */ 96 \syntax_plugin_combo_link::TAG, 97 IconTag::TAG, 98 NoteTag::TAG_INOTE, 99 ButtonTag::MARKUP_LONG, 100 \syntax_plugin_combo_tooltip::TAG, 101 PipelineTag::TAG, 102 BreadcrumbTag::MARKUP_BLOCK, // only the typo is inline but yeah 103 ); 104 105 106 const BLOCK_MARKUP_DOKUWIKI_COMPONENTS = array( 107 "listu_open", // ul 108 "listu_close", 109 "listo_open", 110 "listo_close", 111 "listitem_open", //li 112 "listitem_close", 113 "listcontent_open", // after li ??? 114 "listcontent_close", 115 "table_open", 116 "table_close", 117 "p_open", 118 "p_close", 119 "nest", // seen as enclosing footnotes 120 "hr", 121 "rss" 122 ); 123 124 /** 125 * Not inline, not block 126 */ 127 const TABLE_MARKUP = array( 128 "tablethead_open", 129 "tablethead_close", 130 "tableheader_open", 131 "tableheader_close", 132 "tablerow_open", 133 "tablerow_close", 134 "tablecell_open", 135 "tablecell_close" 136 ); 137 138 /** 139 * A media is not really an image 140 * but it may contains one 141 */ 142 const IMAGE_TAGS = [ 143 syntax_plugin_combo_media::TAG, 144 PageImageTag::MARKUP 145 ]; 146 const CANONICAL = "call"; 147 const TABLE_DISPLAY = "table-display"; 148 149 private $call; 150 151 /** 152 * The key identifier in the {@link CallStack} 153 * @var mixed|string 154 */ 155 private $key; 156 157 /** 158 * Call constructor. 159 * @param $call - the instruction array (ie called a call) 160 */ 161 public function __construct(&$call, $key = "") 162 { 163 $this->call = &$call; 164 $this->key = $key; 165 } 166 167 /** 168 * Insert a tag above 169 * @param $tagName 170 * @param $state 171 * @param $syntaxComponentName - the name of the dokuwiki syntax component (ie plugin_name) 172 * @param array $attribute 173 * @param string|null $rawContext 174 * @param string|null $content - the parsed content 175 * @param string|null $payload - the payload after handler 176 * @param int|null $position 177 * @return Call - a call 178 */ 179 public static function createComboCall($tagName, $state, array $attribute = array(), string $rawContext = null, string $content = null, string $payload = null, int $position = null, string $syntaxComponentName = null): Call 180 { 181 $data = array( 182 PluginUtility::ATTRIBUTES => $attribute, 183 PluginUtility::CONTEXT => $rawContext, 184 PluginUtility::STATE => $state, 185 PluginUtility::TAG => $tagName, 186 PluginUtility::POSITION => $position 187 ); 188 if ($payload !== null) { 189 $data[PluginUtility::PAYLOAD] = $payload; 190 } 191 $positionInText = $position; 192 193 if ($syntaxComponentName !== null) { 194 $componentName = PluginUtility::getComponentName($syntaxComponentName); 195 } else { 196 $componentName = PluginUtility::getComponentName($tagName); 197 } 198 $obj = plugin_load('syntax', $componentName); 199 if ($obj === null) { 200 throw new ExceptionRuntimeInternal("The component tag ($componentName) does not exists"); 201 } 202 203 $call = [ 204 "plugin", 205 array( 206 $componentName, 207 $data, 208 $state, 209 $content 210 ), 211 $positionInText 212 ]; 213 return new Call($call); 214 } 215 216 /** 217 * Insert a dokuwiki call 218 * @param $callName 219 * @param $array 220 * @param $positionInText 221 * @return Call 222 */ 223 public static function createNativeCall($callName, $array = [], $positionInText = null): Call 224 { 225 $call = [ 226 $callName, 227 $array, 228 $positionInText 229 ]; 230 return new Call($call); 231 } 232 233 public static function createFromInstruction($instruction): Call 234 { 235 return new Call($instruction); 236 } 237 238 /** 239 * @param Call $call 240 * @return Call 241 */ 242 public static function createFromCall(Call $call): Call 243 { 244 return self::createFromInstruction($call->toCallArray()); 245 } 246 247 248 /** 249 * 250 * Return the tag name from a call array 251 * 252 * This is not the logical tag. 253 * This is much more what's called: 254 * * the component name for a plugin 255 * * or the handler name for dokuwiki 256 * 257 * For a plugin, this is equivalent 258 * to the {@link SyntaxPlugin::getPluginComponent()} 259 * 260 * This is not the fully qualified component name: 261 * * with the plugin as prefix such as in {@link Call::getComponentName()} 262 * * or with the `open` and `close` prefix such as `p_close` ... 263 * 264 * @return mixed|string 265 */ 266 public function getTagName() 267 { 268 269 $mode = $this->call[0]; 270 if ($mode != "plugin") { 271 272 /** 273 * This is a standard dokuwiki node 274 */ 275 $dokuWikiNodeName = $this->call[0]; 276 277 /** 278 * The dokwuiki node name has also the open and close notion 279 * We delete this is not in the doc and therefore not logical 280 */ 281 $tagName = str_replace("_close", "", $dokuWikiNodeName); 282 return str_replace("_open", "", $tagName); 283 } 284 285 /** 286 * This is a plugin node 287 */ 288 $pluginDokuData = $this->call[1]; 289 290 /** 291 * If the tag is set 292 */ 293 $pluginData = $pluginDokuData[1]; 294 if (isset($pluginData[PluginUtility::TAG])) { 295 return $pluginData[PluginUtility::TAG]; 296 } 297 298 $component = $pluginDokuData[0]; 299 if (!is_array($component)) { 300 /** 301 * Tag name from class 302 */ 303 $componentNames = explode("_", $component); 304 /** 305 * To take care of 306 * PHP Warning: sizeof(): Parameter must be an array or an object that implements Countable 307 * in lib/plugins/combo/class/Tag.php on line 314 308 */ 309 if (is_array($componentNames)) { 310 $tagName = $componentNames[sizeof($componentNames) - 1]; 311 } else { 312 $tagName = $component; 313 } 314 return $tagName; 315 } 316 317 // To resolve: explode() expects parameter 2 to be string, array given 318 LogUtility::msg("The call (" . print_r($this->call, true) . ") has an array and not a string as component (" . print_r($component, true) . "). Page: " . MarkupPath::createFromRequestedPage(), LogUtility::LVL_MSG_ERROR); 319 return ""; 320 321 322 } 323 324 325 /** 326 * The parser state 327 * @return mixed 328 * May be null (example eol, internallink, ...) 329 */ 330 public 331 function getState() 332 { 333 $mode = $this->call[0]; 334 if ($mode !== "plugin") { 335 336 /** 337 * There is no state because this is a standard 338 * dokuwiki syntax found in {@link \Doku_Renderer_xhtml} 339 * check if this is not a `...._close` or `...._open` 340 * to derive the state 341 */ 342 $mode = $this->call[0]; 343 $lastPositionSepName = strrpos($mode, "_"); 344 $closeOrOpen = substr($mode, $lastPositionSepName + 1); 345 switch ($closeOrOpen) { 346 case "open": 347 return DOKU_LEXER_ENTER; 348 case "close": 349 return DOKU_LEXER_EXIT; 350 default: 351 /** 352 * Let op null, is used 353 * in {@link CallStack::processEolToEndStack()} 354 * and things can break 355 */ 356 return null; 357 } 358 359 } else { 360 // Plugin 361 $returnedArray = $this->call[1]; 362 if (array_key_exists(2, $returnedArray)) { 363 return $returnedArray[2]; 364 } else { 365 return null; 366 } 367 } 368 } 369 370 /** 371 * @return mixed the data returned from the {@link DokuWiki_Syntax_Plugin::handle} (ie attributes, payload, ...) 372 */ 373 public 374 function &getPluginData($attribute = null) 375 { 376 $data = &$this->call[1][1]; 377 if ($attribute === null) { 378 return $data; 379 } 380 return $data[$attribute]; 381 382 } 383 384 /** 385 * @return mixed the matched content from the {@link DokuWiki_Syntax_Plugin::handle} 386 */ 387 public 388 function getCapturedContent() 389 { 390 $caller = $this->call[0]; 391 switch ($caller) { 392 case "plugin": 393 return $this->call[1][3]; 394 case "internallink": 395 return '[[' . $this->call[1][0] . '|' . $this->call[1][1] . ']]'; 396 case "eol": 397 return DOKU_LF; 398 case "header": 399 case "cdata": 400 return $this->call[1][0]; 401 default: 402 if (isset($this->call[1][0]) && is_string($this->call[1][0])) { 403 return $this->call[1][0]; 404 } else { 405 return ""; 406 } 407 } 408 } 409 410 411 /** 412 * 413 */ 414 public 415 function &getAttributes(): ?array 416 { 417 418 419 $isPluginCall = $this->isPluginCall(); 420 if (!$isPluginCall) { 421 return $this->call[1]; 422 } else { 423 $data = &$this->getPluginData(); 424 if (!is_array($data)) { 425 LogUtility::error("The handle data is not an array for the call ($this), correct the returned data from the handle syntax plugin function", self::CANONICAL); 426 $data = []; 427 return $data; 428 } 429 if (!isset($data[PluginUtility::ATTRIBUTES])) { 430 $data[PluginUtility::ATTRIBUTES] = []; 431 } 432 $attributes = &$data[PluginUtility::ATTRIBUTES]; 433 if (!is_array($attributes)) { 434 $message = "The attributes value are not an array for the call ($this), the value was wrapped in an array"; 435 LogUtility::error($message, self::CANONICAL); 436 $attributes = [$attributes]; 437 } 438 return $attributes; 439 } 440 } 441 442 public 443 function removeAttributes() 444 { 445 446 $data = &$this->getPluginData(); 447 if (isset($data[PluginUtility::ATTRIBUTES])) { 448 unset($data[PluginUtility::ATTRIBUTES]); 449 } 450 451 } 452 453 public 454 function updateToPluginComponent($component, $state, $attributes = array()) 455 { 456 if ($this->call[0] == "plugin") { 457 $match = $this->call[1][3]; 458 } else { 459 $this->call[0] = "plugin"; 460 $match = ""; 461 } 462 $this->call[1] = array( 463 0 => $component, 464 1 => array( 465 PluginUtility::ATTRIBUTES => $attributes, 466 PluginUtility::STATE => $state, 467 ), 468 2 => $state, 469 3 => $match 470 ); 471 472 } 473 474 /** 475 * Does the display has been set 476 * to override the dokuwiki default 477 * ({@link Syntax::getPType()} 478 * 479 * because an image is by default a inline component 480 * but can be a block (ie top image of a card) 481 * @return bool 482 */ 483 public 484 function isDisplaySet(): bool 485 { 486 return isset($this->call[1][1][PluginUtility::DISPLAY]); 487 } 488 489 /** 490 * @return string|null 491 * {@link Call::INLINE_DISPLAY} or {@link Call::BlOCK_DISPLAY} 492 */ 493 public 494 function getDisplay(): ?string 495 { 496 $mode = $this->getMode(); 497 if ($mode == "plugin") { 498 if ($this->isDisplaySet()) { 499 return $this->call[1][1][PluginUtility::DISPLAY]; 500 } 501 } 502 503 if ($this->getState() == DOKU_LEXER_UNMATCHED) { 504 /** 505 * Unmatched are content (ie text node in XML/HTML) and have 506 * no display 507 */ 508 return Call::INLINE_DISPLAY; 509 } else { 510 $mode = $this->call[0]; 511 if ($mode == "plugin") { 512 global $DOKU_PLUGINS; 513 $component = $this->getComponentName(); 514 /** 515 * @var SyntaxPlugin $syntaxPlugin 516 */ 517 $syntaxPlugin = $DOKU_PLUGINS['syntax'][$component]; 518 $pType = $syntaxPlugin->getPType(); 519 switch ($pType) { 520 case "normal": 521 return Call::INLINE_DISPLAY; 522 case "block": 523 case "stack": 524 return Call::BlOCK_DISPLAY; 525 default: 526 LogUtility::msg("The ptype (" . $pType . ") is unknown."); 527 return null; 528 } 529 } else { 530 if ($mode == "eol") { 531 /** 532 * Control character 533 * We return it as it's used in the 534 * {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()} 535 * to create the paragraph 536 * This is not a block, nor an inline 537 */ 538 return $mode; 539 } 540 541 if (in_array($mode, self::INLINE_DOKUWIKI_COMPONENTS)) { 542 return Call::INLINE_DISPLAY; 543 } 544 545 if (in_array($mode, self::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) { 546 return Call::BlOCK_DISPLAY; 547 } 548 549 if (in_array($mode, self::TABLE_MARKUP)) { 550 return Call::TABLE_DISPLAY; 551 } 552 553 LogUtility::warning("The display of the call with the mode (" . $mode . ") is unknown"); 554 return null; 555 556 557 } 558 } 559 560 } 561 562 /** 563 * Same as {@link Call::getTagName()} 564 * but fully qualified 565 * @return string 566 */ 567 public 568 function getComponentName() 569 { 570 $mode = $this->call[0]; 571 if ($mode == "plugin") { 572 $pluginDokuData = $this->call[1]; 573 return $pluginDokuData[0]; 574 } else { 575 return $mode; 576 } 577 } 578 579 public 580 function updateEolToSpace() 581 { 582 $mode = $this->call[0]; 583 if ($mode != "eol") { 584 LogUtility::msg("You can't update a " . $mode . " to a space. It should be a eol", LogUtility::LVL_MSG_WARNING, "support"); 585 } else { 586 $this->call[0] = "cdata"; 587 $this->call[1] = array( 588 0 => " " 589 ); 590 } 591 592 } 593 594 public 595 function &addAttribute($key, $value) 596 { 597 $mode = $this->call[0]; 598 if ($mode == "plugin") { 599 $this->call[1][1][PluginUtility::ATTRIBUTES][$key] = $value; 600 // keep the new reference 601 return $this->call[1][1][PluginUtility::ATTRIBUTES][$key]; 602 } else { 603 LogUtility::msg("You can't add an attribute to the non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support"); 604 $whatever = []; 605 return $whatever; 606 } 607 } 608 609 public 610 function getContext() 611 { 612 $mode = $this->call[0]; 613 if ($mode === "plugin") { 614 return $this->call[1][1][PluginUtility::CONTEXT] ?? null; 615 } else { 616 LogUtility::msg("You can't ask for a context from a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support"); 617 return null; 618 } 619 } 620 621 /** 622 * 623 * @return array 624 */ 625 public 626 function toCallArray() 627 { 628 return $this->call; 629 } 630 631 public 632 function __toString() 633 { 634 $name = $this->key; 635 if (!empty($name)) { 636 $name .= " - "; 637 } 638 $name .= $this->getTagName(); 639 $name .= " - {$this->getStateName()}"; 640 return $name; 641 } 642 643 /** 644 * @return string|null 645 * 646 * If the type returned is a boolean attribute, 647 * it means you need to define the expected types 648 * in the function {@link TagAttributes::createFromTagMatch()} 649 * as third attribute 650 */ 651 public 652 function getType(): ?string 653 { 654 if ($this->getState() == DOKU_LEXER_UNMATCHED) { 655 return null; 656 } else { 657 return $this->getAttribute(TagAttributes::TYPE_KEY); 658 } 659 } 660 661 /** 662 * @param $key 663 * @param null $default 664 * @return array|string|null 665 */ 666 public 667 function &getAttribute($key, $default = null) 668 { 669 $attributes = &$this->getAttributes(); 670 if (isset($attributes[$key])) { 671 return $attributes[$key]; 672 } 673 return $default; 674 675 } 676 677 public 678 function getPayload() 679 { 680 $mode = $this->call[0]; 681 if ($mode == "plugin") { 682 return $this->call[1][1][PluginUtility::PAYLOAD]; 683 } else { 684 LogUtility::msg("You can't ask for a payload from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support"); 685 return null; 686 } 687 } 688 689 public 690 function setContext($value) 691 { 692 $this->call[1][1][PluginUtility::CONTEXT] = $value; 693 return $this; 694 } 695 696 public 697 function hasAttribute($attributeName): bool 698 { 699 $attributes = $this->getAttributes(); 700 if (isset($attributes[$attributeName])) { 701 return true; 702 } else { 703 if ($this->getType() == $attributeName) { 704 return true; 705 } else { 706 return false; 707 } 708 } 709 } 710 711 public 712 function isPluginCall() 713 { 714 return $this->call[0] === "plugin"; 715 } 716 717 /** 718 * @return mixed|string the position (ie key) in the array 719 */ 720 public 721 function getKey() 722 { 723 return $this->key; 724 } 725 726 public 727 function &getInstructionCall() 728 { 729 return $this->call; 730 } 731 732 public 733 function setState($state) 734 { 735 if ($this->call[0] == "plugin") { 736 // for dokuwiki 737 $this->call[1][2] = $state; 738 // for the combo plugin if any 739 if (isset($this->call[1][1][PluginUtility::STATE])) { 740 $this->call[1][1][PluginUtility::STATE] = $state; 741 } 742 } else { 743 LogUtility::msg("This modification of state is not yet supported for a native call"); 744 } 745 } 746 747 748 /** 749 * Return the position of the first matched character in the text file 750 * @return mixed 751 */ 752 public 753 function getFirstMatchedCharacterPosition() 754 { 755 756 return $this->call[2]; 757 758 } 759 760 /** 761 * Return the position of the last matched character in the text file 762 * 763 * This is the {@link Call::getFirstMatchedCharacterPosition()} 764 * plus the length of the {@link Call::getCapturedContent()} 765 * matched content 766 * @return int|mixed 767 */ 768 public 769 function getLastMatchedCharacterPosition() 770 { 771 $captureContent = $this->getCapturedContent(); 772 $length = 0; 773 if ($captureContent != null) { 774 $length = strlen($captureContent); 775 } 776 return $this->getFirstMatchedCharacterPosition() + $length; 777 } 778 779 /** 780 * @param $value string the class string to add 781 * @return Call 782 */ 783 public 784 function addClassName(string $value): Call 785 { 786 $class = $this->getAttribute("class"); 787 if ($class != null) { 788 $value = "$class $value"; 789 } 790 $this->addAttribute("class", $value); 791 return $this; 792 793 } 794 795 /** 796 * @param $key 797 * @return mixed|null - the delete value of null if not found 798 */ 799 public 800 function removeAttribute($key) 801 { 802 803 $data = &$this->getPluginData(); 804 if (isset($data[PluginUtility::ATTRIBUTES][$key])) { 805 $value = $data[PluginUtility::ATTRIBUTES][$key]; 806 unset($data[PluginUtility::ATTRIBUTES][$key]); 807 return $value; 808 } else { 809 // boolean attribute as first attribute 810 if ($this->getType() == $key) { 811 unset($data[PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY]); 812 return true; 813 } 814 return null; 815 } 816 817 } 818 819 public 820 function setPayload($text) 821 { 822 if ($this->isPluginCall()) { 823 $this->call[1][1][PluginUtility::PAYLOAD] = $text; 824 } else { 825 LogUtility::msg("Setting the payload for a non-native call ($this) is not yet implemented"); 826 } 827 } 828 829 /** 830 * @return bool true if the call is a text call (same as dom text node) 831 */ 832 public 833 function isTextCall() 834 { 835 return ( 836 $this->getState() == DOKU_LEXER_UNMATCHED || 837 $this->getTagName() == "cdata" || 838 $this->getTagName() == "acronym" 839 ); 840 } 841 842 public 843 function setType($type) 844 { 845 if ($this->isPluginCall()) { 846 $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY] = $type; 847 } else { 848 LogUtility::msg("This is not a plugin call ($this), you can't set the type"); 849 } 850 } 851 852 public 853 function addCssStyle($key, $value) 854 { 855 $style = $this->getAttribute("style"); 856 $cssValue = "$key:$value"; 857 if ($style !== null) { 858 $cssValue = "$style; $cssValue"; 859 } 860 $this->addAttribute("style", $cssValue); 861 } 862 863 public 864 function setSyntaxComponentFromTag($tag) 865 { 866 867 if ($this->isPluginCall()) { 868 $this->call[1][0] = PluginUtility::getComponentName($tag); 869 } else { 870 LogUtility::msg("The call ($this) is a native call and we don't support yet the modification of the component to ($tag)"); 871 } 872 } 873 874 875 public 876 function setCapturedContent($content) 877 { 878 $tagName = $this->getTagName(); 879 switch ($tagName) { 880 case "cdata": 881 $this->call[1][0] = $content; 882 break; 883 default: 884 LogUtility::msg("Setting the captured content on a call for the tag ($tagName) is not yet implemented", LogUtility::LVL_MSG_ERROR); 885 } 886 } 887 888 /** 889 * Set the display to block or inline 890 * One of `block` or `inline` 891 */ 892 public 893 function setDisplay($display): Call 894 { 895 $mode = $this->getMode(); 896 if ($mode == "plugin") { 897 $this->call[1][1][PluginUtility::DISPLAY] = $display; 898 } else { 899 LogUtility::msg("You can't set a display on a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING); 900 } 901 return $this; 902 903 } 904 905 /** 906 * The plugin or not 907 * @return mixed 908 */ 909 private 910 function getMode() 911 { 912 return $this->call[0]; 913 } 914 915 /** 916 * Return if this an unmatched call with space 917 * in captured content 918 * @return bool 919 */ 920 public 921 function isUnMatchedEmptyCall(): bool 922 { 923 if ($this->getState() === DOKU_LEXER_UNMATCHED && trim($this->getCapturedContent()) === "") { 924 return true; 925 } 926 return false; 927 } 928 929 public 930 function getExitCode() 931 { 932 $mode = $this->call[0]; 933 if ($mode == "plugin") { 934 $value = $this->call[1][1][PluginUtility::EXIT_CODE] ?? null; 935 if ($value === null) { 936 return 0; 937 } 938 return $value; 939 } else { 940 LogUtility::msg("You can't ask for the exit code from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support"); 941 return 0; 942 } 943 } 944 945 public 946 function setAttribute(string $name, $value): Call 947 { 948 $this->getPluginData()[PluginUtility::ATTRIBUTES][$name] = $value; 949 return $this; 950 } 951 952 public 953 function setPluginData(string $name, $value): Call 954 { 955 $this->getPluginData()[$name] = $value; 956 return $this; 957 } 958 959 public 960 function getIdOrDefault() 961 { 962 $id = $this->getAttribute(TagAttributes::ID_KEY); 963 if ($id !== null) { 964 return $id; 965 } 966 return $this->getAttribute(TagAttributes::GENERATED_ID_KEY); 967 } 968 969 public 970 function getAttributeAndRemove(string $key) 971 { 972 $value = $this->getAttribute($key); 973 $this->removeAttribute($key); 974 return $value; 975 } 976 977 private function getStateName(): string 978 { 979 $state = $this->getState(); 980 switch ($state) { 981 case DOKU_LEXER_ENTER: 982 return "enter"; 983 case DOKU_LEXER_EXIT: 984 return "exit"; 985 case DOKU_LEXER_SPECIAL: 986 return "empty"; 987 case DOKU_LEXER_UNMATCHED: 988 return "text"; 989 default: 990 return "unknown " . $state; 991 } 992 } 993 994 995} 996