PageRenderTime 28ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/regularlabs/helpers/tags.php

https://gitlab.com/che234/adn
PHP | 651 lines | 501 code | 104 blank | 46 comment | 58 complexity | b87ca87aaa54239e3f5cb6eb99e52106 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Regular Labs Library
  4. * @version 16.5.10919
  5. *
  6. * @author Peter van Westen <info@regularlabs.com>
  7. * @link http://www.regularlabs.com
  8. * @copyright Copyright © 2016 Regular Labs All Rights Reserved
  9. * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
  10. */
  11. defined('_JEXEC') or die;
  12. class RLTags
  13. {
  14. static $protected_characters = array(
  15. '=' => '[[:EQUAL:]]',
  16. '"' => '[[:QUOTE:]]',
  17. ',' => '[[:COMMA:]]',
  18. '|' => '[[:BAR:]]',
  19. ':' => '[[:COLON:]]',
  20. );
  21. public static function getValuesFromString($string = '', $main_key = 'title', $known_boolean_keys = array())
  22. {
  23. // Only one value, so return simple key/value object
  24. if (strpos($string, '="') == false && strpos($string, '|') == false)
  25. {
  26. return (object) array($main_key => $string);
  27. }
  28. self::protectSpecialChars($string);
  29. // No foo="bar" syntax found, so assume old syntax
  30. if (strpos($string, '="') == false)
  31. {
  32. self::unprotectSpecialChars($string);
  33. $values = self::getTagValues($string, array($main_key));
  34. self::convertOldSyntax($values, $known_boolean_keys);
  35. return $values;
  36. }
  37. // Cannot find right syntax, so return simple key/value object
  38. if (!preg_match_all('#([a-z0-9-_]+)\s*=\s*"(.*?)"#si', $string, $values))
  39. {
  40. return (object) array($main_key => $string);
  41. }
  42. $tag = new stdClass;
  43. foreach ($values['1'] as $i => $key)
  44. {
  45. $value = $values['2'][$i];
  46. self::unprotectSpecialChars($value);
  47. // Convert numeric values to ints/floats
  48. if (is_numeric($value))
  49. {
  50. $value = $value + 0;
  51. }
  52. // Convert boolean values to actual booleans
  53. $value = ($value === 'true' ? true : $value);
  54. $value = ($value === 'false' ? false : $value);
  55. $tag->{$key} = $value;
  56. }
  57. return $tag;
  58. }
  59. public static function protectSpecialChars(&$string)
  60. {
  61. $escaped_chars = array_keys(self::$protected_characters);
  62. array_walk($escaped_chars, function (&$char)
  63. {
  64. $char = '\\' . $char;
  65. });
  66. // replace escaped characters with special markup
  67. $string = str_replace(
  68. $escaped_chars,
  69. array_values(self::$protected_characters),
  70. $string
  71. );
  72. if (!preg_match_all('#(<.*?>|{.*?}|\[.*?\])#s', $string, $tags))
  73. {
  74. return;
  75. }
  76. foreach ($tags['0'] as $tag)
  77. {
  78. // replace unescaped characters with special markup
  79. $protected = str_replace(
  80. array('=', '"'),
  81. array(self::$protected_characters['='], self::$protected_characters['"']),
  82. $tag
  83. );
  84. $string = str_replace($tag, $protected, $string);
  85. }
  86. }
  87. public static function unprotectSpecialChars(&$string)
  88. {
  89. // replace special markup with unescaped characters
  90. $string = str_replace(
  91. array_values(self::$protected_characters),
  92. array_keys(self::$protected_characters),
  93. $string
  94. );
  95. }
  96. /* @Deprecated */
  97. public static function getTagValues($string = '', $keys = array('title'), $separator = '|', $equal = '=', $limit = 0)
  98. {
  99. $temp_separator = '[[SEPARATOR]]';
  100. $temp_equal = '[[EQUAL]]';
  101. $tag_start = '[[TAG]]';
  102. $tag_end = '[[/TAG]]';
  103. // replace separators and equal signs with special markup
  104. $string = str_replace(array($separator, $equal), array($temp_separator, $temp_equal), $string);
  105. // replace protected separators and equal signs back to original
  106. $string = str_replace(array('\\' . $temp_separator, '\\' . $temp_equal), array($separator, $equal), $string);
  107. // protect all html tags
  108. preg_match_all('#</?[a-z][^>]*>#si', $string, $tags, PREG_SET_ORDER);
  109. if (!empty($tags))
  110. {
  111. foreach ($tags as $tag)
  112. {
  113. $string = str_replace(
  114. $tag['0'],
  115. $tag_start . base64_encode(str_replace(array($temp_separator, $temp_equal), array($separator, $equal), $tag['0'])) . $tag_end,
  116. $string
  117. );
  118. }
  119. }
  120. // split string into array
  121. $vals = $limit
  122. ? explode($temp_separator, $string, (int) $limit)
  123. : explode($temp_separator, $string);
  124. // initialize return vars
  125. $tag_values = new stdClass;
  126. $tag_values->params = array();
  127. // loop through splits
  128. foreach ($vals as $i => $keyval)
  129. {
  130. // spit part into key and val by equal sign
  131. $keyval = explode($temp_equal, $keyval, 2);
  132. if (isset($keyval['1']))
  133. {
  134. $keyval['1'] = str_replace(array($temp_separator, $temp_equal), array($separator, $equal), $keyval['1']);
  135. }
  136. // unprotect tags in key and val
  137. foreach ($keyval as $key => $val)
  138. {
  139. preg_match_all('#' . preg_quote($tag_start, '#') . '(.*?)' . preg_quote($tag_end, '#') . '#si', $val, $tags, PREG_SET_ORDER);
  140. if (!empty($tags))
  141. {
  142. foreach ($tags as $tag)
  143. {
  144. $val = str_replace($tag['0'], base64_decode($tag['1']), $val);
  145. }
  146. $keyval[trim($key)] = $val;
  147. }
  148. }
  149. if (isset($keys[$i]))
  150. {
  151. $key = trim($keys[$i]);
  152. // if value is in the keys array add as defined in keys array
  153. // ignore equal sign
  154. $val = implode($equal, $keyval);
  155. if (substr($val, 0, strlen($key) + 1) == $key . '=')
  156. {
  157. $val = substr($val, strlen($key) + 1);
  158. }
  159. $tag_values->{$key} = $val;
  160. unset($keys[$i]);
  161. }
  162. else
  163. {
  164. // else add as defined in the string
  165. if (isset($keyval['1']))
  166. {
  167. $tag_values->{$keyval['0']} = $keyval['1'];
  168. }
  169. else
  170. {
  171. $tag_values->params[] = implode($equal, $keyval);
  172. }
  173. }
  174. }
  175. return $tag_values;
  176. }
  177. public static function replaceKeyAliases(&$values, $key_aliases = array())
  178. {
  179. foreach ($key_aliases as $key => $aliases)
  180. {
  181. if (isset($values->{$key}))
  182. {
  183. continue;
  184. }
  185. foreach ($aliases as $alias)
  186. {
  187. if (!isset($values->{$alias}))
  188. {
  189. continue;
  190. }
  191. $values->{$key} = $values->{$alias};
  192. unset($values->{$alias});
  193. }
  194. }
  195. }
  196. public static function convertOldSyntax(&$values, $known_boolean_keys = array(), $extra_key = 'class')
  197. {
  198. $extra = isset($values->class) ? array($values->class) : array();
  199. foreach ($values->params as $i => $param)
  200. {
  201. if (!$param)
  202. {
  203. continue;
  204. }
  205. if (in_array($param, $known_boolean_keys))
  206. {
  207. $values->{$param} = true;
  208. continue;
  209. }
  210. if (strpos($param, '=') == false)
  211. {
  212. $extra[] = $param;
  213. continue;
  214. }
  215. list($key, $val) = explode('=', $param, 2);
  216. $values->{$key} = $val;
  217. }
  218. $values->{$extra_key} = trim(implode(' ', $extra));
  219. unset($values->params);
  220. }
  221. public static function getRegexSpaces($modifier = '+')
  222. {
  223. return '(?:\s|&nbsp;|&\#160;)' . $modifier;
  224. }
  225. public static function getRegexInsideTag()
  226. {
  227. return '(?:[^\{\}]*\{[^\}]*\})*.*?';
  228. }
  229. public static function getRegexSurroundingTagPre($elements = array('p', 'span'))
  230. {
  231. return '(?:<(?:' . implode('|', $elements) . ')(?: [^>]*)?>\s*(?:<br ?/?>\s*)*){0,3}';
  232. }
  233. public static function getRegexSurroundingTagPost($elements = array('p', 'span'))
  234. {
  235. return '(?:(?:\s*<br ?/?>)*\s*<\/(?:' . implode('|', $elements) . ')>){0,3}';
  236. }
  237. public static function getRegexTags($tags, $include_no_attributes = true, $include_ending = true, $required_attributes = array())
  238. {
  239. require_once __DIR__ . '/text.php';
  240. $tags = RLText::toArray($tags);
  241. $tags = count($tags) > 1 ? '(?:' . implode('|', $tags) . ')' : $tags['0'];
  242. $value = '(?:\s*=\s*(?:"[^"]*"|\'[^\']*\'|[a-z0-9-_]+))?';
  243. $attributes = '(?:\s+[a-z0-9-_]+' . $value . ')+';
  244. $required_attributes = RLText::toArray($required_attributes);
  245. if (!empty($required_attributes))
  246. {
  247. $attributes = '(?:' . $attributes . ')?' . '(?:\s+' . implode('|', $required_attributes) . ')' . $value . '(?:' . $attributes . ')?';
  248. }
  249. if ($include_no_attributes)
  250. {
  251. $attributes = '\s*(?:' . $attributes . ')?';
  252. }
  253. if (!$include_ending)
  254. {
  255. return '<' . $tags . $attributes . '\s*/?>';
  256. }
  257. return '<(?:\/' . $tags . '|' . $tags . $attributes . '\s*/?)\s*/?>';
  258. }
  259. /*
  260. * parses string through Tidy/DOMDocument to fix missing html closing tags and such
  261. */
  262. public static function fixBrokenHtmlTags($string)
  263. {
  264. require_once JPATH_LIBRARIES . '/regularlabs/helpers/htmlfix.php';
  265. return RLHtmlFix::_($string);
  266. }
  267. /*
  268. * remove empty tags
  269. */
  270. public static function removeEmptyHtmlTagPairs($string, $elements = array('p', 'span'))
  271. {
  272. $breaks = '(?:<br ?/?>\s*)*';
  273. while (preg_match('#<(' . implode('|', $elements) . ')(?: [^>]*)?>\s*(' . $breaks . ')<\/\1>\s*#s', $string, $match))
  274. {
  275. $string = str_replace($match['0'], $match['2'], $string);
  276. }
  277. return $string;
  278. }
  279. // @Deprecated: use fixBrokenHtmlTags
  280. public static function cleanSurroundingTags($tags, $elements = array('p', 'span'))
  281. {
  282. require_once __DIR__ . '/text.php';
  283. $breaks = '(?:(?:<br ?/?>|:\|:)\s*)*';
  284. $keys = array_keys($tags);
  285. $string = implode(':|:', $tags);
  286. // Remove empty tags
  287. while (preg_match('#<(' . implode('|', $elements) . ')(?: [^>]*)?>\s*(' . $breaks . ')<\/\1>\s*#s', $string, $match))
  288. {
  289. $string = str_replace($match['0'], $match['2'], $string);
  290. }
  291. // Remove paragraphs around block elements
  292. $block_elements = array(
  293. 'p', 'div',
  294. 'table', 'tr', 'td', 'thead', 'tfoot',
  295. 'h[1-6]',
  296. );
  297. $block_elements = '(' . implode('|', $block_elements) . ')';
  298. while (preg_match('#(<p(?: [^>]*)?>)(\s*' . $breaks . ')(<' . $block_elements . '(?: [^>]*)?>)#s', $string, $match))
  299. {
  300. if ($match['4'] == 'p')
  301. {
  302. $match['3'] = $match['1'] . $match['3'];
  303. RLText::combinePTags($match['3']);
  304. }
  305. $string = str_replace($match['0'], $match['2'] . $match['3'], $string);
  306. }
  307. while (preg_match('#(</' . $block_elements . '>\s*' . $breaks . ')</p>#s', $string, $match))
  308. {
  309. $string = str_replace($match['0'], $match['1'], $string);
  310. }
  311. $tags = explode(':|:', $string);
  312. $new_tags = array();
  313. foreach ($tags as $key => $val)
  314. {
  315. $key = isset($keys[$key]) ? $keys[$key] : $key;
  316. $new_tags[$key] = $val;
  317. }
  318. return $new_tags;
  319. }
  320. // @Deprecated: use fixBrokenHtmlTags ???
  321. public static function fixSurroundingTags($tags)
  322. {
  323. $keys = array_keys($tags);
  324. $breaks = '(?:(?:<br ?/?>|:\|:)\s*)*';
  325. $string = implode(':|:', $tags);
  326. // Remove inline elements around block elements
  327. $string = preg_replace('#'
  328. . '<(?:' . implode('|', self::getInlineElements()) . ')(?: [^>]*)?>'
  329. . '(' . $breaks . '<(?:' . implode('|', self::getBlockElements()) . ')(?: [^>]*)?>)'
  330. . '#',
  331. '\1', $string);
  332. $string = preg_replace('#'
  333. . '(</(?:' . implode('|', self::getBlockElements()) . ')>' . $breaks . ')'
  334. . '</(?:' . implode('|', self::getInlineElements()) . ')>'
  335. . '#',
  336. '\1', $string);
  337. // Remove inner <p> tags if outer start/end <p> tags are found
  338. $string = preg_replace('#'
  339. . '(<(?:' . implode('|', self::getBlockElementsNoDiv()) . ')(?: [^>]*)?>' . $breaks . ')'
  340. . '<p(?: [^>]*)?>(.*)</p>'
  341. . '(' . $breaks . ')'
  342. . '#',
  343. '\1\2\3', $string);
  344. $string = preg_replace('#'
  345. . '(' . $breaks . ')'
  346. . '<p(?: [^>]*)?>(.*)</p>'
  347. . '(' . $breaks . '</(?:' . implode('|', self::getBlockElementsNoDiv()) . ')>)'
  348. . '#',
  349. '\1\2\3', $string);
  350. // Remove outer <p> tags around block elements
  351. $string = preg_replace('#'
  352. . '^\s*<p(?: [^>]*)?>'
  353. . '(' . $breaks . '</?(?:' . implode('|', self::getBlockElements()) . ')(?: [^>]*)?>)'
  354. . '#',
  355. '\1', $string);
  356. $string = preg_replace('#'
  357. . '(</?(?:' . implode('|', self::getBlockElements()) . ')>' . $breaks . ')'
  358. . '</p>\s*$'
  359. . '#',
  360. '\1', $string);
  361. $tags = explode(':|:', $string);
  362. $new_tags = array();
  363. foreach ($tags as $key => $val)
  364. {
  365. $key = isset($keys[$key]) ? $keys[$key] : $key;
  366. $new_tags[$key] = $val;
  367. }
  368. return $new_tags;
  369. }
  370. private static function getBlockElements($exclude = array())
  371. {
  372. if (!is_array($exclude))
  373. {
  374. $exclude = array($exclude);
  375. }
  376. $elements = array(
  377. 'div', 'p', 'pre',
  378. 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
  379. );
  380. return array_diff($elements, $exclude);
  381. }
  382. private static function getBlockElementsNoDiv($exclude = array())
  383. {
  384. return array_diff(self::getBlockElements($exclude), array('div'));
  385. }
  386. private static function getInlineElements()
  387. {
  388. return array(
  389. 'span', 'code', 'a',
  390. 'strong', 'b', 'em', 'i', 'u', 'big', 'small', 'font',
  391. );
  392. }
  393. /*
  394. * tags that have a matching ending tag
  395. */
  396. private static function getPairedElements()
  397. {
  398. return array(
  399. 'div', 'p', 'span', 'pre', 'a',
  400. 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
  401. 'strong', 'b', 'em', 'i', 'u', 'big', 'small', 'font',
  402. // html 5 stuff
  403. 'header', 'nav', 'section', 'article', 'aside', 'footer',
  404. 'figure', 'figcaption', 'details', 'summary', 'mark', 'time',
  405. );
  406. }
  407. public static function setSurroundingTags($pre, $post, $tags = 0)
  408. {
  409. if ($tags == 0)
  410. {
  411. // tags that have a matching ending tag
  412. $tags = self::getPairedElements();
  413. }
  414. $a = explode('<', $pre);
  415. $b = explode('</', $post);
  416. if (count($b) > 1 && count($a) > 1)
  417. {
  418. $a = array_reverse($a);
  419. $a_pre = array_pop($a);
  420. $b_pre = array_shift($b);
  421. $a_tags = $a;
  422. foreach ($a_tags as $i => $a_tag)
  423. {
  424. $a[$i] = '<' . trim($a_tag);
  425. $a_tags[$i] = preg_replace('#^([a-z0-9]+).*$#', '\1', trim($a_tag));
  426. }
  427. $b_tags = $b;
  428. foreach ($b_tags as $i => $b_tag)
  429. {
  430. $b[$i] = '</' . trim($b_tag);
  431. $b_tags[$i] = preg_replace('#^([a-z0-9]+).*$#', '\1', trim($b_tag));
  432. }
  433. foreach ($b_tags as $i => $b_tag)
  434. {
  435. if ($b_tag && in_array($b_tag, $tags))
  436. {
  437. foreach ($a_tags as $j => $a_tag)
  438. {
  439. if ($b_tag == $a_tag)
  440. {
  441. $a_tags[$i] = '';
  442. $b[$i] = trim(preg_replace('#^</' . $b_tag . '.*?>#', '', $b[$i]));
  443. $a[$j] = trim(preg_replace('#^<' . $a_tag . '.*?>#', '', $a[$j]));
  444. break;
  445. }
  446. }
  447. }
  448. }
  449. foreach ($a_tags as $i => $tag)
  450. {
  451. if ($tag && in_array($tag, $tags))
  452. {
  453. array_unshift($b, trim($a[$i]));
  454. $a[$i] = '';
  455. }
  456. }
  457. $a = array_reverse($a);
  458. list($pre, $post) = array(implode('', $a), implode('', $b));
  459. }
  460. return array(trim($pre), trim($post));
  461. }
  462. public static function getDivTags($start_tag = '', $end_tag = '', $tag_start = '{', $tag_end = '}')
  463. {
  464. $start_div = array('pre' => '', 'tag' => '', 'post' => '');
  465. $end_div = array('pre' => '', 'tag' => '', 'post' => '');
  466. if (!empty($start_tag)
  467. && preg_match(
  468. '#^(?P<pre>.*?)(?P<tag>' . $tag_start . 'div(?: .*?)?' . $tag_end . ')(?P<post>.*)$#s',
  469. $start_tag,
  470. $match
  471. )
  472. )
  473. {
  474. $start_div = $match;
  475. }
  476. if (!empty($end_tag)
  477. && preg_match(
  478. '#^(?P<pre>.*?)(?P<tag>' . $tag_start . '/div' . $tag_end . ')(?P<post>.*)$#s',
  479. $end_tag,
  480. $match
  481. )
  482. )
  483. {
  484. $end_div = $match;
  485. }
  486. if (empty($start_div['tag']) || empty($end_div['tag']))
  487. {
  488. return array($start_div, $end_div);
  489. }
  490. $extra = trim(preg_replace('#' . $tag_start . 'div(.*)' . $tag_end . '#si', '\1', $start_div['tag']));
  491. $start_div['tag'] = '<div>';
  492. $end_div['tag'] = '</div>';
  493. if (empty($extra))
  494. {
  495. return array($start_div, $end_div);
  496. }
  497. $extra = explode('|', $extra);
  498. $extras = new stdClass;
  499. foreach ($extra as $e)
  500. {
  501. if (strpos($e, ':') === false)
  502. {
  503. continue;
  504. }
  505. list($key, $val) = explode(':', $e, 2);
  506. $extras->{$key} = $val;
  507. }
  508. $attribs = '';
  509. if (isset($extras->class))
  510. {
  511. $attribs .= 'class="' . $extras->class . '"';
  512. }
  513. $style = array();
  514. if (isset($extras->width))
  515. {
  516. if (is_numeric($extras->width))
  517. {
  518. $extras->width .= 'px';
  519. }
  520. $style[] = 'width:' . $extras->width;
  521. }
  522. if (isset($extras->height))
  523. {
  524. if (is_numeric($extras->height))
  525. {
  526. $extras->height .= 'px';
  527. }
  528. $style[] = 'height:' . $extras->height;
  529. }
  530. if (isset($extras->align))
  531. {
  532. $style[] = 'float:' . $extras->align;
  533. }
  534. if (!isset($extras->align) && isset($extras->float))
  535. {
  536. $style[] = 'float:' . $extras->float;
  537. }
  538. if (!empty($style))
  539. {
  540. $attribs .= ' style="' . implode(';', $style) . ';"';
  541. }
  542. $start_div['tag'] = trim('<div ' . trim($attribs)) . '>';
  543. return array($start_div, $end_div);
  544. }
  545. }