PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/system/cms/libraries/Tags.php

https://github.com/JamieLomas/pyrocms
PHP | 920 lines | 666 code | 62 blank | 192 comment | 17 complexity | 09afbe9b62b5bf3e7aa3b782dadbb572 MD5 | raw file
  1. <?php
  2. /**
  3. * Tags
  4. *
  5. * A simple tag parsing library.
  6. *
  7. * @package Tags
  8. * @version 1.0
  9. * @author Dan Horrigan <http://dhorrigan.com>
  10. * @license Apache License v2.0
  11. * @copyright 2010 Dan Horrigan
  12. *
  13. * Licensed under the Apache License, Version 2.0 (the "License");
  14. * you may not use this file except in compliance with the License.
  15. * You may obtain a copy of the License at
  16. *
  17. * http://www.apache.org/licenses/LICENSE-2.0
  18. *
  19. * Unless required by applicable law or agreed to in writing, software
  20. * distributed under the License is distributed on an "AS IS" BASIS,
  21. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22. * See the License for the specific language governing permissions and
  23. * limitations under the License.
  24. */
  25. class Tags {
  26. private $_trigger = '';
  27. private $_l_delim = '{';
  28. private $_r_delim = '}';
  29. private $_mark = 'k0dj3j4nJHDj22j';
  30. private $_escape = 'noparse';
  31. private $_regex_all_tags = '';
  32. private $_tag_count = 0;
  33. private $_current_callback = array();
  34. private $_skip_content = array();
  35. private $_tags = array();
  36. private $_persistent_tags = array();
  37. private $_known_tags = array();
  38. // --------------------------------------------------------------------
  39. /**
  40. * Constructor
  41. *
  42. * @access public
  43. * @param array The custom config
  44. * @return void
  45. */
  46. public function __construct($config = array())
  47. {
  48. foreach ($config as $key => $val)
  49. {
  50. if (isset($this->{'_' . $key}))
  51. {
  52. $this->{'_' . $key} = $val;
  53. }
  54. }
  55. }
  56. // --------------------------------------------------------------------
  57. /**
  58. * Set Delimiters
  59. *
  60. * Set the delimeters for the tags
  61. *
  62. * @access public
  63. * @param string The left delimeter
  64. * @param string The right delimeter
  65. * @return object Returns $this to enable method chaining
  66. */
  67. public function set_delimiters($left, $right)
  68. {
  69. $this->_l_delim = $left;
  70. $this->_r_delim = $right;
  71. return $this;
  72. }
  73. // --------------------------------------------------------------------
  74. /**
  75. * Set Trigger
  76. *
  77. * Sets the tag trigger to use. This allows you to only consider tags
  78. * that have a trigger:
  79. *
  80. * {tag:name}{/tag:name}
  81. *
  82. * @access public
  83. * @param string The tag trigger
  84. * @return object Returns $this to enable method chaining
  85. */
  86. public function set_trigger($trigger)
  87. {
  88. $this->_trigger = $trigger;
  89. return $this;
  90. }
  91. // --------------------------------------------------------------------
  92. /**
  93. * Set Skip Content
  94. *
  95. * ...
  96. *
  97. * @access public
  98. * @param array The skip content
  99. * @return object Returns $this to enable method chaining
  100. */
  101. public function set_skip_content($skip_content = array())
  102. {
  103. $this->_skip_content = $skip_content;
  104. return $this;
  105. }
  106. // --------------------------------------------------------------------
  107. /**
  108. * Set Skip Content
  109. *
  110. * ...
  111. *
  112. * @access public
  113. * @param array The skip content
  114. * @return object Returns $this to enable method chaining
  115. */
  116. public function set_persistent_tags($segments = array())
  117. {
  118. $this->_persistent_tags = $segments;
  119. return $this;
  120. }
  121. // --------------------------------------------------------------------
  122. /**
  123. * Parse
  124. *
  125. * Parses the content and returns an array with marked content and tags
  126. * or the resulting content from calling the callback for each tag.
  127. *
  128. * @access public
  129. * @param string The content to parse
  130. * @param array The callback for each tag
  131. * @return mixed Either the tags as array or callback results
  132. */
  133. public function parse($content, $data = array(), $callback = array())
  134. {
  135. if (trim($this->_trigger) == '')
  136. {
  137. throw new Exception('You must set a trigger before you can parse the content.');
  138. return $content;
  139. }
  140. static $func_depth = 0;
  141. log_message('debug', 'Tag Class: Parser method Initialized');
  142. $this->_current_callback = $callback;
  143. $this->_regex_all_tags = '/' . $this->_l_delim . $this->_trigger . '[^' . $this->_l_delim . $this->_r_delim . ']*?' . $this->_r_delim . '/i';
  144. $this->_tags = array();
  145. $content = $this->parse_globals($this->_escape_tags($content), $data);
  146. if ($this->_skip_content)
  147. {
  148. if (preg_match_all('/{[\w\d_]*?}/i', $content, $strs))
  149. {
  150. $ignore = array('{else}');
  151. $escaped = array();
  152. $total = sizeof($strs[0]);
  153. for ($i = 0; $i < $total; $i++)
  154. {
  155. $str = $strs[0][$i];
  156. if (in_array($str, $ignore) OR in_array($str, $escaped))
  157. {
  158. $ignore[] = $str;
  159. continue;
  160. }
  161. $escaped[($str_escaped = escape_tags($str))] = $str;
  162. $content = str_replace($str, $str_escaped, $content);
  163. }
  164. if ($escaped)
  165. {
  166. log_message('debug', "There's a probability of exists a syntax error in:" . PHP_EOL . "\t\t\t\t\t\t\t\t=> " . $content);
  167. }
  168. }
  169. foreach ($this->_skip_content as $marker => $safe_content)
  170. {
  171. $content = str_replace($marker, $safe_content, $content);
  172. }
  173. $this->_skip_content = array();
  174. }
  175. $content = $this->_extract_tags($content);
  176. if (isset($escaped) && $escaped)
  177. {
  178. $content = str_replace(array_keys($escaped), array_values($escaped), $content);
  179. }
  180. if ( ! $this->_tags)
  181. {
  182. $content = $this->parse_php($this->parse_conditionals($content), $data);
  183. log_message('debug', 'Tag Class: Parser method End');
  184. return array(
  185. 'content' => $content,
  186. 'tags' => array()
  187. );
  188. }
  189. $content = $this->_replace_data($content, $data);
  190. $has_callback = FALSE;
  191. // If there is a callback, call it for each tag
  192. if ( ! empty($callback) AND is_callable($callback))
  193. {
  194. $has_callback = TRUE;
  195. $remainings = array();
  196. foreach ($this->_tags as $key => $tag)
  197. {
  198. if ($tag['full_segments'] === $this->_escape)
  199. {
  200. $content = str_replace($tag['marker'], $tag['content'], $content);
  201. continue;
  202. }
  203. if ($remainings)
  204. {
  205. foreach ($remainings as $marker => &$remaining)
  206. {
  207. if (strpos($tag['full_tag'], $marker) !== FALSE)
  208. {
  209. list($replacements, $return_data) = $remaining;
  210. $tag['full_tag'] = str_replace($marker, $return_data, $tag['full_tag'], $count);
  211. foreach ($tag['attributes'] as &$attribute)
  212. {
  213. $attribute = str_replace($marker, $return_data, $attribute, $count2);
  214. if ($count2 >= $count)
  215. {
  216. break;
  217. }
  218. }
  219. if ($count >= $replacements)
  220. {
  221. unset($remainings[$marker]);
  222. }
  223. else
  224. {
  225. $remaining[0] -= $count;
  226. }
  227. }
  228. }
  229. }
  230. if (in_array($tag['full_segments'], $this->_persistent_tags))
  231. {
  232. $this->_known_tags[] = $tag;
  233. unset($this->_tags[$key]);
  234. continue;
  235. }
  236. ++$func_depth;
  237. $return_data = call_user_func($callback, $tag);
  238. --$func_depth;
  239. $content = str_replace($tag['marker'], $return_data, $content, $count);
  240. if ($count < $tag['replacements'])
  241. {
  242. $remainings[$tag['marker']] = array($tag['replacements'], $return_data);
  243. }
  244. unset($this->_tags[$key]);
  245. }
  246. }
  247. // If there is no callback then lets loop through any remaining tags and just set them as ''
  248. else
  249. {
  250. foreach ($this->_tags as $key => $tag)
  251. {
  252. $content = str_replace($tag['marker'], '', $content);
  253. unset($this->_tags[$key]);
  254. }
  255. }
  256. if ($func_depth === 0 && $has_callback && ! (empty($this->_persistent_tags) && empty($this->_known_tags)))
  257. {
  258. foreach ($this->_known_tags as $key => $tag)
  259. {
  260. if (in_array($tag['full_segments'], $this->_persistent_tags))
  261. {
  262. $return_data = call_user_func($callback, $tag);
  263. $content = str_replace($tag['marker'], $return_data, $content);
  264. }
  265. }
  266. $this->_persistent_tags = array();
  267. $this->_known_tags = array();
  268. }
  269. $content = $this->parse_php($this->parse_conditionals($content), $data);
  270. log_message('debug', 'Tag Class: Parser method End');
  271. return array(
  272. 'content' => $content,
  273. 'tags' => $this->_tags
  274. );
  275. }
  276. private function _escape_tags($content = '')
  277. {
  278. $a = $this->_l_delim . $this->_trigger . $this->_escape . $this->_r_delim;
  279. $b = substr_replace($a, '/', 1, 0);
  280. $offset = 0;
  281. while (($start = strpos($content, $a, $offset)) !== FALSE && (($end = strpos($content, $b, ($start += strlen($a)))) !== FALSE))
  282. {
  283. $_start = $start;
  284. while (($_start = strpos($content, $a, $_start)) !== FALSE && ($_start += strlen($a)) < $end)
  285. {
  286. $end = strpos($content, $b, ($end += strlen($b)));
  287. }
  288. $lenght = $end - $start;
  289. $replacement = escape_tags(substr($content, $start, $lenght));
  290. $content = substr_replace($content, $replacement, $start, $lenght);
  291. $offset = $start + strlen($replacement . $b);
  292. }
  293. return $content;
  294. }
  295. private function _extract_tags($orig_content = '', $attemp = 0)
  296. {
  297. while (($start = strpos($orig_content, $this->_l_delim . $this->_trigger)) !== FALSE)
  298. {
  299. $content = $orig_content;
  300. if ( ! preg_match($this->_regex_all_tags, $content, $tag))
  301. {
  302. break;
  303. }
  304. // We use these later
  305. $tag_len = strlen($tag[0]);
  306. $full_tag = $tag[0];
  307. $start = strpos($content, $full_tag);
  308. // Trim off the left and right delimeters
  309. $tag = trim($full_tag, $this->_l_delim . $this->_r_delim);
  310. // Get the segments of the tag
  311. $segments = preg_replace('/(.*?)\s+.*/', '$1', $tag);
  312. // Get the attribute string
  313. $attributes = (preg_match('/\s+.*/', $tag, $args)) ? trim($args[0]) : '';
  314. // Lets start to create the parsed tag
  315. $parsed = array(
  316. 'full_tag' => $full_tag,
  317. 'attributes' => $this->_parse_attributes($attributes),
  318. 'full_segments' => str_replace($this->_trigger, '', $segments),
  319. 'segments' => $this->_parse_segments(str_replace($this->_trigger, '', $segments)),
  320. 'skip_content' => array()
  321. );
  322. // Set the end tag to search for
  323. $end_tag = $this->_l_delim . '/' . $segments . $this->_r_delim;
  324. // Lets trim off the first part of the content
  325. $content = substr($content, $start + $tag_len);
  326. // If there is an end tag, get and set the content.
  327. if (($end = strpos($content, $end_tag)) !== FALSE)
  328. {
  329. if (substr_count($content, '{if', 0, $end) >= substr_count($content, '{/if', 0, $end))
  330. {
  331. $content = substr($content, 0, $end);
  332. $parsed['full_tag'] .= $content . $end_tag;
  333. $parsed = $this->_skip_content($parsed, $content);
  334. }
  335. else
  336. {
  337. $parsed['content'] = '';
  338. }
  339. }
  340. else
  341. {
  342. $parsed['content'] = '';
  343. }
  344. $parsed['marker'] = 'marker_' . ($this->_tag_count++) . $this->_mark;
  345. $orig_content = str_replace($parsed['full_tag'], $parsed['marker'], $orig_content, $count);
  346. $parsed['replacements'] = $count;
  347. $this->_tags[] = $parsed;
  348. }
  349. if ($start !== FALSE && $attemp < 1)
  350. {
  351. $tag_name = $this->_get_tag_by_pos($orig_content, $start);
  352. $tag_replace = $this->_l_delim . escape_tags(trim($tag_name, $this->_l_delim . $this->_r_delim)) . $this->_r_delim;
  353. $orig_content = str_replace($tag_name, $tag_replace, $orig_content);
  354. $orig_content = $this->_extract_tags($orig_content, $attemp+1);
  355. }
  356. return $orig_content;
  357. }
  358. private function _get_tag_by_pos($content = '', $start = 0)
  359. {
  360. $tag_name = $this->_l_delim . $this->_trigger;
  361. $tag_unclosed = substr($content, $start + strlen($tag_name));
  362. $tag_name_parts = explode($this->_r_delim, $tag_unclosed);
  363. // skip
  364. $i = 0;
  365. while (list($key, $part) = each($tag_name_parts))
  366. {
  367. $tag_name .= $part . $this->_r_delim;
  368. // there may be something like: foo="{bar}" inside the tag that we need
  369. if (($l_delim_total = substr_count($part, $this->_l_delim)) > 1)
  370. {
  371. $i += $l_delim_total - 1;
  372. }
  373. // it seems that now we can close the tag
  374. if (strpos($part, $this->_l_delim) === FALSE)
  375. {
  376. // really, we can!
  377. if (($i--) == 0)
  378. {
  379. break;
  380. }
  381. }
  382. }
  383. return $tag_name;
  384. }
  385. private function _skip_content($tag = array(), $content = '')
  386. {
  387. $offset = 0;
  388. while (($tag_start = strpos($content, $this->_l_delim . $this->_trigger, $offset)) !== FALSE)
  389. {
  390. // we need some info before think on skip content
  391. $tag_segments = preg_replace('/(.*?)\s+.*/', '$1', substr($content, $tag_start));
  392. $tag_end = $this->_l_delim . '/' . trim($tag_segments, $this->_l_delim . $this->_r_delim) . $this->_r_delim;
  393. // has nested double tag ?
  394. if (($end = strpos($content, $tag_end, $tag_start)) !== FALSE)
  395. {
  396. // what is the tag name ?
  397. $tag_name = $this->_get_tag_by_pos($content, $tag_start);
  398. // ok! we have the tag name and a position to starts skip content
  399. $start = $tag_start + strlen($tag_name);
  400. // generate a marker
  401. $marker = 'skip_' . ($this->_tag_count++) . $this->_mark;
  402. $safe_content = substr($content, $start, $end - $start);
  403. if (strpos($safe_content, $this->_mark) !== FALSE)
  404. {
  405. foreach ($this->_tags as $_tag)
  406. {
  407. if (strpos($safe_content, $_tag['marker']) !== FALSE)
  408. {
  409. $safe_content = str_replace($_tag['marker'], $_tag['full_tag'], $safe_content);
  410. }
  411. }
  412. }
  413. // save a copy of safe content
  414. $tag['skip_content'][$marker] = $safe_content;
  415. // finally skip the content
  416. $content = substr_replace($content, $marker, $start, $end - $start);
  417. // increase offset position
  418. $offset = strpos($content, $marker) + strlen($marker . $tag_end);
  419. }
  420. else
  421. {
  422. // just increase offset position
  423. ++$offset;
  424. }
  425. }
  426. $tag['content'] = $content;
  427. return $tag;
  428. }
  429. private function _replace_data($content = '', $data = array())
  430. {
  431. // Clean up the array
  432. $data = $this->_force_array($data);
  433. if ( ! (is_array($data) && $data))
  434. {
  435. return $content;
  436. }
  437. $oc_nok = 0;
  438. $oc_ok = 0;
  439. $check_persistent_tags = ! empty($this->_persistent_tags);
  440. foreach ($this->_tags as $key => $tag)
  441. {
  442. if ($check_persistent_tags && in_array($tag['full_segments'], $this->_persistent_tags))
  443. {
  444. continue;
  445. }
  446. // Parse the single tags
  447. if (empty($tag['content']))
  448. {
  449. $return_data = $this->_parse_data_single($tag, $data);
  450. }
  451. // Parse the double tags
  452. else
  453. {
  454. $return_data = FALSE;
  455. if (array_key_exists($tag['segments'][0], $data))
  456. {
  457. $return_data = $this->_parse_data_double($tag, $data);
  458. }
  459. }
  460. // If the tag referenced data then put that data in the content
  461. if ($return_data === FALSE)
  462. {
  463. continue;
  464. }
  465. $content = str_replace($tag['marker'], $return_data, $content, $count);
  466. // Search and set missing replacements (tags in content and/or attributes of anothers tags)
  467. $i = $key;
  468. while (($count < $tag['replacements']) && isset($this->_tags[++$i]))
  469. {
  470. $next_tag =& $this->_tags[$i];
  471. if ( ! $occurences = substr_count($next_tag['full_tag'], $tag['marker']))
  472. {
  473. continue;
  474. }
  475. log_message('debug', 'Tag Class: There more occurrences in:' . PHP_EOL . "\t\t\t\t\t\t\t\t=> " . $next_tag['full_tag']);
  476. if (($count + $occurences) > $tag['replacements'])
  477. {
  478. $count_attributes = $count;
  479. $count_content = $count;
  480. foreach ($next_tag['attributes'] as $j => $attribute)
  481. {
  482. while (($pos = strpos($attribute, $tag['marker'])) !== FALSE)
  483. {
  484. $next_tag['attributes'][$j] = substr_replace($attribute, $return_data, $pos, strlen($tag['marker']));
  485. ++$count_attributes;
  486. if ($count_attributes === $tag['replacements'])
  487. {
  488. // todo: update full_tag
  489. break 3;
  490. }
  491. }
  492. }
  493. if ($next_tag['content'])
  494. {
  495. while (($pos = strpos($next_tag['content'], $tag['marker'])) !== FALSE)
  496. {
  497. $next_tag['content'] = substr_replace($next_tag['content'], $return_data, $pos, strlen($tag['marker']));
  498. ++$count_content;
  499. if ($count_content === $tag['replacements'])
  500. {
  501. // todo: update full_tag
  502. break 3;
  503. }
  504. }
  505. }
  506. ++$oc_nok;
  507. }
  508. else
  509. {
  510. foreach ($next_tag['attributes'] as $j => $attribute)
  511. {
  512. $next_tag['attributes'][$j] = str_replace($tag['marker'], $return_data, $attribute);
  513. }
  514. $next_tag['content'] = str_replace($tag['marker'], $return_data, $next_tag['content'], $count_content);
  515. $next_tag['full_tag'] = str_replace($tag['marker'], $return_data, $next_tag['full_tag'], $count_full_tag);
  516. ++$oc_ok;
  517. }
  518. }
  519. $oc_nok && log_message('debug', sprintf('Tag Class: Find %d occurrences nok', $oc_nok));
  520. $oc_ok && log_message('debug', sprintf('Tag Class: Find %d occurrences ok', $oc_ok));
  521. unset($this->_tags[$key]);
  522. }
  523. return $content;
  524. }
  525. // --------------------------------------------------------------------
  526. public function parse_conditionals($content)
  527. {
  528. if (strpos($content, '{if ') === false)
  529. {
  530. return $content;
  531. }
  532. preg_match_all('#{if (.*?)}#i', $content, $matches, PREG_OFFSET_CAPTURE);
  533. $len_offset = 0;
  534. foreach ($matches[0] as $match)
  535. {
  536. $replacement = preg_replace('#((^|\(|\)|\s|\+|\-|\*|\/|\.|\||\&|\>|\<|\=)((?!true|false|null)[a-z][a-z0-9-_]-_*))#i', '$2\$$3', $match[0]);
  537. $content = substr($content, 0, $match[1] + $len_offset) . $replacement . substr($content, $match[1] + strlen($match[0]) + $len_offset);
  538. $len_offset += strlen($replacement) - strlen($match[0]);
  539. }
  540. preg_match_all('#{elseif (.*?)}#i', $content, $matches, PREG_OFFSET_CAPTURE);
  541. $len_offset = 0;
  542. foreach ($matches[0] as $match)
  543. {
  544. $replacement = preg_replace('#((^|\(|\)|\s|\+|\-|\*|\/|\.|\||\&|\>|\<|\=)((?!true|false|null)[a-z][a-z0-9-_]*))#i', '$2\$$3', $match[0]);
  545. $content = substr($content, 0, $match[1] + $len_offset) . $replacement . substr($content, $match[1] + strlen($match[0]) + $len_offset);
  546. $len_offset += strlen($replacement) - strlen($match[0]);
  547. }
  548. $content = preg_replace('#{if (.*?)}#i', '<?php if($1): ?>', $content);
  549. $content = preg_replace('#{elseif (.*?)}#i', '<?php elseif($1): ?>', $content);
  550. $content = preg_replace('#{else}#i', '<?php else: ?>', $content);
  551. $content = preg_replace('#{/if}#i', '<?php endif; ?>', $content);
  552. return $content;
  553. }
  554. // --------------------------------------------------------------------
  555. private function parse_php($_content_to_parse, $data = array())
  556. {
  557. extract($data);
  558. ob_start();
  559. echo eval('?>' . $_content_to_parse . '<?php ');
  560. $_content_to_parse = ob_get_contents();
  561. ob_end_clean();
  562. return $_content_to_parse;
  563. }
  564. // --------------------------------------------------------------------
  565. /**
  566. * Parse Globals
  567. *
  568. * Parses global data tags. These are tags that do not use a trigger
  569. * and have a variable in the $data array. This enables you to use
  570. * globals inside of other tags:
  571. *
  572. * The Tag:
  573. * {tag:blog:posts offset="{offset}"}
  574. *
  575. * The data array:
  576. * array(
  577. * 'offset' => $this->uri->segment(3),
  578. * );
  579. *
  580. * @access public
  581. * @param string The content to parse
  582. * @param array The globals
  583. * @return string The parsed content
  584. */
  585. public function parse_globals($content, $data)
  586. {
  587. foreach ($data as $var => $value)
  588. {
  589. if (is_scalar($value))
  590. {
  591. $content = str_replace($this->_l_delim . $var . $this->_r_delim, $value, $content);
  592. }
  593. }
  594. return $content;
  595. }
  596. // --------------------------------------------------------------------
  597. /**
  598. * Get the data pertaining to the given single tag.
  599. *
  600. * Example Data:
  601. * $data = array(
  602. * 'books' => array(
  603. * 'count' => 2
  604. * )
  605. * );
  606. *
  607. * Example Tag:
  608. * {books:count}
  609. *
  610. * @access private
  611. * @param array The single tag
  612. * @param array The data to parse
  613. * @return mixed Either the data for the tag or FALSE
  614. */
  615. private function _parse_data_single($tag, $data)
  616. {
  617. foreach ($tag['segments'] as $segment)
  618. {
  619. if ( ! is_array($data) OR ! isset($data[$segment]))
  620. {
  621. return FALSE;
  622. }
  623. $data = $data[$segment];
  624. }
  625. return $data;
  626. }
  627. // --------------------------------------------------------------------
  628. /**
  629. * Get the data pertaining to the given double tag. This will
  630. * loop through arrays of given data
  631. *
  632. * Example Data:
  633. * $data = array(
  634. * 'books' => array(
  635. * array(
  636. * 'title' => 'PHP for Dummies',
  637. * 'author' => 'John Doe'
  638. * ),
  639. * array(
  640. * 'title' => 'CodeIgniter for Dummies',
  641. * 'author' => 'Jane Doe'
  642. * )
  643. * )
  644. * );
  645. *
  646. * Example Tags:
  647. * {books}
  648. * {title} by {author}<br />
  649. * {/books}
  650. *
  651. * @access private
  652. * @param array The double tag
  653. * @param array The data to parse
  654. * @return mixed Either the data for the tag or FALSE
  655. */
  656. private function _parse_data_double($tag, $data)
  657. {
  658. $return_data = '';
  659. $new_data = $data;
  660. foreach ($tag['segments'] as $segment)
  661. {
  662. if ( ! is_array($new_data) OR ! isset($new_data[$segment]))
  663. {
  664. return FALSE;
  665. }
  666. $new_data = $new_data[$segment];
  667. }
  668. $temp = new Tags;
  669. $temp->set_trigger($this->_trigger);
  670. foreach ($new_data as $val)
  671. {
  672. if ( ! is_array($val))
  673. {
  674. $val = array($val);
  675. }
  676. // We add the array element to the full data array so that full data
  677. // tags can work within double data tags
  678. $val = $val + $data;
  679. $return = $temp->parse($tag['content'], $val, $this->_current_callback);
  680. $return_data .= $return['content'];
  681. }
  682. unset($temp);
  683. return $return_data;
  684. }
  685. // --------------------------------------------------------------------
  686. /**
  687. * Forces normal multi-dimensional arrays and objects into an
  688. * array structure that will work with EE/Mojo/CI Parsers.
  689. *
  690. * @author Phil Sturgeon <http://philsturgeon.co.uk>
  691. * @access private
  692. * @param mixed The object or array to clean
  693. * @param int Used for recursion
  694. * @return array The clean array
  695. */
  696. private function _force_array($var, $level = 0)
  697. {
  698. if (is_object($var))
  699. {
  700. $var = (array) $var;
  701. }
  702. if (is_array($var))
  703. {
  704. // Make sure everything else is array or single value
  705. foreach ($var as &$child)
  706. {
  707. if (is_object($child))
  708. {
  709. $child = $this->_force_array($child, $level + 1);
  710. }
  711. }
  712. }
  713. return $var;
  714. }
  715. // --------------------------------------------------------------------
  716. /**
  717. * Parse Attributes
  718. *
  719. * Parses the string of attributes into a keyed array
  720. *
  721. * @param string The string of attributes
  722. * @return array The keyed array of attributes
  723. */
  724. private function _parse_attributes($attributes)
  725. {
  726. preg_match_all('/(.*?)\s*=\s*(\042|\047)(.*?)\\2/is', $attributes, $parts);
  727. // The tag has no attrbutes
  728. if (empty($parts[0]))
  729. {
  730. return array();
  731. }
  732. // The tag has attributes, so lets parse them
  733. else
  734. {
  735. $attr = array();
  736. for ($i = 0; $i < count($parts[1]); $i++)
  737. {
  738. $attr[trim($parts[1][$i])] = $parts[3][$i];
  739. }
  740. }
  741. return $attr;
  742. }
  743. // --------------------------------------------------------------------
  744. /**
  745. * Parse Segments
  746. *
  747. * Parses the string of segments into an array
  748. *
  749. * @param string The string of segments
  750. * @return array The array of segments
  751. */
  752. private function _parse_segments($segments)
  753. {
  754. $segments = explode(':', $segments);
  755. return $segments;
  756. }
  757. }
  758. /* End of file Tags.php */