format( $number ); } else { if ( $number % 100 >= 11 && $number % 100 <= 13 ) { $ordinal = $number . 'th'; } else { switch ( $number % 10 ) { case 1: $ordinal = $number . 'st'; break; case 2: $ordinal = $number . 'nd'; break; case 3: $ordinal = $number . 'rd'; break; default: $ordinal = $number . 'th'; break; } } return $ordinal; } } /** * Remove element from multi-dimensional array * * @param array $items The multi-dimensional array. * @param string $key The key of the element. * @param string $value The value of the element. * @return array */ function edac_remove_element_with_value( $items, $key, $value ) { foreach ( $items as $sub_key => $sub_array ) { if ( $sub_array[ $key ] === $value ) { unset( $items[ $sub_key ] ); } } return $items; } /** * Filter a multi-dimensional array * * @param array $items The multi-dimensional array. * @param string $index The index of the element. * @param string $value The element value to match. * @return array */ function edac_filter_by_value( $items, $index, $value ) { if ( is_array( $items ) && count( $items ) > 0 ) { foreach ( array_keys( $items ) as $key ) { $temp[ $key ] = $items[ $key ][ $index ]; if ( $temp[ $key ] === $value ) { $newarray[ $key ] = $items[ $key ]; } } } if ( isset( $newarray ) && is_array( $newarray ) && count( $newarray ) ) { return array_values( $newarray ); } return []; } /** * Get days plugin has been active * * @return int */ function edac_days_active() { $activation_date = get_option( 'edac_activation_date' ); if ( $activation_date ) { $diff = strtotime( $activation_date ) - strtotime( gmdate( 'Y-m-d H:i:s' ) ); return abs( round( $diff / 86400 ) ); } return 0; } /** * Custom Post Types * * Returns only non-built-in post types that are publicly viewable, meaning * both public and publicly_queryable are true. Post types that redirect * frontend requests (publicly_queryable = false) are excluded so they do * not appear as scan targets in settings. * * @return array */ function edac_custom_post_types() { $args = [ 'public' => true, 'publicly_queryable' => true, '_builtin' => false, ]; $output = 'names'; // names or objects, note names is the default. $operator = 'and'; // Options 'and' or 'or'. return get_post_types( $args, $output, $operator ); } /** * Available Post Types * * @return array */ function edac_post_types() { /** * Filter the post types that the plugin will check. * * @since 1.4.0 * * @param array $post_types post types. */ $post_types = apply_filters( 'edac_filter_post_types', [ 'post', 'page' ] ); if ( ! is_array( $post_types ) ) { $post_types = [ $post_types ]; } // remove duplicates. $post_types = array_unique( $post_types ); // validate post types. foreach ( $post_types as $key => $post_type ) { if ( ! post_type_exists( $post_type ) ) { unset( $post_types[ $key ] ); } } return $post_types; } /** * Retrieve a human readable post type label. * * @param string $post_type Post type slug. * @return string */ function edac_get_post_type_label( string $post_type ): string { $post_type = sanitize_key( (string) $post_type ); if ( '' === $post_type ) { return ''; } $post_type_object = get_post_type_object( $post_type ); if ( $post_type_object instanceof \WP_Post_Type && ! empty( $post_type_object->labels->name ) ) { return $post_type_object->labels->name; } return ucfirst( $post_type ); } /** * This function validates a table name against WordPress naming conventions and checks its existence in the database. * * The function first checks if the provided table name only contains alphanumeric characters, underscores, or hyphens. * If not, it returns null. * * After that, it checks if a table with that name actually exists in the database using the SHOW TABLES LIKE query. * If the table doesn't exist, it also returns null. * * If both checks are passed, it returns the valid table name. * * @param string $table_name The name of the table to be validated. * * @return string|null The validated table name, or null if the table name is invalid or the table does not exist. */ function edac_get_valid_table_name( $table_name ) { global $wpdb; static $found_table_name; if ( isset( $found_table_name ) ) { return $found_table_name; } // Check if table name only contains alphanumeric characters, underscores, or hyphens. if ( ! preg_match( '/^[a-zA-Z0-9_\-]+$/', $table_name ) ) { // Invalid table name. return null; } // Verify that the table actually exists in the database. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) !== $table_name ) { // Table does not exist. return null; } $found_table_name = $table_name; return $found_table_name; } /** * Upcoming meetups in json format * * @param string $meetup meetup name. * @param integer $count number of meetups to return. * @return array */ function edac_get_upcoming_meetups_json( $meetup, $count = 5 ) { if ( empty( $meetup ) || ! is_string( $meetup ) ) { return []; } // Min of 1 and max of 25. $count = absint( max( 1, min( 25, $count ) ) ); // Sanitize meetup name for both cache key and GraphQL query to prevent injection. $sanitized_meetup = sanitize_title( $meetup ); $key = '_upcoming_meetups__' . $sanitized_meetup . '__' . (int) $count; $stale_key = $key . '__stale'; $cached_value = get_transient( $key ); if ( false !== $cached_value ) { return is_array( $cached_value ) ? $cached_value : []; } $output = []; $request_uri = 'https://api.meetup.com/gql-ext'; $query = ' query Group { groupByUrlname(urlname: "' . $sanitized_meetup . '") { events(first: ' . (int) $count . ') { totalCount edges { node { dateTime eventUrl id title } } } } }'; $request = wp_remote_post( $request_uri, [ 'headers' => [ 'Content-Type' => 'application/json', ], 'timeout' => 10, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout -- Timeout set for external request. 'body' => wp_json_encode( [ 'query' => $query, ] ), ] ); if ( ! is_wp_error( $request ) && 200 === (int) wp_remote_retrieve_response_code( $request ) ) { $response_body = json_decode( wp_remote_retrieve_body( $request ) ); $edges = null; if ( is_object( $response_body ) && isset( $response_body->data->groupByUrlname->events->edges ) ) { $edges = $response_body->data->groupByUrlname->events->edges; } if ( is_array( $edges ) ) { foreach ( $edges as $edge ) { if ( ! isset( $edge->node ) ) { continue; } $event = $edge->node; // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL response uses camelCase. if ( empty( $event->title ) || empty( $event->dateTime ) || empty( $event->eventUrl ) || empty( $event->id ) ) { continue; } $timestamp = strtotime( (string) $event->dateTime ); if ( false === $timestamp ) { continue; } $event_data = new stdClass(); $event_data->name = (string) $event->title; $event_data->time = $timestamp * 1000; // Convert to milliseconds to match old format. $event_data->link = (string) $event->eventUrl; $event_data->id = (string) $event->id; // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase. $output[] = $event_data; } } } if ( ! empty( $output ) ) { set_transient( $key, $output, DAY_IN_SECONDS ); update_option( $stale_key, $output, false ); return $output; } $stale_value = get_option( $stale_key ); if ( is_array( $stale_value ) && ! empty( $stale_value ) ) { // Serve stale data for a short window while retrying upstream requests periodically. set_transient( $key, $stale_value, HOUR_IN_SECONDS ); return $stale_value; } return []; } /** * Upcoming meetups in html * * @param string $meetup meetup name. * @param integer $count number of meetups to return. * @param string $heading heading level. * @return string */ function edac_get_upcoming_meetups_html( $meetup, $count = 5, $heading = '3' ) { $json = edac_get_upcoming_meetups_json( $meetup, $count ); if ( empty( $json ) ) { return ''; } $html = ''; return $html; } /** * Calculate the issue density * * @param int $issue_count number of issues. * @param int $element_count number of elements. * @param int $content_length length of content. * @return int */ function edac_get_issue_density( $issue_count, $element_count, $content_length ) { if ( $element_count < 1 || $content_length < 1 ) { return 0; } $element_weight = .8; $content_weight = .2; $error_elements_percentage = $issue_count / $element_count; $error_content_percentage = $issue_count / $content_length; $score = ( ( $error_elements_percentage * $element_weight ) + ( $error_content_percentage * $content_weight ) ); return round( $score * 100, 2 ); } /** * Get simplified summary * * @param integer $post Post ID. * @return void */ function edac_get_simplified_summary( $post = null ) { if ( null === $post ) { $post = get_the_ID(); } if ( null === $post ) { return; } echo wp_kses_post( ( new \EDAC\Inc\Simplified_Summary() )->simplified_summary_markup( $post ) ); } /** * Get Post Count by available custom post types * * @return mixed */ function edac_get_posts_count() { $output = []; $post_types = Settings::get_scannable_post_types(); if ( $post_types ) { foreach ( $post_types as $post_type ) { $counts = wp_count_posts( $post_type ); if ( $counts ) { foreach ( $counts as $key => $value ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual if ( 0 == $value ) { unset( $counts->{$key} ); } } } if ( $counts ) { $array = []; foreach ( $counts as $key => $value ) { $array[] = $key . ' = ' . $value; } if ( $array ) { $output[] = $post_type . ': ' . implode( ', ', $array ); } } } } if ( $output ) { return implode( ', ', $output ); } return false; } /** * Get Raw Global Error Count * * @return array */ function edac_get_error_count() { global $wpdb; // Define a unique cache key for our data. $cache_key = 'edac_errors_' . get_current_blog_id(); $stored_errors = wp_cache_get( $cache_key ); // Check if the result exists in the cache. if ( false === $stored_errors ) { // If not, perform the database query. $table_name = $wpdb->prefix . 'accessibility_checker'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $stored_errors = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT count(*) FROM %i WHERE siteid = %d AND ruletype = %s', $table_name, get_current_blog_id(), 'error' ) ); // Save the result in the cache for future use. wp_cache_set( $cache_key, $stored_errors ); } return $stored_errors; } /** * Get Raw Global Warning Count * * @return array Array of. */ function edac_get_warning_count() { global $wpdb; // Define a unique cache key for our data. $cache_key = 'edac_warnings_' . get_current_blog_id(); $stored_warnings = wp_cache_get( $cache_key ); // Check if the result exists in the cache. if ( false === $stored_warnings ) { // If not, perform the database query. $table_name = $wpdb->prefix . 'accessibility_checker'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $stored_warnings = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT count(*) FROM %i WHERE siteid = %d AND ruletype = %s', $table_name, get_current_blog_id(), 'warning' ) ); // Save the result in the cache for future use. wp_cache_set( $cache_key, $stored_warnings ); } return $stored_warnings; } /** * Get Database Table Count * * @param string $table Database table. * @return int */ function edac_database_table_count( $table ) { global $wpdb; // Create a unique cache key based on the table's name. $cache_key = 'edac_table_count_' . $table; // Try to get the count from the cache first. $count = wp_cache_get( $cache_key ); if ( false === $count ) { // If the count is not in the cache, perform the database query. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $count = $wpdb->get_var( $wpdb->prepare( 'SELECT count(*) FROM %i', $wpdb->prefix . $table ) ); // Save the count to the cache for future use. wp_cache_set( $cache_key, $count ); } return $count; } /** * Generate a summary statistic list item. * * @since 1.14.0 * * @param string $item_class The base CSS class for the list item. * @param int $count The count of items to display. * @param string $label The translated label with count included. * @param string $icon_name Icon name passed to edac_icon(). Default 'error'. * * @return string The generated HTML list item. */ function edac_generate_summary_stat( string $item_class, int $count, string $label, string $icon_name = 'error' ): string { $has_error_class = ( $count > 0 ) ? ' has-errors' : ''; return '
  • ' . $count . ' ' . $label . ' ' . edac_icon( $icon_name ) . '
  • '; } /** * Generate links to pro page with some params. * * @param array $query_args A list of key value pairs to add as query vars to the link. * @param string $type The type of link to generate. Default is 'pro'. * @param array $args Additional arguments to pass on the link. * @return string */ function edac_generate_link_type( $query_args = [], $type = 'pro', $args = [] ): string { if ( ! is_array( $query_args ) ) { $query_args = []; } if ( ! is_array( $args ) ) { $args = []; } $date_now = new DateTime( gmdate( 'Y-m-d H:i:s' ) ); $activation_date = new DateTime( get_option( 'edac_activation_date', gmdate( 'Y-m-d H:i:s' ) ) ); $interval = $date_now->diff( $activation_date ); $days_active = $interval->days; $query_defaults = [ 'utm_source' => 'accessibility-checker', 'utm_medium' => 'software', 'utm_campaign' => 'wordpress-general', 'php_version' => PHP_VERSION, 'platform' => 'wordpress', 'platform_version' => $GLOBALS['wp_version'], 'software' => edac_is_pro() ? 'pro' : 'free', 'software_version' => defined( 'EDACP_VERSION' ) ? EDACP_VERSION : EDAC_VERSION, 'days_active' => $days_active, ]; // Add the ref parameter if one is set via filter. $ref = apply_filters( 'edac_filter_generate_link_type_ref', '' ); if ( ! empty( $ref ) && is_string( $ref ) ) { $query_args['ref'] = $ref; } $query_args = array_merge( $query_defaults, $query_args ); switch ( $type ) { case 'help': $base_link = trailingslashit( 'https://a11ychecker.com/help' . ( $args['help_id'] ?? '' ) ); break; case 'custom': // phpcs:ignore -- intentially only breaking inside the condition because if it's not set we want to hit default. if ( ! empty( $args['base_link'] ) ) { $base_link = $args['base_link']; break; } case 'pro': default: $base_link = 'https://equalizedigital.com/accessibility-checker/pricing/'; break; } return add_query_arg( $query_args, $base_link ); } /** * Echo or return a link with some utms. * * This is just a simplified wrapper around `edac_generate_link_type` to generate a link with UTM parameters. * * @param string $base_url the base URL to which UTM parameters will be added. * @param string $campaign the UTM campaign name, optional. * @param string $content the UTM content name, optional. * @param bool $directly_echo whether to echo the link or return it. Default is true. * * @return void|string */ function edac_link_wrapper( $base_url, $campaign = '', $content = '', $directly_echo = true ) { if ( empty( $base_url ) || ! is_string( $base_url ) ) { return; } $params = []; if ( ! empty( $campaign ) ) { $params['utm_campaign'] = $campaign; } if ( ! empty( $content ) ) { $params['utm_content'] = $content; } $link = edac_generate_link_type( $params, 'custom', [ 'base_link' => $base_url ] ); if ( ! $directly_echo ) { return $link; } echo esc_url( $link ); } /** * Check if WooCommerce is enabled. * * This just checks for existence of the main WooCommerce function and class. * * @return bool */ function edac_is_woocommerce_enabled() { return function_exists( 'WC' ) && class_exists( 'WooCommerce' ); } /** * Check if a given post id is the WooCommerce checkout page. * * @param int $post_id The post ID to check. * @return bool */ function edac_check_if_post_id_is_woocommerce_checkout_page( $post_id ) { if ( ! edac_is_woocommerce_enabled() ) { return false; } return wc_get_page_id( 'checkout' ) === $post_id; } /** * Parse HTML content to extract image or SVG elements * * @param string $html The HTML content to parse. * @return array Array containing 'img' (string) and 'svg' (string) keys. */ function edac_parse_html_for_media( $html ) { if ( empty( $html ) ) { return [ 'img' => null, 'svg' => null, ]; } // Decode HTML entities before processing. $decoded_html = html_entity_decode( $html, ENT_QUOTES | ENT_HTML5 ); // Early return if no media tags found. if ( stripos( $decoded_html, ' null, 'svg' => null, ]; } // More specific img tag regex pattern. if ( preg_match( '/]+src=([\'"])(.*?)\1[^>]*>/i', $decoded_html, $matches ) ) { return [ 'img' => $matches[2] ?? null, 'svg' => null, ]; } // SVG pattern remains the same. if ( preg_match( '/]*>.*?<\/svg>/is', $decoded_html, $matches ) ) { return [ 'img' => null, 'svg' => $matches[0], ]; } return [ 'img' => null, 'svg' => null, ]; } /** * Remove corrected posts * * @param int $post_ID The ID of the post. * @param string $type The type of the post. * @param int $pre The flag indicating the removal stage (1 for before validation php based rules, 2 for after validation). * @param string $ruleset The type of the ruleset to correct (php or js). For backwards compatibility, defaults to 'php'. * * @return void */ function edac_remove_corrected_posts( $post_ID, $type, $pre = 1, $ruleset = 'php' ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $ruleset is for backwards compatibility. global $wpdb; $rules = edac_register_rules(); if ( 0 === count( $rules ) ) { return; } $sql = 1 === $pre ? "UPDATE {$wpdb->prefix}accessibility_checker SET recordcheck = %d WHERE siteid = %d AND postid = %d AND type = %s" : "DELETE FROM {$wpdb->prefix}accessibility_checker WHERE recordcheck = %d AND siteid = %d AND postid = %d AND type = %s"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Using direct query for adding data to database, caching not required for one time operation. $wpdb->query( $wpdb->prepare( $sql, // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared 0, get_current_blog_id(), $post_ID, $type ) ); } /** * Generate a landmark link with proper URL and ARIA label * * @param string $landmark The landmark type (e.g., "header", "navigation", "main"). * @param string $landmark_selector The CSS selector for the landmark. * @param int $post_id The post ID to link to. * @param string $css_class Optional CSS class for the link. Default 'edac-details-rule-records-record-landmark-link'. * @param bool $target_blank Whether to open link in new window. Default true. * * @return string The HTML for the landmark link or just the landmark text if no selector. */ function edac_generate_landmark_link( $landmark, $landmark_selector, $post_id, $css_class = 'edac-details-rule-records-record-landmark-link', $target_blank = true ) { if ( empty( $landmark ) ) { return ''; } $landmark = ucwords( $landmark ); $landmark = esc_html( $landmark ); // If we have both landmark and selector, create a link. if ( ! empty( $landmark_selector ) ) { $link = apply_filters( 'edac_get_origin_url_for_virtual_page', get_the_permalink( $post_id ), $post_id ); $landmark_url = add_query_arg( [ 'edac_landmark' => base64_encode( $landmark_selector ), 'edac_nonce' => wp_create_nonce( 'edac_highlight' ), ], $link ); // translators: %s is the landmark type (e.g., "Header", "Navigation", "Main"). $landmark_aria_label = sprintf( __( 'View %s landmark on website, opens a new window', 'accessibility-checker' ), $landmark ); $target_attr = $target_blank ? ' target="_blank"' : ''; return sprintf( '%s', esc_url( $landmark_url ), esc_attr( $css_class ), $target_attr, esc_attr( $landmark_aria_label ), $landmark ); } // If we only have landmark text, return it formatted. return $landmark; } /** * Check if a post is a virtual page. * * This function checks if a post is a virtual page using the pro plugin's * VirtualItemType:POST_TYPE constant. * * @param int $post_id The post ID to check. * @return bool True if the post is a virtual page, false otherwise. */ function edac_is_virtual_page( $post_id ) { if ( class_exists( '\EqualizeDigital\AccessibilityCheckerPro\VirtualContent\PostType\VirtualItemType' ) ) { $post_type = get_post_type( $post_id ); $pro_post_type = \EqualizeDigital\AccessibilityCheckerPro\VirtualContent\PostType\VirtualItemType::POST_TYPE; return $pro_post_type === $post_type; } return false; } /** * Check if the Pro version of the plugin is active. * * @return bool True if Pro version is active, false otherwise. */ function edac_is_pro() { return defined( 'EDACP_VERSION' ) && defined( 'EDAC_KEY_VALID' ) && EDAC_KEY_VALID; } /** * Get current timestamp in UTC for database storage. * * @since 1.35.0 * @return string MySQL datetime string in UTC. */ function edac_get_current_utc_datetime(): string { return gmdate( 'Y-m-d H:i:s' ); } /** * Format a UTC datetime string for display in WordPress timezone. * * Uses the WordPress configured date and time format settings. * * @since 1.35.0 * @param string $utc_datetime MySQL datetime string in UTC format. * @return string Formatted datetime string in WordPress timezone, or empty string if invalid. */ function edac_format_datetime_from_utc( string $utc_datetime ): string { if ( ! $utc_datetime || '0000-00-00 00:00:00' === $utc_datetime ) { return ''; } $timestamp = strtotime( $utc_datetime . ' UTC' ); if ( false === $timestamp ) { return ''; } $date_format = get_option( 'date_format' ); $time_format = get_option( 'time_format' ); $format = $date_format . ' ' . $time_format; return wp_date( $format, $timestamp ); } /** * Determine the icon name to display for the readability panel. * * PHP equivalent of the JS getPanelIcon() helper used in the readability sidebar panel. * * Logic: * - No content or grade is 0 → 'warning' * - Reading level is passing (below 9th grade) → 'check' * - Reading level is failing: * - No simplified summary → 'warning' * - Summary exists and grade is valid (> 0 and not failed) → 'check' * - Summary grade failed → 'warning' * - Default → 'warning' * * @since 1.38.0 * * @param bool $has_content Whether the post has enough content to calculate a grade. * @param int $post_grade Flesch-Kincaid grade level of the post content. 0 means not calculable. * @param bool $post_grade_failed True when the post grade is above 9th grade (reading level not 'below'). * @param string $summary_text The simplified summary text, if any. * @param int $summary_grade Flesch-Kincaid grade level of the simplified summary. * @param bool $summary_grade_failed True when the simplified summary grade is above 9th grade. * @return string Icon name: 'check' or 'warning'. */ function edac_get_readability_panel_icon( bool $has_content, int $post_grade, bool $post_grade_failed, string $summary_text, int $summary_grade, bool $summary_grade_failed ): string { if ( ! $has_content || 0 === $post_grade ) { return 'warning'; } if ( ! $post_grade_failed ) { return 'check'; } // Reading level is above 9th grade – evaluate the simplified summary. if ( ! $summary_text ) { return 'warning'; } if ( $summary_grade > 0 && ! $summary_grade_failed ) { return 'check'; } if ( $summary_grade_failed ) { return 'warning'; } return 'warning'; } /** * Render an icon as an HTML string. * * PHP equivalent of the Icon JS component. Returns a wrapping the * requested SVG with the appropriate BEM classes and ARIA attributes. * * Supported icon names: check, warning, error, info. * Supported types (controls color via CSS): success, warning, error, info. * When $type is omitted the type is derived automatically from $name. * * @since 1.38.0 * * @param string $name Icon name: 'check', 'warning', 'error', or 'info'. Default 'check'. * @param string $type Optional. BEM modifier type for color: 'success', 'warning', 'error', 'info'. * When empty, derived automatically from $name. * @param bool $aria_hidden Whether to add aria-hidden="true". Automatically set to false when * $aria_label is provided. Default true. * @param string $aria_label Accessible label. When provided, aria-hidden is set to false and * aria-label is added to the wrapper span. * @param string $css_class Additional CSS classes to append to the wrapper span. * @return string The rendered HTML, or an empty string for an unknown icon name. */ function edac_icon( string $name = 'check', string $type = '', bool $aria_hidden = true, string $aria_label = '', string $css_class = '' ): string { $svgs = [ 'check' => '' . '' . '' . '', 'warning' => '' . '' . '', 'error' => '' . '' . '', 'info' => '' . '' . '', 'dismissed' => '' . '' . '', ]; if ( ! isset( $svgs[ $name ] ) ) { return ''; } // Auto-derive type from name when not explicitly provided. if ( '' === $type ) { $type_map = [ 'check' => 'success', 'warning' => 'warning', 'error' => 'error', 'info' => 'info', 'dismissed' => 'dismissed', ]; $type = $type_map[ $name ] ?? ''; } // Build CSS class string. $classes = array_filter( [ 'edac-icon', $type ? 'edac-icon--' . $type : '', $css_class ] ); $class_attr = implode( ' ', $classes ); // Build ARIA attributes. // When an aria-label is supplied the icon must be announced, so aria-hidden is forced off. $resolved_aria_hidden = $aria_label ? false : $aria_hidden; $aria_attrs = 'aria-hidden="' . ( $resolved_aria_hidden ? 'true' : 'false' ) . '"'; if ( $aria_label ) { $aria_attrs .= ' aria-label="' . esc_attr( $aria_label ) . '"'; } return '' . $svgs[ $name ] . ''; }