/**
  * Analyze a template, expanding Field-level Markup Substitution Parameters
  *
  * Field-level parameters must have one of the following prefix values:
  * template, request, query, custom, terms, meta, iptc, exif, xmp, pdf.
  * All but request and query require an attachment ID.
  *
  * @since 1.50
  *
  * @param	string	A formatting string containing [+placeholders+]
  * @param	array	Optional: an array of values from the query, if any, e.g. shortcode parameters
  * @param	array	Optional: an array of values to add to the returned array
  * @param	integer	Optional: attachment ID for attachment-specific placeholders
  * @param	boolean	Optional: for option 'multi', retain existing values
  * @param	string	Optional: default option value
  *
  * @return	array	( parameter => value ) for all field-level parameters and anything in $markup_values
  */
 public static function mla_expand_field_level_parameters($tpl, $query = NULL, $markup_values = array(), $post_id = 0, $keep_existing = false, $default_option = 'text')
 {
     static $cached_post_id = 0, $item_metadata = NULL, $attachment_metadata = NULL, $id3_metadata = NULL;
     if ($cached_post_id != $post_id) {
         $item_metadata = NULL;
         $attachment_metadata = NULL;
         $id3_metadata = NULL;
         $cached_post_id = $post_id;
     }
     $placeholders = self::mla_get_template_placeholders($tpl, $default_option);
     $template_count = 0;
     foreach ($placeholders as $key => $value) {
         if (isset($markup_values[$key])) {
             continue;
         }
         switch ($value['prefix']) {
             case 'template':
                 $markup_values = self::mla_expand_field_level_parameters($value['value'], $query, $markup_values, $post_id, $keep_existing, $default_option);
                 $template_count++;
                 break;
             case 'meta':
                 if (is_null($item_metadata)) {
                     if (0 < $post_id) {
                         $item_metadata = get_metadata('post', $post_id, '_wp_attachment_metadata', true);
                     } else {
                         break;
                     }
                 }
                 $markup_values[$key] = self::mla_find_array_element($value['value'], $item_metadata, $value['option']);
                 break;
             case 'query':
                 if (isset($query) && isset($query[$value['value']])) {
                     $markup_values[$key] = $query[$value['value']];
                 } else {
                     $markup_values[$key] = '';
                 }
                 break;
             case 'request':
                 if (isset($_REQUEST[$value['value']])) {
                     $record = $_REQUEST[$value['value']];
                 } else {
                     $record = '';
                 }
                 if (is_scalar($record)) {
                     $text = sanitize_text_field((string) $record);
                 } elseif (is_array($record)) {
                     if ('export' == $value['option']) {
                         $text = sanitize_text_field(var_export($record, true));
                     } else {
                         $text = '';
                         foreach ($record as $term) {
                             $term_name = sanitize_text_field($term);
                             $text .= strlen($text) ? ',' . $term_name : $term_name;
                         }
                     }
                 }
                 // is_array
                 $markup_values[$key] = $text;
                 break;
             case 'terms':
                 if (0 < $post_id) {
                     $terms = get_object_term_cache($post_id, $value['value']);
                     if (false === $terms) {
                         $terms = wp_get_object_terms($post_id, $value['value']);
                         wp_cache_add($post_id, $terms, $value['value'] . '_relationships');
                     }
                 } else {
                     break;
                 }
                 $text = '';
                 if (is_wp_error($terms)) {
                     $text = implode(',', $terms->get_error_messages());
                 } elseif (!empty($terms)) {
                     if ('single' == $value['option'] || 1 == count($terms)) {
                         reset($terms);
                         $term = current($terms);
                         $text = sanitize_term_field('name', $term->name, $term->term_id, $value['value'], 'display');
                     } elseif ('export' == $value['option']) {
                         $text = sanitize_text_field(var_export($terms, true));
                     } else {
                         foreach ($terms as $term) {
                             $term_name = sanitize_term_field('name', $term->name, $term->term_id, $value['value'], 'display');
                             $text .= strlen($text) ? ', ' . $term_name : $term_name;
                         }
                     }
                 }
                 $markup_values[$key] = $text;
                 break;
             case 'custom':
                 if (0 < $post_id) {
                     $record = get_metadata('post', $post_id, $value['value'], 'single' == $value['option']);
                     if (empty($record) && 'ALL_CUSTOM' == $value['value']) {
                         $meta_values = self::mla_fetch_attachment_metadata($post_id);
                         $clean_data = array();
                         foreach ($meta_values as $meta_key => $meta_value) {
                             if (0 !== strpos($meta_key, 'mla_item_')) {
                                 continue;
                             }
                             $meta_key = substr($meta_key, 9);
                             if (is_array($meta_value)) {
                                 $clean_data[$meta_key] = '(ARRAY)';
                             } elseif (is_string($meta_value)) {
                                 $clean_data[$meta_key] = self::_bin_to_utf8(substr($meta_value, 0, 256));
                             } else {
                                 $clean_data[$meta_key] = $meta_value;
                             }
                         }
                         // foreach value
                         /*
                          * Convert the array to text, strip the outer "array( ... ,)" literal,
                          * the interior linefeed/space/space separators and backslashes.
                          */
                         $record = var_export($clean_data, true);
                         $record = substr($record, 7, strlen($record) - 10);
                         $record = str_replace(chr(0xa) . '  ', ' ', $record);
                         $record = str_replace('\\', '', $record);
                     }
                     // ALL_CUSTOM
                 } else {
                     break;
                 }
                 $text = '';
                 if (is_wp_error($record)) {
                     $text = implode(',', $record->get_error_messages());
                 } elseif (!empty($record)) {
                     if (is_scalar($record)) {
                         $text = 'raw' == $value['format'] ? (string) $record : sanitize_text_field((string) $record);
                     } elseif (is_array($record)) {
                         if ('export' == $value['option']) {
                             $text = 'raw' == $value['format'] ? var_export($record, true) : sanitize_text_field(var_export($record, true));
                         } else {
                             $text = '';
                             foreach ($record as $term) {
                                 $term_name = 'raw' == $value['format'] ? $term : sanitize_text_field($term);
                                 $text .= strlen($text) ? ', ' . $term_name : $term_name;
                             }
                         }
                     }
                     // is_array
                 }
                 // ! empty
                 $markup_values[$key] = $text;
                 break;
             case 'iptc':
                 if (is_null($attachment_metadata)) {
                     if (0 < $post_id) {
                         $attachment_metadata = self::mla_fetch_attachment_image_metadata($post_id);
                     } else {
                         break;
                     }
                 }
                 $markup_values[$key] = self::mla_iptc_metadata_value($value['value'], $attachment_metadata, $value['option'], $keep_existing);
                 break;
             case 'exif':
                 if (is_null($attachment_metadata)) {
                     if (0 < $post_id) {
                         $attachment_metadata = self::mla_fetch_attachment_image_metadata($post_id);
                     } else {
                         break;
                     }
                 }
                 $record = self::mla_exif_metadata_value($value['value'], $attachment_metadata, $value['option'], $keep_existing);
                 if (is_array($record)) {
                     $markup_values[$key] = self::_process_field_level_array($record, $value['option'], $keep_existing);
                 } else {
                     $markup_values[$key] = $record;
                 }
                 break;
             case 'xmp':
                 if (is_null($attachment_metadata)) {
                     if (0 < $post_id) {
                         $attachment_metadata = self::mla_fetch_attachment_image_metadata($post_id);
                     } else {
                         break;
                     }
                 }
                 $markup_values[$key] = self::mla_xmp_metadata_value($value['value'], $attachment_metadata['mla_xmp_metadata'], $value['option'], $keep_existing);
                 break;
             case 'id3':
                 if (is_null($id3_metadata)) {
                     if (0 < $post_id) {
                         $id3_metadata = self::mla_fetch_attachment_id3_metadata($post_id);
                     } else {
                         break;
                     }
                 }
                 $markup_values[$key] = self::mla_id3_metadata_value($value['value'], $id3_metadata, $value['option'], $keep_existing);
                 break;
             case 'pdf':
                 if (is_null($attachment_metadata)) {
                     if (0 < $post_id) {
                         $attachment_metadata = self::mla_fetch_attachment_image_metadata($post_id);
                     } else {
                         break;
                     }
                 }
                 $record = self::mla_pdf_metadata_value($value['value'], $attachment_metadata);
                 if (is_array($record)) {
                     $markup_values[$key] = self::_process_field_level_array($record, $value['option'], $keep_existing);
                 } else {
                     $markup_values[$key] = $record;
                 }
                 break;
             case '':
                 $candidate = str_replace('{', '[', str_replace('}', ']', $value['value']));
                 $key = str_replace('{', '[', str_replace('}', ']', $key));
                 if (MLAOptions::mla_is_data_source($candidate)) {
                     $data_value = array('data_source' => $candidate, 'keep_existing' => false, 'format' => 'raw', 'option' => $value['option']);
                     // single, export, text for array values, e.g., alt_text
                     $markup_values[$key] = MLAOptions::mla_get_data_source($post_id, 'single_attachment_mapping', $data_value);
                 } elseif (isset($markup_values[$value['value']])) {
                     /*
                      * A standard element can have a format modifier, e.g., commas, attr
                      */
                     $markup_values[$key] = $markup_values[$value['value']];
                 }
                 break;
             default:
                 // ignore anything else
         }
         // switch
         if (isset($markup_values[$key])) {
             $markup_values[$key] = self::mla_apply_field_level_format($markup_values[$key], $value);
         }
         // isset( $markup_values[ $key ] )
     }
     // foreach placeholder
     if ($template_count) {
         $markup_values['[+template_count+]'] = $template_count;
     }
     return $markup_values;
 }