PageRenderTime 69ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/markdown/lib/lines.php

https://gitlab.com/VxMxPx/mysli
PHP | 463 lines | 239 code | 38 blank | 186 comment | 26 complexity | df696b056a459b63746ebaf1f0ace328 MD5 | raw file
  1. <?php
  2. /**
  3. * Helper, manage lines when processing Markdown.
  4. */
  5. namespace markdown;
  6. use sys\arr;
  7. class lines
  8. {
  9. /**
  10. * Inputed lines.
  11. */
  12. protected $in = [];
  13. /**
  14. * Processed lines.
  15. */
  16. protected $lines = [];
  17. /**
  18. * Instance of lines.
  19. * --
  20. * @param array $lines Raw input of Markdown lines.
  21. */
  22. function __construct(array $lines=[])
  23. {
  24. $this->in = $lines;
  25. $this->reset();
  26. }
  27. /**
  28. * Return all processed lines.
  29. * --
  30. * @return array
  31. */
  32. function get_all()
  33. {
  34. return $this->lines;
  35. }
  36. /**
  37. * Get line's tag(s) at particular position.
  38. * --
  39. * @param integer $at
  40. * --
  41. * @return array
  42. * [ array $open_tags, string $line, array $close_tags, array $attributes ]
  43. */
  44. function get_raw($at)
  45. {
  46. if (isset($this->lines[$at]))
  47. {
  48. return $this->lines[$at];
  49. }
  50. else
  51. {
  52. return [ [], null, [], [] ];
  53. }
  54. }
  55. /**
  56. * Reset processed lines (to be the same as when object was constructed).
  57. * If $at provided, reset only particular line.
  58. * --
  59. * @param integer $at
  60. */
  61. function reset($at=null)
  62. {
  63. if ($at !== null)
  64. {
  65. if ($this->has($at))
  66. {
  67. $this->lines[$at] = [
  68. [], $this->in[$at], [], []
  69. ];
  70. }
  71. }
  72. else
  73. {
  74. $this->lines = [];
  75. // Reset live & empty output
  76. foreach ($this->in as $lineno => $line)
  77. {
  78. $this->lines[$lineno] = [
  79. // Open tags
  80. [],
  81. // Current line
  82. $line,
  83. // Close tags
  84. [],
  85. // Attributes
  86. []
  87. ];
  88. }
  89. }
  90. }
  91. /**
  92. * Get number of all lines.
  93. * --
  94. * @return integer
  95. */
  96. function count()
  97. {
  98. return count($this->in);
  99. }
  100. /**
  101. * Set line at particular position.
  102. * This allows you to set line's content and tag(s).
  103. * --
  104. * @param integer $at
  105. * Positon where to set.
  106. *
  107. * @param string $line
  108. * Line's content.
  109. *
  110. * @param mixed $tags
  111. * Either:
  112. * array [string $open, string $close]
  113. * string Both tags
  114. */
  115. function set($at, $line, $tags=null)
  116. {
  117. if (isset($this->lines[$at]))
  118. {
  119. // Line itself
  120. $this->lines[$at][1] = $line;
  121. if ($tags)
  122. {
  123. $this->set_tag($at, $tags);
  124. }
  125. }
  126. else
  127. {
  128. err('set_non_existent_line', "At `{$at}` to `{$line}`");
  129. }
  130. }
  131. /**
  132. * Get line at particular position.
  133. * This will return only line without tags. See $this->get_raw() to get
  134. * full array (with attributes, etc...) for a line.
  135. * --
  136. * @param integer $at
  137. * --
  138. * @return sting
  139. */
  140. function get($at, $trim=false)
  141. {
  142. if (isset($this->lines[$at]))
  143. {
  144. return $this->lines[$at][1];
  145. }
  146. else
  147. {
  148. return null;
  149. }
  150. }
  151. /**
  152. * There's no content on this line.
  153. * --
  154. * @param integer $at
  155. * @param boolean $trim Weather to trim line before checking.
  156. * --
  157. * @return boolean
  158. */
  159. function is_empty($at, $trim=false)
  160. {
  161. $line = $this->get($at);
  162. return !($trim ? trim($line) : $line);
  163. }
  164. /**
  165. * Does line exists at particulat position.
  166. * --
  167. * @param integer $at
  168. * --
  169. * @return boolean
  170. */
  171. function has($at)
  172. {
  173. return isset($this->lines[$at]);
  174. }
  175. /**
  176. * Erase line at particular position.
  177. * --
  178. * @param integer $at
  179. * @param boolean $fully Wipe all tags on the line too.
  180. */
  181. function erase($at, $fully=false)
  182. {
  183. if (isset($this->lines[$at]))
  184. {
  185. if ($fully)
  186. {
  187. $this->lines[$at] = [
  188. [], '', [], []
  189. ];
  190. }
  191. else
  192. {
  193. $this->lines[$at][1] = '';
  194. }
  195. }
  196. }
  197. /**
  198. * Get line's attribute.
  199. * --
  200. * @param string $at
  201. * @param string $attr
  202. * @param mixed $default
  203. * --
  204. * @return mixed
  205. */
  206. function get_attr($at, $attr, $default=null)
  207. {
  208. if (isset($this->lines[$at]) && isset($this->lines[$at][3][$attr]))
  209. {
  210. return $this->lines[$at][3][$attr];
  211. }
  212. else
  213. {
  214. return $default;
  215. }
  216. }
  217. /**
  218. * Set attribute for a line.
  219. * --
  220. * @param integer $at
  221. * @param mixed $attr string one value | array multiple
  222. * @param mixed $value
  223. */
  224. function set_attr($at, $attr, $value=null)
  225. {
  226. if (isset($this->lines[$at]))
  227. {
  228. if (is_array($attr))
  229. {
  230. foreach ($attr as $attr_k => $attr_v)
  231. {
  232. $this->lines[$at][3][$attr_k] = $attr_v;
  233. }
  234. }
  235. else
  236. {
  237. $this->lines[$at][3][$attr] = $value;
  238. }
  239. }
  240. else
  241. {
  242. if (is_array($attr)) $attr = implode(',', array_keys($attr));
  243. err('attr_on_non_existent_line', "Line `{$attr}` at `{$at}`");
  244. }
  245. }
  246. /**
  247. * Set line's tag(s) at particular position.
  248. * --
  249. * @param integer $at
  250. *
  251. * @param mixed $tags
  252. * `array [string $open, string $close]` | `string both`
  253. *
  254. * @param boolean $close_prepend
  255. * Prepend closed tag, rather than append.
  256. * This will make sense in most cases when setting both tags.
  257. */
  258. function set_tag($at, $tags=null, $close_prepend=true)
  259. {
  260. if (isset($this->lines[$at]))
  261. {
  262. // Both the same
  263. if ($tags)
  264. {
  265. if (!is_array($tags))
  266. {
  267. $tags = [$tags, $tags];
  268. }
  269. // Open tag
  270. if (isset($tags[0]) && $tags[0])
  271. {
  272. $this->lines[$at][0][] = $tags[0];
  273. }
  274. // Close tag
  275. if (isset($tags[1]) && $tags[1])
  276. {
  277. if ($close_prepend)
  278. {
  279. array_unshift($this->lines[$at][2], $tags[1]);
  280. }
  281. else
  282. {
  283. $this->lines[$at][2][] = $tags[1];
  284. }
  285. }
  286. }
  287. }
  288. else
  289. {
  290. err('tag_on_non_existent_line', "Line: ".print_r($tags, true)."` at `{$at}`");
  291. }
  292. }
  293. /**
  294. * Move tags from one line to another (this will erase tags in $from and
  295. * move them $to). Tags will be appened to $to.
  296. * --
  297. * @param integer $from
  298. * @param integer $to
  299. * @param array $target [ open, close ]
  300. */
  301. function move_tags($from, $to, array $target)
  302. {
  303. if (!$this->has($from)) err('source_not_found', $from);
  304. if (!$this->has($to)) err('target_not_found', $to);
  305. $this->lines[$to][2] = array_merge(
  306. $this->lines[$to][2],
  307. $this->lines[$from][2]
  308. );
  309. $this->lines[$from][2] = [];
  310. }
  311. /**
  312. * Get one tag at particular line, at particular position.
  313. * --
  314. * @param integer $at
  315. * Tag at which Line.
  316. *
  317. * @param integer $from
  318. * Position, which can be negative, for example:
  319. * -1 to get last tag in the list.
  320. *
  321. * @param string $type
  322. * Either null (open tag) or '/' to query close tag.
  323. * --
  324. * @return string
  325. */
  326. function get_tag($at, $from, $type=null)
  327. {
  328. list($o, $c) = $this->get_tags($at);
  329. $pool = $type ? $c : $o;
  330. $pool = array_slice($pool, $from, 1);
  331. return isset($pool[0]) ? $pool[0] : null;
  332. }
  333. /**
  334. * Get tag(s) for particular line.
  335. * --
  336. * @param integer $at
  337. * --
  338. * @return array [ array open, array close ]
  339. */
  340. function get_tags($at)
  341. {
  342. list($o, $_, $c, $_) = $this->get_raw($at);
  343. return [$o, $c];
  344. }
  345. /**
  346. * See if open (or close) tag exists at particular line.
  347. * --
  348. * @param integer $at
  349. *
  350. * @param string $tag
  351. * `null` opened tags
  352. * `tag` particular opened tag,
  353. * `/` closed tags
  354. * `/tag` particular close tag
  355. * --
  356. * @return boolean
  357. */
  358. function has_tag($at, $tag=null)
  359. {
  360. list($open, $_, $close) = $this->get_raw($at);
  361. $pool = substr($tag, 0, 1) === '/' ? $close : $open;
  362. $tag = trim($tag, '/');
  363. return $tag ? (array_search($tag, $pool) !== false) : !!count($pool);
  364. }
  365. /**
  366. * Erase particular tag(s).
  367. * --
  368. * @param integer $at
  369. * @param string $tag Tag name, use /tag to target close tags.
  370. * @param integer $count How many to erase.
  371. */
  372. function erase_tag($at, $tag, $count)
  373. {
  374. $is_close = substr($tag, 0, 1) === '/';
  375. $pool = !$is_close ? 0 : 2;
  376. $pool = &$this->lines[$at][$pool];
  377. $tag = trim($tag, '/');
  378. for ($i=count($pool)-1; $i >= 0; $i--)
  379. {
  380. if ($pool[$i] === $tag)
  381. {
  382. unset($pool[$i]);
  383. $count--;
  384. }
  385. if ($count === 0)
  386. {
  387. break;
  388. }
  389. }
  390. $pool = array_values($pool);
  391. }
  392. /**
  393. * Get number of open and closed tags at position.
  394. * --
  395. * @param integer $at
  396. *
  397. * @param string $tag
  398. * If you don't provide a tag,
  399. * it will return sum count of all tags at this line.
  400. * --
  401. * @return array `[integer $open, integer $close]`
  402. */
  403. function count_tag($at, $tag=null)
  404. {
  405. list($open, $_, $close) = $this->get_raw($at);
  406. return [
  407. $tag ? arr::count_values_of($open, $tag) : count($open),
  408. $tag ? arr::count_values_of($close, $tag) : count($close)
  409. ];
  410. }
  411. /**
  412. * Output lines for debuging, flat array format.
  413. * --
  414. * @return array
  415. */
  416. function debug_flat_array()
  417. {
  418. $array = [];
  419. foreach ($this->get_all() as $line)
  420. {
  421. $array[] = implode(',', $line[0]) . ' ' .$line[1] . ' ' . implode(',', $line[2]);
  422. }
  423. return $array;
  424. }
  425. }