public function find_all_tags_and_repeaters($type = 'content', $contents = false) { $template = $this->template; $path = $this->file; if ($contents === false) { $contents = $this->load(); } $untouched_content = $contents; $out = array(); // Excluded tags are discarded $tag_pairs_to_exclude = array(); $tag_pairs_with_empty_openers = array('blocks'); // List of tags to process. Blocks needs to come before others, as blocks can contain e.g. repeaters. $tag_pairs_to_process = array('blocks', 'repeater'); if (PERCH_RUNWAY) { $tag_pairs_to_process[] = 'related'; } // Add excluded tags so they can be discarded. $tag_pairs_to_process = array_merge($tag_pairs_to_process, $tag_pairs_to_exclude); foreach ($tag_pairs_to_process as $tag_type) { // parse out tag pairs $empty_opener = in_array($tag_type, $tag_pairs_with_empty_openers); $close_tag = '</perch:' . $tag_type . '>'; $close_tag_len = mb_strlen($close_tag); $open_tag = '<perch:' . $tag_type . ($empty_opener ? '' : ' '); $order = 1; // escape hatch $i = 0; $max_loops = 100; // loop through while we have closing tags while ($close_pos = mb_strpos($contents, $close_tag)) { // we always have to go from the start, as the string length changes, // but stop at the closing tag $chunk = mb_substr($contents, 0, $close_pos); // search from the back of the chunk for the opening tag $open_pos = mb_strrpos($chunk, $open_tag); // get the pair html chunk $len = $close_pos + $close_tag_len - $open_pos; $pair_html = mb_substr($contents, $open_pos, $len); // find the opening tag - it's right at the start $opening_tag_end_pos = mb_strpos($pair_html, '>') + 1; $opening_tag = mb_substr($pair_html, 0, $opening_tag_end_pos); // condition contents $condition_contents = mb_substr($pair_html, $opening_tag_end_pos, 0 - $close_tag_len); // Do the business $OpeningTag = new PerchXMLTag($opening_tag); $tmp = array(); if ($tag_type == 'repeater') { $Repeater = new PerchRepeater($OpeningTag->attributes); $Repeater->set('id', $OpeningTag->id()); //$Repeater->tags = $this->find_all_tags($type, $condition_contents); $Repeater->tags = $this->find_all_tags_and_repeaters($type, $condition_contents); $tmp['tag'] = $Repeater; } elseif ($tag_type == 'blocks') { $OpeningTag->set('id', '_blocks'); $OpeningTag->set('type', 'PerchBlocks'); $tmp['tag'] = $OpeningTag; } else { $tmp['tag'] = $OpeningTag; } // Set the order if ($OpeningTag->order()) { $tmp['order'] = (int) $OpeningTag->order(); } else { $tmp['order'] = strpos($untouched_content, $opening_tag); } // If the tag isn't one to strip/exclude, add it to the list. if (!in_array($tag_type, $tag_pairs_to_exclude)) { $out[] = $tmp; } // Remove the pair so we can parse the next one $contents = str_replace($pair_html, '', $contents); // escape hatch counter $i++; if ($i > $max_loops) { return $contents; } } } $s = '/<perch:(' . $type . '|categories)[^>]*>/'; $count = preg_match_all($s, $contents, $matches); if ($count > 0) { $i = 100; if (is_array($matches[0])) { foreach ($matches[0] as $match) { $tmp = array(); $tmp['tag'] = new PerchXMLTag($match); if (!in_array('categories', $this->disabled_features)) { if ($tmp['tag']->tag_name() == 'perch:categories') { $tmp['tag']->set('type', 'category'); } } if ($tmp['tag']->tag_name() == 'perch:related') { $tmp['tag']->set('type', 'related'); } if ($tmp['tag']->type() != 'repeater') { if ($tmp['tag']->order()) { $tmp['order'] = (int) $tmp['tag']->order(); } else { $tmp['order'] = strpos($untouched_content, $match); #PerchUtil::debug('Setting order to: '.$tmp['order']); $i++; } $out[] = $tmp; } } } } if (PerchUtil::count($out)) { // sort tags using 'order' attribute $out = PerchUtil::array_sort($out, 'order'); $final = array(); foreach ($out as $tag) { $final[] = $tag['tag']; } return $final; } return false; }