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 if ($syntaxPlugin === null) { 521 // not a syntax plugin (ie frontmatter) 522 return null; 523 } 524 $pType = $syntaxPlugin->getPType(); 525 switch ($pType) { 526 case "normal": 527 return Call::INLINE_DISPLAY; 528 case "block": 529 case "stack": 530 return Call::BlOCK_DISPLAY; 531 default: 532 LogUtility::msg("The ptype (" . $pType . ") is unknown."); 533 return null; 534 } 535 } else { 536 if ($mode == "eol") { 537 /** 538 * Control character 539 * We return it as it's used in the 540 * {@link \syntax_plugin_combo_para::fromEolToParagraphUntilEndOfStack()} 541 * to create the paragraph 542 * This is not a block, nor an inline 543 */ 544 return $mode; 545 } 546 547 if (in_array($mode, self::INLINE_DOKUWIKI_COMPONENTS)) { 548 return Call::INLINE_DISPLAY; 549 } 550 551 if (in_array($mode, self::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) { 552 return Call::BlOCK_DISPLAY; 553 } 554 555 if (in_array($mode, self::TABLE_MARKUP)) { 556 return Call::TABLE_DISPLAY; 557 } 558 559 LogUtility::warning("The display of the call with the mode (" . $mode . ") is unknown"); 560 return null; 561 562 563 } 564 } 565 566 } 567 568 /** 569 * Same as {@link Call::getTagName()} 570 * but fully qualified 571 * @return string 572 */ 573 public 574 function getComponentName() 575 { 576 $mode = $this->call[0]; 577 if ($mode == "plugin") { 578 $pluginDokuData = $this->call[1]; 579 return $pluginDokuData[0]; 580 } else { 581 return $mode; 582 } 583 } 584 585 public 586 function updateEolToSpace() 587 { 588 $mode = $this->call[0]; 589 if ($mode != "eol") { 590 LogUtility::msg("You can't update a " . $mode . " to a space. It should be a eol", LogUtility::LVL_MSG_WARNING, "support"); 591 } else { 592 $this->call[0] = "cdata"; 593 $this->call[1] = array( 594 0 => " " 595 ); 596 } 597 598 } 599 600 public 601 function &addAttribute($key, $value) 602 { 603 $mode = $this->call[0]; 604 if ($mode == "plugin") { 605 $this->call[1][1][PluginUtility::ATTRIBUTES][$key] = $value; 606 // keep the new reference 607 return $this->call[1][1][PluginUtility::ATTRIBUTES][$key]; 608 } else { 609 LogUtility::msg("You can't add an attribute to the non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support"); 610 $whatever = []; 611 return $whatever; 612 } 613 } 614 615 public 616 function getContext() 617 { 618 $mode = $this->call[0]; 619 if ($mode === "plugin") { 620 return $this->call[1][1][PluginUtility::CONTEXT] ?? null; 621 } else { 622 LogUtility::msg("You can't ask for a context from a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING, "support"); 623 return null; 624 } 625 } 626 627 /** 628 * 629 * @return array 630 */ 631 public 632 function toCallArray() 633 { 634 return $this->call; 635 } 636 637 public 638 function __toString() 639 { 640 $name = $this->key; 641 if (!empty($name)) { 642 $name .= " - "; 643 } 644 $name .= $this->getTagName(); 645 $name .= " - {$this->getStateName()}"; 646 return $name; 647 } 648 649 /** 650 * @return string|null 651 * 652 * If the type returned is a boolean attribute, 653 * it means you need to define the expected types 654 * in the function {@link TagAttributes::createFromTagMatch()} 655 * as third attribute 656 */ 657 public 658 function getType(): ?string 659 { 660 if ($this->getState() == DOKU_LEXER_UNMATCHED) { 661 return null; 662 } else { 663 return $this->getAttribute(TagAttributes::TYPE_KEY); 664 } 665 } 666 667 /** 668 * @param $key 669 * @param null $default 670 * @return array|string|null 671 */ 672 public 673 function &getAttribute($key, $default = null) 674 { 675 $attributes = &$this->getAttributes(); 676 if (isset($attributes[$key])) { 677 return $attributes[$key]; 678 } 679 return $default; 680 681 } 682 683 public 684 function getPayload() 685 { 686 $mode = $this->call[0]; 687 if ($mode == "plugin") { 688 return $this->call[1][1][PluginUtility::PAYLOAD]; 689 } else { 690 LogUtility::msg("You can't ask for a payload from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support"); 691 return null; 692 } 693 } 694 695 public 696 function setContext($value) 697 { 698 $this->call[1][1][PluginUtility::CONTEXT] = $value; 699 return $this; 700 } 701 702 public 703 function hasAttribute($attributeName): bool 704 { 705 $attributes = $this->getAttributes(); 706 if (isset($attributes[$attributeName])) { 707 return true; 708 } else { 709 if ($this->getType() == $attributeName) { 710 return true; 711 } else { 712 return false; 713 } 714 } 715 } 716 717 public 718 function isPluginCall() 719 { 720 return $this->call[0] === "plugin"; 721 } 722 723 /** 724 * @return mixed|string the position (ie key) in the array 725 */ 726 public 727 function getKey() 728 { 729 return $this->key; 730 } 731 732 public 733 function &getInstructionCall() 734 { 735 return $this->call; 736 } 737 738 public 739 function setState($state) 740 { 741 if ($this->call[0] == "plugin") { 742 // for dokuwiki 743 $this->call[1][2] = $state; 744 // for the combo plugin if any 745 if (isset($this->call[1][1][PluginUtility::STATE])) { 746 $this->call[1][1][PluginUtility::STATE] = $state; 747 } 748 } else { 749 LogUtility::msg("This modification of state is not yet supported for a native call"); 750 } 751 } 752 753 754 /** 755 * Return the position of the first matched character in the text file 756 * @return mixed 757 */ 758 public 759 function getFirstMatchedCharacterPosition() 760 { 761 762 return $this->call[2]; 763 764 } 765 766 /** 767 * Return the position of the last matched character in the text file 768 * 769 * This is the {@link Call::getFirstMatchedCharacterPosition()} 770 * plus the length of the {@link Call::getCapturedContent()} 771 * matched content 772 * @return int|mixed 773 */ 774 public 775 function getLastMatchedCharacterPosition() 776 { 777 $captureContent = $this->getCapturedContent(); 778 $length = 0; 779 if ($captureContent != null) { 780 $length = strlen($captureContent); 781 } 782 return $this->getFirstMatchedCharacterPosition() + $length; 783 } 784 785 /** 786 * @param $value string the class string to add 787 * @return Call 788 */ 789 public 790 function addClassName(string $value): Call 791 { 792 $class = $this->getAttribute("class"); 793 if ($class != null) { 794 $value = "$class $value"; 795 } 796 $this->addAttribute("class", $value); 797 return $this; 798 799 } 800 801 /** 802 * @param $key 803 * @return mixed|null - the delete value of null if not found 804 */ 805 public 806 function removeAttribute($key) 807 { 808 809 $data = &$this->getPluginData(); 810 if (isset($data[PluginUtility::ATTRIBUTES][$key])) { 811 $value = $data[PluginUtility::ATTRIBUTES][$key]; 812 unset($data[PluginUtility::ATTRIBUTES][$key]); 813 return $value; 814 } else { 815 // boolean attribute as first attribute 816 if ($this->getType() == $key) { 817 unset($data[PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY]); 818 return true; 819 } 820 return null; 821 } 822 823 } 824 825 public 826 function setPayload($text) 827 { 828 if ($this->isPluginCall()) { 829 $this->call[1][1][PluginUtility::PAYLOAD] = $text; 830 } else { 831 LogUtility::msg("Setting the payload for a non-native call ($this) is not yet implemented"); 832 } 833 } 834 835 /** 836 * @return bool true if the call is a text call (same as dom text node) 837 */ 838 public 839 function isTextCall() 840 { 841 return ( 842 $this->getState() == DOKU_LEXER_UNMATCHED || 843 $this->getTagName() == "cdata" || 844 $this->getTagName() == "acronym" 845 ); 846 } 847 848 public 849 function setType($type) 850 { 851 if ($this->isPluginCall()) { 852 $this->call[1][1][PluginUtility::ATTRIBUTES][TagAttributes::TYPE_KEY] = $type; 853 } else { 854 LogUtility::msg("This is not a plugin call ($this), you can't set the type"); 855 } 856 } 857 858 public 859 function addCssStyle($key, $value) 860 { 861 $style = $this->getAttribute("style"); 862 $cssValue = "$key:$value"; 863 if ($style !== null) { 864 $cssValue = "$style; $cssValue"; 865 } 866 $this->addAttribute("style", $cssValue); 867 } 868 869 public 870 function setSyntaxComponentFromTag($tag) 871 { 872 873 if ($this->isPluginCall()) { 874 $this->call[1][0] = PluginUtility::getComponentName($tag); 875 } else { 876 LogUtility::msg("The call ($this) is a native call and we don't support yet the modification of the component to ($tag)"); 877 } 878 } 879 880 881 public 882 function setCapturedContent($content) 883 { 884 $tagName = $this->getTagName(); 885 switch ($tagName) { 886 case "cdata": 887 $this->call[1][0] = $content; 888 break; 889 default: 890 LogUtility::msg("Setting the captured content on a call for the tag ($tagName) is not yet implemented", LogUtility::LVL_MSG_ERROR); 891 } 892 } 893 894 /** 895 * Set the display to block or inline 896 * One of `block` or `inline` 897 */ 898 public 899 function setDisplay($display): Call 900 { 901 $mode = $this->getMode(); 902 if ($mode == "plugin") { 903 $this->call[1][1][PluginUtility::DISPLAY] = $display; 904 } else { 905 LogUtility::msg("You can't set a display on a non plugin call mode (" . $mode . ")", LogUtility::LVL_MSG_WARNING); 906 } 907 return $this; 908 909 } 910 911 /** 912 * The plugin or not 913 * @return mixed 914 */ 915 private 916 function getMode() 917 { 918 return $this->call[0]; 919 } 920 921 /** 922 * Return if this an unmatched call with space 923 * in captured content 924 * @return bool 925 */ 926 public 927 function isUnMatchedEmptyCall(): bool 928 { 929 if ($this->getState() === DOKU_LEXER_UNMATCHED && trim($this->getCapturedContent()) === "") { 930 return true; 931 } 932 return false; 933 } 934 935 public 936 function getExitCode() 937 { 938 $mode = $this->call[0]; 939 if ($mode == "plugin") { 940 $value = $this->call[1][1][PluginUtility::EXIT_CODE] ?? null; 941 if ($value === null) { 942 return 0; 943 } 944 return $value; 945 } else { 946 LogUtility::msg("You can't ask for the exit code from a non plugin call mode (" . $mode . ").", LogUtility::LVL_MSG_WARNING, "support"); 947 return 0; 948 } 949 } 950 951 public 952 function setAttribute(string $name, $value): Call 953 { 954 $this->getAttributes()[$name] = $value; 955 return $this; 956 } 957 958 public 959 function setPluginData(string $name, $value): Call 960 { 961 $this->getPluginData()[$name] = $value; 962 return $this; 963 } 964 965 public 966 function getIdOrDefault() 967 { 968 $id = $this->getAttribute(TagAttributes::ID_KEY); 969 if ($id !== null) { 970 return $id; 971 } 972 return $this->getAttribute(TagAttributes::GENERATED_ID_KEY); 973 } 974 975 public 976 function getAttributeAndRemove(string $key) 977 { 978 $value = $this->getAttribute($key); 979 $this->removeAttribute($key); 980 return $value; 981 } 982 983 private function getStateName(): string 984 { 985 $state = $this->getState(); 986 switch ($state) { 987 case DOKU_LEXER_ENTER: 988 return "enter"; 989 case DOKU_LEXER_EXIT: 990 return "exit"; 991 case DOKU_LEXER_SPECIAL: 992 return "empty"; 993 case DOKU_LEXER_UNMATCHED: 994 return "text"; 995 default: 996 return "unknown " . $state; 997 } 998 } 999 1000 1001} 1002