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 * It may be any type. Array, scalar 373 */ 374 public 375 function &getPluginData($attribute = null) 376 { 377 $data = &$this->call[1][1]; 378 if ($attribute === null) { 379 return $data; 380 } 381 return $data[$attribute]; 382 383 } 384 385 /** 386 * @return mixed the matched content from the {@link DokuWiki_Syntax_Plugin::handle} 387 */ 388 public 389 function getCapturedContent() 390 { 391 $caller = $this->call[0]; 392 switch ($caller) { 393 case "plugin": 394 return $this->call[1][3]; 395 case "internallink": 396 return '[[' . $this->call[1][0] . '|' . $this->call[1][1] . ']]'; 397 case "eol": 398 return DOKU_LF; 399 case "header": 400 case "cdata": 401 return $this->call[1][0]; 402 default: 403 if (isset($this->call[1][0]) && is_string($this->call[1][0])) { 404 return $this->call[1][0]; 405 } else { 406 return ""; 407 } 408 } 409 } 410 411 412 /** 413 * Return the attributes of a call 414 */ 415 public 416 function &getAttributes(): array 417 { 418 419 $isPluginCall = $this->isPluginCall(); 420 if (!$isPluginCall) { 421 return $this->call[1]; 422 } 423 424 $data = &$this->getPluginData(); 425 if (!is_array($data)) { 426 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); 427 // We discard, it may be a third party plugin 428 // The log will throw an error if it's on our hand 429 $data = []; 430 return $data; 431 } 432 if (!isset($data[PluginUtility::ATTRIBUTES])) { 433 $data[PluginUtility::ATTRIBUTES] = []; 434 } 435 $attributes = &$data[PluginUtility::ATTRIBUTES]; 436 if (!is_array($attributes)) { 437 $message = "The attributes value are not an array for the call ($this), the value was wrapped in an array"; 438 LogUtility::error($message, self::CANONICAL); 439 $attributes = [$attributes]; 440 } 441 return $attributes; 442 } 443 444 public 445 function removeAttributes() 446 { 447 448 $data = &$this->getPluginData(); 449 if (isset($data[PluginUtility::ATTRIBUTES])) { 450 unset($data[PluginUtility::ATTRIBUTES]); 451 } 452 453 } 454 455 public 456 function updateToPluginComponent($component, $state, $attributes = array()) 457 { 458 if ($this->call[0] == "plugin") { 459 $match = $this->call[1][3]; 460 } else { 461 $this->call[0] = "plugin"; 462 $match = ""; 463 } 464 $this->call[1] = array( 465 0 => $component, 466 1 => array( 467 PluginUtility::ATTRIBUTES => $attributes, 468 PluginUtility::STATE => $state, 469 ), 470 2 => $state, 471 3 => $match 472 ); 473 474 } 475 476 /** 477 * Does the display has been set 478 * to override the dokuwiki default 479 * ({@link Syntax::getPType()} 480 * 481 * because an image is by default a inline component 482 * but can be a block (ie top image of a card) 483 * @return bool 484 */ 485 public 486 function isDisplaySet(): bool 487 { 488 return isset($this->call[1][1][PluginUtility::DISPLAY]); 489 } 490 491 /** 492 * @return string|null 493 * {@link Call::INLINE_DISPLAY} or {@link Call::BlOCK_DISPLAY} 494 */ 495 public 496 function getDisplay(): ?string 497 { 498 $mode = $this->getMode(); 499 if ($mode == "plugin") { 500 if ($this->isDisplaySet()) { 501 return $this->call[1][1][PluginUtility::DISPLAY]; 502 } 503 } 504 505 if ($this->getState() == DOKU_LEXER_UNMATCHED) { 506 /** 507 * Unmatched are content (ie text node in XML/HTML) and have 508 * no display 509 */ 510 return Call::INLINE_DISPLAY; 511 } else { 512 $mode = $this->call[0]; 513 if ($mode == "plugin") { 514 global $DOKU_PLUGINS; 515 $component = $this->getComponentName(); 516 /** 517 * @var SyntaxPlugin $syntaxPlugin 518 */ 519 $syntaxPlugin = $DOKU_PLUGINS['syntax'][$component]; 520 $pType = $syntaxPlugin->getPType(); 521 switch ($pType) { 522 case "normal": 523 return Call::INLINE_DISPLAY; 524 case "block": 525 case "stack": 526 return Call::BlOCK_DISPLAY; 527 default: 528 LogUtility::msg("The ptype (" . $pType . ") is unknown."); 529 return null; 530 } 531 } else { 532 if ($mode == "eol") { 533 /** 534 * Control character 535 * We return it as it's used in the 536 * {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()} 537 * to create the paragraph 538 * This is not a block, nor an inline 539 */ 540 return $mode; 541 } 542 543 if (in_array($mode, self::INLINE_DOKUWIKI_COMPONENTS)) { 544 return Call::INLINE_DISPLAY; 545 } 546 547 if (in_array($mode, self::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) { 548 return Call::BlOCK_DISPLAY; 549 } 550 551 if (in_array($mode, self::TABLE_MARKUP)) { 552 return Call::TABLE_DISPLAY; 553 } 554 555 LogUtility::warning("The display of the call with the mode (" . $mode . ") is unknown"); 556 return null; 557 558 559 } 560 } 561 562 } 563 564 /** 565 * Same as {@link Call::getTagName()} 566 * but fully qualified 567 * @return string 568 */ 569 public 570 function getComponentName() 571 { 572 $mode = $this->call[0]; 573 if ($mode == "plugin") { 574 $pluginDokuData = $this->call[1]; 575 return $pluginDokuData[0]; 576 } else { 577 return $mode; 578 } 579 } 580 581 public 582 function updateEolToSpace() 583 { 584 $mode = $this->call[0]; 585 if ($mode != "eol") { 586 LogUtility::msg("You can't update a " . $mode . " to a space. It should be a eol", LogUtility::LVL_MSG_WARNING, "support"); 587 } else { 588 $this->call[0] = "cdata"; 589 $this->call[1] = array( 590 0 => " " 591 ); 592 } 593 594 } 595 596 public 597 function &addAttribute($key, $value) 598 { 599 $mode = $this->call[0]; 600 if ($mode == "plugin") { 601 $this->call[1][1][PluginUtility::ATTRIBUTES][$key] = $value; 602 // keep the new reference 603 return $this->call[1][1][PluginUtility::ATTRIBUTES][$key]; 604 } else { 605 LogUtility::msg("You can't add an attribute to the non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support"); 606 $whatever = []; 607 return $whatever; 608 } 609 } 610 611 public 612 function getContext() 613 { 614 $mode = $this->call[0]; 615 if ($mode === "plugin") { 616 return $this->call[1][1][PluginUtility::CONTEXT] ?? null; 617 } else { 618 LogUtility::msg("You can't ask for a context from a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support"); 619 return null; 620 } 621 } 622 623 /** 624 * 625 * @return array 626 */ 627 public 628 function toCallArray() 629 { 630 return $this->call; 631 } 632 633 public 634 function __toString() 635 { 636 $name = $this->key; 637 if (!empty($name)) { 638 $name .= " - "; 639 } 640 $name .= $this->getTagName(); 641 $name .= " - {$this->getStateName()}"; 642 return $name; 643 } 644 645 /** 646 * @return string|null 647 * 648 * If the type returned is a boolean attribute, 649 * it means you need to define the expected types 650 * in the function {@link TagAttributes::createFromTagMatch()} 651 * as third attribute 652 */ 653 public 654 function getType(): ?string 655 { 656 if ($this->getState() == DOKU_LEXER_UNMATCHED) { 657 return null; 658 } else { 659 return $this->getAttribute(TagAttributes::TYPE_KEY); 660 } 661 } 662 663 /** 664 * @param $key 665 * @param null $default 666 * @return array|string|null 667 */ 668 public 669 function &getAttribute($key, $default = null) 670 { 671 $attributes = &$this->getAttributes(); 672 if (isset($attributes[$key])) { 673 return $attributes[$key]; 674 } 675 return $default; 676 677 } 678 679 public 680 function getPayload() 681 { 682 $mode = $this->call[0]; 683 if ($mode == "plugin") { 684 return $this->call[1][1][PluginUtility::PAYLOAD]; 685 } else { 686 LogUtility::msg("You can't ask for a payload from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support"); 687 return null; 688 } 689 } 690 691 public 692 function setContext($value) 693 { 694 $this->call[1][1][PluginUtility::CONTEXT] = $value; 695 return $this; 696 } 697 698 public 699 function hasAttribute($attributeName): bool 700 { 701 $attributes = $this->getAttributes(); 702 if (isset($attributes[$attributeName])) { 703 return true; 704 } else { 705 if ($this->getType() == $attributeName) { 706 return true; 707 } else { 708 return false; 709 } 710 } 711 } 712 713 public 714 function isPluginCall() 715 { 716 return $this->call[0] === "plugin"; 717 } 718 719 /** 720 * @return mixed|string the position (ie key) in the array 721 */ 722 public 723 function getKey() 724 { 725 return $this->key; 726 } 727 728 public 729 function &getInstructionCall() 730 { 731 return $this->call; 732 } 733 734 public 735 function setState($state) 736 { 737 if ($this->call[0] == "plugin") { 738 // for dokuwiki 739 $this->call[1][2] = $state; 740 // for the combo plugin if any 741 if (isset($this->call[1][1][PluginUtility::STATE])) { 742 $this->call[1][1][PluginUtility::STATE] = $state; 743 } 744 } else { 745 LogUtility::msg("This modification of state is not yet supported for a native call"); 746 } 747 } 748 749 750 /** 751 * Return the position of the first matched character in the text file 752 * @return mixed 753 */ 754 public 755 function getFirstMatchedCharacterPosition() 756 { 757 758 return $this->call[2]; 759 760 } 761 762 /** 763 * Return the position of the last matched character in the text file 764 * 765 * This is the {@link Call::getFirstMatchedCharacterPosition()} 766 * plus the length of the {@link Call::getCapturedContent()} 767 * matched content 768 * @return int|mixed 769 */ 770 public 771 function getLastMatchedCharacterPosition() 772 { 773 $captureContent = $this->getCapturedContent(); 774 $length = 0; 775 if ($captureContent != null) { 776 $length = strlen($captureContent); 777 } 778 return $this->getFirstMatchedCharacterPosition() + $length; 779 } 780 781 /** 782 * @param $value string the class string to add 783 * @return Call 784 */ 785 public 786 function addClassName(string $value): Call 787 { 788 $class = $this->getAttribute("class"); 789 if ($class != null) { 790 $value = "$class $value"; 791 } 792 $this->addAttribute("class", $value); 793 return $this; 794 795 } 796 797 /** 798 * @param $key 799 * @return mixed|null - the delete value of null if not found 800 */ 801 public 802 function removeAttribute($key) 803 { 804 805 $data = &$this->getPluginData(); 806 if (isset($data[PluginUtility::ATTRIBUTES][$key])) { 807 $value = $data[PluginUtility::ATTRIBUTES][$key]; 808 unset($data[PluginUtility::ATTRIBUTES][$key]); 809 return $value; 810 } else { 811 // boolean attribute as first attribute 812 if ($this->getType() == $key) { 813 unset($data[PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY]); 814 return true; 815 } 816 return null; 817 } 818 819 } 820 821 public 822 function setPayload($text) 823 { 824 if ($this->isPluginCall()) { 825 $this->call[1][1][PluginUtility::PAYLOAD] = $text; 826 } else { 827 LogUtility::msg("Setting the payload for a non-native call ($this) is not yet implemented"); 828 } 829 } 830 831 /** 832 * @return bool true if the call is a text call (same as dom text node) 833 */ 834 public 835 function isTextCall() 836 { 837 return ( 838 $this->getState() == DOKU_LEXER_UNMATCHED || 839 $this->getTagName() == "cdata" || 840 $this->getTagName() == "acronym" 841 ); 842 } 843 844 public 845 function setType($type) 846 { 847 if ($this->isPluginCall()) { 848 $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY] = $type; 849 } else { 850 LogUtility::msg("This is not a plugin call ($this), you can't set the type"); 851 } 852 } 853 854 public 855 function addCssStyle($key, $value) 856 { 857 $style = $this->getAttribute("style"); 858 $cssValue = "$key:$value"; 859 if ($style !== null) { 860 $cssValue = "$style; $cssValue"; 861 } 862 $this->addAttribute("style", $cssValue); 863 } 864 865 public 866 function setSyntaxComponentFromTag($tag) 867 { 868 869 if ($this->isPluginCall()) { 870 $this->call[1][0] = PluginUtility::getComponentName($tag); 871 } else { 872 LogUtility::msg("The call ($this) is a native call and we don't support yet the modification of the component to ($tag)"); 873 } 874 } 875 876 877 public 878 function setCapturedContent($content) 879 { 880 $tagName = $this->getTagName(); 881 switch ($tagName) { 882 case "cdata": 883 $this->call[1][0] = $content; 884 break; 885 default: 886 LogUtility::msg("Setting the captured content on a call for the tag ($tagName) is not yet implemented", LogUtility::LVL_MSG_ERROR); 887 } 888 } 889 890 /** 891 * Set the display to block or inline 892 * One of `block` or `inline` 893 */ 894 public 895 function setDisplay($display): Call 896 { 897 $mode = $this->getMode(); 898 if ($mode == "plugin") { 899 $this->call[1][1][PluginUtility::DISPLAY] = $display; 900 } else { 901 LogUtility::msg("You can't set a display on a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING); 902 } 903 return $this; 904 905 } 906 907 /** 908 * The plugin or not 909 * @return mixed 910 */ 911 private 912 function getMode() 913 { 914 return $this->call[0]; 915 } 916 917 /** 918 * Return if this an unmatched call with space 919 * in captured content 920 * @return bool 921 */ 922 public 923 function isUnMatchedEmptyCall(): bool 924 { 925 if ($this->getState() === DOKU_LEXER_UNMATCHED && trim($this->getCapturedContent()) === "") { 926 return true; 927 } 928 return false; 929 } 930 931 public 932 function getExitCode() 933 { 934 $mode = $this->call[0]; 935 if ($mode == "plugin") { 936 $value = $this->call[1][1][PluginUtility::EXIT_CODE] ?? null; 937 if ($value === null) { 938 return 0; 939 } 940 return $value; 941 } else { 942 LogUtility::msg("You can't ask for the exit code from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support"); 943 return 0; 944 } 945 } 946 947 public 948 function setAttribute(string $name, $value): Call 949 { 950 $this->getAttributes()[$name] = $value; 951 return $this; 952 } 953 954 public 955 function setPluginData(string $name, $value): Call 956 { 957 $this->getPluginData()[$name] = $value; 958 return $this; 959 } 960 961 public 962 function getIdOrDefault() 963 { 964 $id = $this->getAttribute(TagAttributes::ID_KEY); 965 if ($id !== null) { 966 return $id; 967 } 968 return $this->getAttribute(TagAttributes::GENERATED_ID_KEY); 969 } 970 971 public 972 function getAttributeAndRemove(string $key) 973 { 974 $value = $this->getAttribute($key); 975 $this->removeAttribute($key); 976 return $value; 977 } 978 979 private function getStateName(): string 980 { 981 $state = $this->getState(); 982 switch ($state) { 983 case DOKU_LEXER_ENTER: 984 return "enter"; 985 case DOKU_LEXER_EXIT: 986 return "exit"; 987 case DOKU_LEXER_SPECIAL: 988 return "empty"; 989 case DOKU_LEXER_UNMATCHED: 990 return "text"; 991 default: 992 return "unknown " . $state; 993 } 994 } 995 996 997} 998