1<?php 2 3/* 4 * This file is part of the league/commonmark package. 5 * 6 * (c) Colin O'Dell <colinodell@gmail.com> 7 * 8 * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js) 9 * - (c) John MacFarlane 10 * 11 * For the full copyright and license information, please view the LICENSE 12 * file that was distributed with this source code. 13 */ 14 15namespace League\CommonMark\Block\Element; 16 17use League\CommonMark\ContextInterface; 18use League\CommonMark\Cursor; 19use League\CommonMark\Node\Node; 20 21/** 22 * Block-level element 23 * 24 * @method parent() ?AbstractBlock 25 */ 26abstract class AbstractBlock extends Node 27{ 28 /** 29 * Used for storage of arbitrary data. 30 * 31 * @var array<string, mixed> 32 */ 33 public $data = []; 34 35 /** 36 * @var bool 37 */ 38 protected $open = true; 39 40 /** 41 * @var bool 42 */ 43 protected $lastLineBlank = false; 44 45 /** 46 * @var int 47 */ 48 protected $startLine = 0; 49 50 /** 51 * @var int 52 */ 53 protected $endLine = 0; 54 55 protected function setParent(Node $node = null) 56 { 57 if ($node && !$node instanceof self) { 58 throw new \InvalidArgumentException('Parent of block must also be block (can not be inline)'); 59 } 60 61 parent::setParent($node); 62 } 63 64 public function isContainer(): bool 65 { 66 return true; 67 } 68 69 /** 70 * @return bool 71 */ 72 public function hasChildren(): bool 73 { 74 return $this->firstChild !== null; 75 } 76 77 /** 78 * Returns true if this block can contain the given block as a child node 79 * 80 * @param AbstractBlock $block 81 * 82 * @return bool 83 */ 84 abstract public function canContain(AbstractBlock $block): bool; 85 86 /** 87 * Whether this is a code block 88 * 89 * Code blocks are extra-greedy - they'll try to consume all subsequent 90 * lines of content without calling matchesNextLine() each time. 91 * 92 * @return bool 93 */ 94 abstract public function isCode(): bool; 95 96 /** 97 * @param Cursor $cursor 98 * 99 * @return bool 100 */ 101 abstract public function matchesNextLine(Cursor $cursor): bool; 102 103 /** 104 * @param int $startLine 105 * 106 * @return $this 107 */ 108 public function setStartLine(int $startLine) 109 { 110 $this->startLine = $startLine; 111 if (empty($this->endLine)) { 112 $this->endLine = $startLine; 113 } 114 115 return $this; 116 } 117 118 /** 119 * @return int 120 */ 121 public function getStartLine(): int 122 { 123 return $this->startLine; 124 } 125 126 /** 127 * @param int $endLine 128 * 129 * @return $this 130 */ 131 public function setEndLine(int $endLine) 132 { 133 $this->endLine = $endLine; 134 135 return $this; 136 } 137 138 /** 139 * @return int 140 */ 141 public function getEndLine(): int 142 { 143 return $this->endLine; 144 } 145 146 /** 147 * Whether the block ends with a blank line 148 * 149 * @return bool 150 */ 151 public function endsWithBlankLine(): bool 152 { 153 return $this->lastLineBlank; 154 } 155 156 /** 157 * @param bool $blank 158 * 159 * @return void 160 */ 161 public function setLastLineBlank(bool $blank) 162 { 163 $this->lastLineBlank = $blank; 164 } 165 166 /** 167 * Determines whether the last line should be marked as blank 168 * 169 * @param Cursor $cursor 170 * @param int $currentLineNumber 171 * 172 * @return bool 173 */ 174 public function shouldLastLineBeBlank(Cursor $cursor, int $currentLineNumber): bool 175 { 176 return $cursor->isBlank(); 177 } 178 179 /** 180 * Whether the block is open for modifications 181 * 182 * @return bool 183 */ 184 public function isOpen(): bool 185 { 186 return $this->open; 187 } 188 189 /** 190 * Finalize the block; mark it closed for modification 191 * 192 * @param ContextInterface $context 193 * @param int $endLineNumber 194 * 195 * @return void 196 */ 197 public function finalize(ContextInterface $context, int $endLineNumber) 198 { 199 if (!$this->open) { 200 return; 201 } 202 203 $this->open = false; 204 $this->endLine = $endLineNumber; 205 206 // This should almost always be true 207 if ($context->getTip() !== null) { 208 $context->setTip($context->getTip()->parent()); 209 } 210 } 211 212 /** 213 * @param string $key 214 * @param mixed $default 215 * 216 * @return mixed 217 */ 218 public function getData(string $key, $default = null) 219 { 220 return \array_key_exists($key, $this->data) ? $this->data[$key] : $default; 221 } 222} 223