$img ) {
$options['images'][ $k ]['uid'] = hash( 'crc32b', 'image-' . $k . $img['id'] );
}
if ( $count < 0 ) {
$count = 99999;
}
// Load certain taxonomies.
$images = array();
// phpcs:ignore WordPress.Security.NonceVerification
if ( ! $for_filter && ( isset( $_GET['vp_filter'] ) || isset( $query_opts['vp_filter'] ) ) ) {
// phpcs:ignore WordPress.Security.NonceVerification
$category = sanitize_text_field( wp_unslash( $_GET['vp_filter'] ?? $query_opts['vp_filter'] ) );
foreach ( $options['images'] as $img ) {
if ( isset( $img['categories'] ) && is_array( $img['categories'] ) ) {
foreach ( $img['categories'] as $cat ) {
if ( self::create_slug( $cat ) === $category ) {
$images[] = $img;
break;
}
}
}
}
} else {
$images = $options['images'];
}
$images_ids = array();
foreach ( $images as $k => $img ) {
$images_ids[] = (int) $img['id'];
}
// Find all used attachments.
$all_attachments = get_posts(
array(
'post_type' => 'attachment',
'posts_per_page' => -1,
'paged' => -1,
'post__in' => $images_ids,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
)
);
// prepare titles and descriptions.
foreach ( $images as $k => $img ) {
$img_meta = array(
'title' => '',
'description' => '',
'caption' => '',
'alt' => '',
'none' => '',
'date' => '',
);
// Find current attachment post data.
$attachment = false;
foreach ( $all_attachments as $post ) {
if ( $post->ID === (int) $img['id'] ) {
$attachment = $post;
break;
}
}
if ( $attachment ) {
// get image meta if needed.
if ( 'none' !== $options['images_titles_source'] || 'none' !== $options['images_descriptions_source'] ) {
if ( $attachment && 'attachment' === $attachment->post_type ) {
$img_meta['title'] = $attachment->post_title;
$img_meta['description'] = $attachment->post_content;
$img_meta['caption'] = wp_get_attachment_caption( $attachment->ID );
$img_meta['alt'] = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true );
}
}
// title.
if ( 'custom' !== $options['images_titles_source'] ) {
$images[ $k ]['title'] = isset( $img_meta[ $options['images_titles_source'] ] ) ? $img_meta[ $options['images_titles_source'] ] : '';
}
// description.
if ( 'custom' !== $options['images_descriptions_source'] ) {
$images[ $k ]['description'] = isset( $img_meta[ $options['images_descriptions_source'] ] ) ? $img_meta[ $options['images_descriptions_source'] ] : '';
}
// add published date.
$images[ $k ]['published_time'] = get_the_date( 'Y-m-d H:i:s', $attachment );
}
}
// order.
$custom_order = false;
$custom_order_direction = $options['images_order_direction'];
if ( isset( $options['images_order_by'] ) ) {
$custom_order = $options['images_order_by'];
}
// custom sorting.
// phpcs:ignore WordPress.Security.NonceVerification
if ( isset( $_GET['vp_sort'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification
$custom_get_order = sanitize_text_field( wp_unslash( $_GET['vp_sort'] ) );
switch ( $custom_get_order ) {
case 'title':
case 'date':
$custom_order = $custom_get_order;
$custom_order_direction = 'asc';
break;
case 'title_desc':
$custom_order = 'title';
$custom_order_direction = 'desc';
break;
case 'date_desc':
$custom_order = 'date';
$custom_order_direction = 'desc';
break;
}
}
if ( $custom_order && ! empty( $images ) ) {
switch ( $custom_order ) {
case 'date':
case 'title':
$sort_tmp = array();
$new_images = array();
$sort_by = 'date';
if ( 'title' === $custom_order ) {
$sort_by = 'title';
}
foreach ( $images as &$ma ) {
$sort_tmp[] = &$ma[ $sort_by ];
}
array_multisort( $sort_tmp, $images );
foreach ( $images as &$ma ) {
$new_images[] = $ma;
}
$images = $new_images;
break;
case 'rand':
// We don't need to randomize order for filter,
// because filter list will be always changed once AJAX loaded.
if ( ! $for_filter ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.rand_seeding_mt_srand
mt_srand( self::get_rand_seed_session() );
for ( $i = count( $images ) - 1; $i > 0; $i-- ) {
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.rand_mt_rand
$j = @mt_rand( 0, $i );
$tmp = $images[ $i ];
$images[ $i ] = $images[ $j ];
$images[ $j ] = $tmp;
}
}
break;
}
if ( 'desc' === $custom_order_direction ) {
$images = array_reverse( $images );
}
}
// pages count.
$query_opts['max_num_pages'] = ceil( count( $images ) / $count );
$start_from_item = ( $paged - 1 ) * $count;
$end_on_item = $start_from_item + $count;
if ( $for_filter ) {
$start_from_item = 0;
$end_on_item = 99999;
}
// get images for current page only.
foreach ( (array) $images as $k => $img ) {
$i = $k + 1;
if ( $i > $start_from_item && $i <= $end_on_item ) {
$query_opts['images'][] = $img;
}
}
} else {
$query_opts = array(
'posts_per_page' => $count,
'paged' => $paged,
'orderby' => 'post_date',
'order' => 'DESC',
'post_type' => 'portfolio',
);
// Get all available categories for filter.
if ( $for_filter ) {
$query_opts['posts_per_page'] = -1;
$query_opts['paged'] = -1;
}
// Post based.
if ( 'post-based' === $options['content_source'] ) {
// Exclude IDs.
if ( ! empty( $options['posts_excluded_ids'] ) ) {
$query_opts['post__not_in'] = $options['posts_excluded_ids'];
}
// Order By.
switch ( $options['posts_order_by'] ) {
case 'title':
$query_opts['orderby'] = 'title';
break;
case 'id':
$query_opts['orderby'] = 'ID';
break;
case 'post__in':
$query_opts['orderby'] = 'post__in';
break;
case 'menu_order':
// We should order by `menu_order` and as fallback order by `post_date`.
$query_opts['orderby'] = array(
'menu_order' => $options['posts_order_direction'],
'post_date' => 'desc',
);
break;
case 'comment_count':
$query_opts['orderby'] = 'comment_count';
break;
case 'modified':
$query_opts['orderby'] = 'modified';
break;
case 'rand':
// Update ORDER BY clause to use vpf_random_seed.
$query_opts['orderby'] = 'RAND(' . self::get_rand_seed_session() . ')';
break;
default:
$query_opts['orderby'] = 'post_date';
break;
}
// Order.
$query_opts['order'] = $options['posts_order_direction'];
if ( 'ids' === $options['posts_source'] ) { // IDs.
$query_opts['post_type'] = 'any';
$query_opts['post__not_in'] = array();
if ( ! empty( $options['posts_ids'] ) ) {
$query_opts['post__in'] = $options['posts_ids'];
}
} elseif ( 'custom_query' === $options['posts_source'] ) { // Custom Query.
$query_opts['post_type'] = 'any';
$tmp_arr = array();
parse_str( html_entity_decode( $options['posts_custom_query'] ), $tmp_arr );
$query_opts = array_merge( $query_opts, $tmp_arr );
} elseif ( 'current_query' === $options['posts_source'] ) {
global $wp_query;
if ( $wp_query && isset( $wp_query->query_vars ) && is_array( $wp_query->query_vars ) ) {
$query_vars = $wp_query->query_vars;
// Unset `offset` because if is set, $wp_query overrides/ignores the paged parameter and breaks pagination.
if ( isset( $query_vars['offset'] ) ) {
unset( $query_vars['offset'] );
}
// Add post type.
if ( empty( $query_vars['post_type'] ) && is_singular() ) {
$query_vars['post_type'] = get_post_type( get_the_ID() );
}
// Add pagination paged value.
if ( $query_opts['paged'] && ( ! isset( $query_vars['paged'] ) || ! $query_vars['paged'] ) ) {
$query_vars['paged'] = $query_opts['paged'];
}
$query_opts = $query_vars;
}
} else {
$query_opts['post_type'] = $options['posts_source'];
// Post Types Set.
if ( 'post_types_set' === $options['posts_source'] ) {
$query_opts['post_type'] = (array) $options['post_types_set'];
}
// Taxonomies.
if ( ! empty( $options['posts_taxonomies'] ) && ! isset( $query_opts['tax_query'] ) ) {
$terms_list = get_terms(
get_object_taxonomies( is_array( $query_opts['post_type'] ) ? $query_opts['post_type'] : array( $query_opts['post_type'] ) ),
array(
'hide_empty' => false,
)
);
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
$query_opts['tax_query'] = array(
// We save strings like 'or', 'and'
// but to use it we need these strings in uppercase.
'relation' => strtoupper( $options['posts_taxonomies_relation'] ),
);
// We need this empty array, because when taxonomy selected,
// and posts don't have this taxonomy, we will see all available posts.
// Related topic: https://wordpress.org/support/topic/exclude-certain-category-from-filter/.
if ( 'OR' === $query_opts['tax_query']['relation'] ) {
$query_opts['tax_query'][] = array();
}
foreach ( $options['posts_taxonomies'] as $taxonomy ) {
$taxonomy_name = null;
foreach ( $terms_list as $term ) {
if ( $term->term_id === (int) $taxonomy ) {
$taxonomy_name = $term->taxonomy;
continue;
}
}
if ( $taxonomy_name ) {
$query_opts['tax_query'][] = array(
'taxonomy' => $taxonomy_name,
'field' => 'id',
'terms' => $taxonomy,
);
}
}
}
// Offset.
if ( $options['posts_offset'] ) {
$query_opts['offset'] = $options['posts_offset'] + ( $paged - 1 ) * $count;
}
}
// Avoid duplicates.
// We should prevent this when using filter, since all current posts will be excluded
// from the filter query and we may not see all filter buttons.
if ( ! $for_filter && $options['posts_avoid_duplicate_posts'] ) {
$not_id = (array) ( isset( $query_opts['post__not_in'] ) ? $query_opts['post__not_in'] : array() );
$query_opts['post__not_in'] = array_merge( $not_id, self::get_all_used_posts() );
// Remove posts from post__in.
if ( isset( $query_opts['post__in'] ) ) {
$query_opts['post__in'] = array_diff( (array) $query_opts['post__in'], (array) $query_opts['post__not_in'] );
}
}
}
// Custom sorting.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['vp_sort'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$custom_get_order = sanitize_text_field( wp_unslash( $_GET['vp_sort'] ) );
$custom_order = false;
$custom_order_direction = false;
switch ( $custom_get_order ) {
case 'title':
case 'date':
$custom_order = 'post_' . $custom_get_order;
$custom_order_direction = 'asc';
break;
case 'title_desc':
$custom_order = 'post_title';
$custom_order_direction = 'desc';
break;
case 'date_desc':
$custom_order = 'post_date';
$custom_order_direction = 'desc';
break;
}
if ( $custom_order && $custom_order_direction ) {
$query_opts['orderby'] = $custom_order;
$query_opts['order'] = $custom_order_direction;
}
}
// Load certain taxonomies using custom filter.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! $for_filter && ( isset( $_GET['vp_filter'] ) || isset( $query_opts['vp_filter'] ) ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$taxonomies = sanitize_text_field( wp_unslash( $_GET['vp_filter'] ?? $query_opts['vp_filter'] ) );
$taxonomies = explode( ':', $taxonomies );
if ( $taxonomies && isset( $taxonomies[0] ) && isset( $taxonomies[1] ) ) {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
$query_opts['tax_query'] = array(
'relation' => 'AND',
array(
'taxonomy' => $taxonomies[0],
'field' => 'slug',
'terms' => $taxonomies[1],
),
isset( $query_opts['tax_query'] ) ? $query_opts['tax_query'] : '',
);
}
}
}
$query_opts = apply_filters( 'vpf_extend_query_args', $query_opts, $options, $layout_id );
return $query_opts;
}
/**
* Print notice
*
* @param string $notice notice string.
*/
public static function notice( $notice ) {
if ( ! $notice ) {
return;
}
visual_portfolio()->include_template(
'notices/notices',
array(
'notice' => $notice,
)
);
}
/**
* Print error
*
* @param string $error error string.
*/
public static function error( $error ) {
if ( ! $error ) {
return;
}
visual_portfolio()->include_template(
'errors/errors',
array(
'error' => $error,
)
);
}
/**
* Print filters
*
* @param array $vp_options current vp_list options.
*/
public static function filter( $vp_options ) {
if ( ! $vp_options['filter'] ) {
return;
}
$is_images = 'images' === $vp_options['content_source'];
$is_social = 'social-stream' === $vp_options['content_source'];
$query_opts = self::get_query_params( $vp_options, true );
// Get active item.
$active_item = self::get_filter_active_item( $query_opts );
if ( $is_images || $is_social ) {
$term_items = self::get_images_terms( $query_opts, $active_item );
} else {
$portfolio_query = new WP_Query( $query_opts );
$term_items = self::get_posts_terms( $portfolio_query, $active_item );
}
// Add 'All' active item.
if ( ! empty( $term_items['terms'] ) && $vp_options['filter_text_all'] ) {
array_unshift(
$term_items['terms'],
array(
'filter' => '*',
'label' => $vp_options['filter_text_all'],
'description' => false,
'count' => false,
'id' => 0,
'parent' => 0,
'active' => ! $term_items['there_is_active'],
'url' => self::get_pagenum_link(
array(
'vp_filter' => '',
'vp_page' => 1,
)
),
'class' => 'vp-filter__item' . ( ! $term_items['there_is_active'] ? ' vp-filter__item-active' : '' ),
)
);
}
// No filters available.
if ( empty( $term_items['terms'] ) ) {
return;
}
// get options for the current filter.
$filter_options = array();
$filter_options_slug = 'filter_' . $vp_options['filter'] . '__';
foreach ( $vp_options as $k => $opt ) {
// add option to array.
if ( substr( $k, 0, strlen( $filter_options_slug ) ) === $filter_options_slug ) {
$opt_name = str_replace( $filter_options_slug, '', $k );
$filter_options[ $opt_name ] = $opt;
}
// remove style options from the options list.
if ( substr( $k, 0, strlen( $filter_options_slug ) ) === $filter_options_slug ) {
unset( $vp_options[ $k ] );
}
}
$args = array(
'class' => 'vp-filter',
// phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.BlockComment.NoEmptyLineBefore
/*
* Example:
array(
array(
'filter' => '*',
'label' => $options['filter_text_all'],
'description' => false,
'count' => false,
'active' => true,
'url' => Visual_Portfolio_Get::get_pagenum_link(
array(
'vp_filter' => '',
'vp_page' => 1,
)
),
'class' => 'vp-filter__item',
),
)
*/
'items' => apply_filters( 'vpf_extend_filter_items', $term_items['terms'], $vp_options ),
'show_count' => $vp_options['filter_show_count'],
'opts' => $filter_options,
'vp_opts' => $vp_options,
);
?>
include_template( 'items-list/filter' . $filter_style_pref . '/filter', $args );
// We need to include these styles, since users can insert filters using separate shortcode.
Visual_Portfolio_Assets::store_used_assets(
'visual-portfolio-filter-' . $vp_options['filter'],
'items-list/filter' . $filter_style_pref . '/style',
'template_style'
);
?>
include_template( 'items-list/sort' . $sort_style_pref . '/sort', $args );
// We need to include these styles, since users can insert sort using separate shortcode.
Visual_Portfolio_Assets::store_used_assets(
'visual-portfolio-sort-' . $vp_options['sort'],
'items-list/sort' . $sort_style_pref . '/style',
'template_style'
);
?>
esc_url_raw(
str_replace(
999999999,
'%#%',
remove_query_arg(
'add-to-cart',
self::get_pagenum_link(
array(
'vp_page' => 999999999,
)
)
)
)
),
'format' => '',
'type' => 'array',
'current' => $args['start_page'],
'total' => $args['max_pages'],
'prev_text' => '<',
'next_text' => '>',
'end_size' => 1,
'mid_size' => 2,
)
);
// parse html string and make arrays.
$filtered_links = array();
if ( $pagination_links ) {
foreach ( $pagination_links as $link ) {
$tag_data = self::extract_tags( $link, array( 'a', 'span' ) );
$tag_data = ! empty( $tag_data ) ? $tag_data[0] : $tag_data;
if ( ! empty( $tag_data ) ) {
$atts = isset( $tag_data['attributes'] ) ? $tag_data['attributes'] : false;
$href = $atts && isset( $atts['href'] ) ? $atts['href'] : false;
$class = $atts && isset( $atts['class'] ) ? $atts['class'] : '';
$label = isset( $tag_data['contents'] ) ? $tag_data['contents'] : false;
$arr = array(
'url' => $href,
'label' => $label,
'class' => 'vp-pagination__item',
'active' => strpos( $class, 'current' ) !== false,
'is_prev_arrow' => strpos( $class, 'prev' ) !== false,
'is_next_arrow' => strpos( $class, 'next' ) !== false,
'is_dots' => strpos( $class, 'dots' ) !== false,
);
if ( $arr['active'] ) {
$arr['class'] .= ' vp-pagination__item-active';
}
if ( $arr['is_prev_arrow'] ) {
$arr['class'] .= ' vp-pagination__item-prev';
}
if ( $arr['is_next_arrow'] ) {
$arr['class'] .= ' vp-pagination__item-next';
}
if ( $arr['is_dots'] ) {
$arr['class'] .= ' vp-pagination__item-dots';
}
// skip arrows if disabled.
if ( ! $vp_options['pagination_paged__show_arrows'] && ( $arr['is_prev_arrow'] || $arr['is_next_arrow'] ) ) {
continue;
}
// skip numbers if disabled.
if ( ! $vp_options['pagination_paged__show_numbers'] && ! $arr['is_prev_arrow'] && ! $arr['is_next_arrow'] ) {
continue;
}
$filtered_links[] = apply_filters( 'vpf_pagination_item_data', $arr, $args, $vp_options );
}
}
}
return $filtered_links;
}
/**
* Return current page url with paged support.
*
* @param array $query_arg - custom query arg.
* @return string
*/
public static function get_pagenum_link( $query_arg = array() ) {
// Use current page url.
global $wp;
$current_url = '';
// We should use REQUEST_URI in case if the user used non-default permalinks.
// For example, this one:
// - /index.php/%postname% .
if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
$current_url = esc_url_raw( wp_unslash( ( is_ssl() ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) );
} else {
$current_url = trailingslashit( home_url( $wp->request ) );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_GET ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$current_url = add_query_arg( array_map( 'sanitize_text_field', wp_unslash( $_GET ) ), $current_url );
}
}
if ( isset( $query_arg['vp_filter'] ) && ! $query_arg['vp_filter'] ) {
unset( $query_arg['vp_filter'] );
$current_url = remove_query_arg( 'vp_filter', $current_url );
}
if ( isset( $query_arg['vp_sort'] ) && ! $query_arg['vp_sort'] ) {
unset( $query_arg['vp_sort'] );
$current_url = remove_query_arg( 'vp_sort', $current_url );
}
if ( isset( $query_arg['vp_page'] ) && 1 === $query_arg['vp_page'] ) {
unset( $query_arg['vp_page'] );
$current_url = remove_query_arg( 'vp_page', $current_url );
}
// Add custom query args.
$current_url = add_query_arg( $query_arg, $current_url );
$current_url = apply_filters( 'vpf_get_pagenum_link', $current_url, $query_arg );
return $current_url;
}
/**
* Create slug from user input string.
*
* @param string $str - user string.
* @param string $delimiter - words delimiter.
*
* @return string
*/
public static function create_slug( $str, $delimiter = '_' ) {
$slug = $str;
if ( class_exists( 'Cocur\Slugify\Slugify' ) ) {
$slugify = new Cocur\Slugify\Slugify();
$slug = $slugify->slugify( $str, $delimiter );
// Sometimes Slugify can't add slug to string with unicode.
// We will return the standard string.
if ( ! $slug ) {
$slug = $str;
}
}
return $slug;
}
/**
* Get list with all used posts on the current page.
*
* @return array
*/
public static function get_all_used_posts() {
// add post IDs from main query.
if ( self::$check_main_query && ! self::is_preview() ) {
self::$check_main_query = false;
global $wp_query;
if ( isset( $wp_query ) && isset( $wp_query->posts ) ) {
foreach ( $wp_query->posts as $post ) {
self::$used_posts[] = $post->ID;
}
}
}
return self::$used_posts;
}
/**
* Get list with all used portfolios on the current page.
*
* @return array
*/
public static function get_all_used_layouts() {
return self::$used_layouts;
}
/**
* Extract specific HTML tags and their attributes from a string.
*
* Found in http://w-shadow.com/blog/2009/10/20/how-to-extract-html-tags-and-their-attributes-with-php/
*
* You can either specify one tag, an array of tag names, or a regular expression that matches the tag name(s).
* If multiple tags are specified you must also set the $selfclosing parameter and it must be the same for
* all specified tags (so you can't extract both normal and self-closing tags in one go).
*
* The function returns a numerically indexed array of extracted tags. Each entry is an associative array
* with these keys :
* tag_name - the name of the extracted tag, e.g. "a" or "img".
* offset - the numberic offset of the first character of the tag within the HTML source.
* contents - the inner HTML of the tag. This is always empty for self-closing tags.
* attributes - a name -> value array of the tag's attributes, or an empty array if the tag has none.
* full_tag - the entire matched tag, e.g. 'example.com'. This key
* will only be present if you set $return_the_entire_tag to true.
*
* @param string $html The HTML code to search for tags.
* @param string|array $tag The tag(s) to extract.
* @param bool $selfclosing Whether the tag is self-closing or not. Setting it to null will force the script to try and make an educated guess.
* @param bool $return_the_entire_tag Return the entire matched tag in 'full_tag' key of the results array.
* @param string $charset The character set of the HTML code. Defaults to ISO-8859-1.
*
* @return array An array of extracted tags, or an empty array if no matching tags were found.
*/
private static function extract_tags( $html, $tag, $selfclosing = null, $return_the_entire_tag = false, $charset = 'ISO-8859-1' ) {
if ( is_array( $tag ) ) {
$tag = implode( '|', $tag );
}
// If the user didn't specify if $tag is a self-closing tag we try to auto-detect it by checking against a list of known self-closing tags.
$selfclosing_tags = array( 'area', 'base', 'basefont', 'br', 'hr', 'input', 'img', 'link', 'meta', 'col', 'param' );
if ( is_null( $selfclosing ) ) {
$selfclosing = in_array( $tag, $selfclosing_tags, true );
}
// The regexp is different for normal and self-closing tags because I can't figure out how to make a sufficiently robust unified one.
if ( $selfclosing ) {
$tag_pattern =
'@<(?P' . $tag . ') # \s[^>]+)? # attributes, if any
\s*/?> # /> or just >, being lenient here
@xsi';
} else {
$tag_pattern =
'@<(?P' . $tag . ') # \s[^>]+)? # attributes, if any
\s*> # >
(?P.*?) # tag contents
(?P=tag)> # the closing
@xsi';
}
$attribute_pattern =
'@
(?P\w+) # attribute name
\s*=\s*
(
(?P[\"\'])(?P.*?)(?P=quote) # a quoted value
| # or
(?P[^\s"\']+?)(?:\s+|$) # an unquoted value (terminated by whitespace or EOF)
)
@xsi';
// Find all tags.
if ( ! preg_match_all( $tag_pattern, $html, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) {
// Return an empty array if we didn't find anything.
return array();
}
$tags = array();
foreach ( $matches as $match ) {
// Parse tag attributes, if any.
$attributes = array();
if ( ! empty( $match['attributes'][0] ) ) {
if ( preg_match_all( $attribute_pattern, $match['attributes'][0], $attribute_data, PREG_SET_ORDER ) ) {
// Turn the attribute data into a name->value array.
foreach ( $attribute_data as $attr ) {
if ( ! empty( $attr['value_quoted'] ) ) {
$value = $attr['value_quoted'];
} elseif ( ! empty( $attr['value_unquoted'] ) ) {
$value = $attr['value_unquoted'];
} else {
$value = '';
}
// Passing the value through html_entity_decode is handy when you want to extract link URLs or something like that. You might want to remove or modify this call if it doesn't fit your situation.
$value = html_entity_decode( $value, ENT_QUOTES, $charset );
$attributes[ $attr['name'] ] = $value;
}
}
}
$tag = array(
'tag_name' => $match['tag'][0],
'offset' => $match[0][1],
'contents' => ! empty( $match['contents'] ) ? $match['contents'][0] : '', // empty for self-closing tags.
'attributes' => $attributes,
);
if ( $return_the_entire_tag ) {
$tag['full_tag'] = $match[0][0];
}
$tags[] = $tag;
}
return $tags;
}
}
new Visual_Portfolio_Get();