*/ public static function defaults(): array { $types = array( 'text' => 'text', 'textarea' => 'textarea', 'number' => 'number', 'range' => 'number', 'email' => 'email', 'url' => 'url', 'password' => 'password', 'image' => 'image', 'file' => 'file', 'wysiwyg' => 'wysiwyg', 'oembed' => 'oembed', 'select' => 'choice', 'checkbox' => 'choice', 'radio' => 'choice', 'button_group' => 'choice', 'true_false' => 'true_false', 'link' => 'link', 'post_object' => 'post_object', 'page_link' => 'page_link', 'relationship' => 'post_object', 'taxonomy' => 'taxonomy', 'user' => 'user', 'google_map' => 'google_map', 'date_picker' => 'text', 'date_time_picker' => 'text', 'time_picker' => 'text', 'color_picker' => 'color_picker', 'icon_picker' => 'icon_picker', 'group' => 'group', // Field types that hold no value: no code. 'message' => 'nothing', 'tab' => 'nothing', 'accordion' => 'nothing', 'separator' => 'nothing', 'output' => 'nothing', ); $renderers = array(); foreach ( $types as $type => $method ) { $renderers[ $type ] = array( self::class, $method ); } return $renderers; } /** * Message, Tab, Accordion, Separator: they hold no value, so they have no code. * * @return string */ public static function nothing(): string { return ''; } /** * Text, and the date and time pickers (their value is already formatted with the field's Return Format). * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function text( array $field, array $args ): string { $v = $args['var']; $note = array(); if ( in_array( $field['type'] ?? '', array( 'date_picker', 'date_time_picker', 'time_picker' ), true ) ) { $format = Code::comment( (string) ( $field['return_format'] ?? '' ) ); if ( '' !== $format ) { $note[] = ''; } } return self::guarded( $args, self::filled( $v ), array_merge( $note, array( '

' ) ) ); } /** * Text Area: HTML when New Lines adds paragraphs or line breaks, plain text otherwise. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function textarea( array $field, array $args ): string { $v = $args['var']; if ( in_array( $field['new_lines'] ?? '', array( 'wpautop', 'br' ), true ) ) { if ( 'wpautop' === $field['new_lines'] ) { // The value holds its paragraphs. return self::guarded( $args, self::filled( $v ), array( '
', "\t', '
' ) ); } return self::guarded( $args, self::filled( $v ), array( '

' ) ); } return self::guarded( $args, self::filled( $v ), array( '

' ) ); } /** * Number and Range: 0 is a value. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function number( array $field, array $args ): string { $v = $args['var']; return self::guarded( $args, 'is_numeric( ' . $v . ' )', array( '

' ) ); } /** * Email. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function email( array $field, array $args ): string { $v = $args['var']; return self::guarded( $args, $v, array( '' ) ); } /** * URL. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function url( array $field, array $args ): string { $v = $args['var']; return self::guarded( $args, $v, array( self::link_tag( $v, $v ) ) ); } /** * Password: never printed. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function password( array $field, array $args ): string { return implode( "\n", array( '', ) ); } /** * Image: Array, URL or ID. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function image( array $field, array $args ): string { $v = $args['var']; switch ( $field['return_format'] ?? 'array' ) { case 'url': $line = ''; break; case 'id': $line = ""; break; default: $line = '"; } return self::guarded( $args, $v, array( $line ) ); } /** * File: Array, URL or ID. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function file( array $field, array $args ): string { $v = $args['var']; switch ( $field['return_format'] ?? 'array' ) { case 'url': $line = self::link_tag( $v, 'wp_basename( ' . $v . ' )' ); break; case 'id': $line = self::link_tag( 'wp_get_attachment_url( ' . $v . ' )', 'get_the_title( ' . $v . ' )' ); break; default: $line = self::link_tag( $v . "['url']", $v . "['title']" ); } return self::guarded( $args, $v, array( $line ) ); } /** * Wysiwyg Editor: HTML. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function wysiwyg( array $field, array $args ): string { $v = $args['var']; return self::guarded( $args, $v, array( '
', "\t', '
' ) ); } /** * OEmbed: the embed HTML of an approved provider; the iframe is kept, anything else is stripped. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function oembed( array $field, array $args ): string { $v = $args['var']; $attributes = array( 'src', 'title', 'width', 'height', 'frameborder', 'allow', 'allowfullscreen', 'referrerpolicy', 'loading' ); $lines = array( '
', "\t array(", ); $width = max( array_map( 'strlen', $attributes ) ) + 2; foreach ( $attributes as $attribute ) { $lines[] = "\t\t\t\t" . str_pad( "'" . $attribute . "'", $width ) . ' => true,'; } $lines = array_merge( $lines, array( "\t\t\t),", "\t\t)", "\t);", "\t?>", '
' ) ); return self::guarded( $args, $v, $lines ); } /** * Select, Checkbox, Radio Button and Button Group: Value, Label or Array; one value or a list. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function choice( array $field, array $args ): string { $v = $args['var']; $format = $field['return_format'] ?? 'value'; $type = $field['type'] ?? ''; $list = 'checkbox' === $type || ( 'select' === $type && ! empty( $field['multiple'] ) ); $item = $list ? Code::suffix( $v, 'item' ) : $v; $text = 'array' === $format ? $item . "['label']" : $item; $line = ''; if ( $list ) { return self::guarded( $args, $v, self::list_of( $v, $item, array( '
  • ' . $line . '
  • ' ) ) ); } // A choice can be "0": only an empty value is skipped (an Array is never empty when set). return self::guarded( $args, 'array' === $format ? $v : self::filled( $v ), array( '

    ' . $line . '

    ' ) ); } /** * True / False. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function true_false( array $field, array $args ): string { $v = $args['var']; return implode( "\n", array( '', '', "\t

    Yes

    ", '', "\t

    No

    ", '', ) ); } /** * Link: Array or URL. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function link( array $field, array $args ): string { $v = $args['var']; if ( 'url' === ( $field['return_format'] ?? 'array' ) ) { return self::guarded( $args, $v, array( self::link_tag( $v, $v ) ) ); } return self::guarded( $args, $v, array( '\" target=\"\">", ) ); } /** * Post Object (one or several) and Relationship (always several): Post Object or Post ID. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function post_object( array $field, array $args ): string { $v = $args['var']; $list = 'relationship' === ( $field['type'] ?? '' ) || ! empty( $field['multiple'] ); $item = $list ? Code::suffix( $v, 'item' ) : $v; $id = 'id' === ( $field['return_format'] ?? 'object' ); $note = ''; $anchor = self::link_tag( 'get_permalink( ' . $item . ' )', 'get_the_title( ' . $item . ' )' ); if ( $list ) { return self::guarded( $args, $v, array_merge( array( $note ), self::list_of( $v, $item, array( '
  • ' . $anchor . '
  • ' ) ) ) ); } return self::guarded( $args, $v, array( $note, $anchor ) ); } /** * Page Link: a URL, or a list of URLs. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function page_link( array $field, array $args ): string { $v = $args['var']; if ( ! empty( $field['multiple'] ) ) { $item = Code::suffix( $v, 'item' ); return self::guarded( $args, $v, self::list_of( $v, $item, array( '
  • ' . self::link_tag( $item, $item ) . '
  • ' ) ) ); } return self::guarded( $args, $v, array( self::link_tag( $v, $v ) ) ); } /** * Taxonomy: Checkbox and Multi Select give a list, Radio Buttons and Select one term; Term Object or Term ID. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function taxonomy( array $field, array $args ): string { $v = $args['var']; $list = in_array( $field['field_type'] ?? 'checkbox', array( 'checkbox', 'multi_select' ), true ); $object = 'object' === ( $field['return_format'] ?? 'id' ); $item = $list ? Code::suffix( $v, 'item' ) : $v; if ( $object ) { $body = array( 'name ); ?>' ); } else { $term = Code::suffix( $v, 'term' ); $body = array( '', '', "\tname ); ?>', '', ); } if ( $list ) { return self::guarded( $args, $v, self::list_of( $v, $item, self::wrap( 'li', $body ) ) ); } return self::guarded( $args, $v, self::wrap( 'p', $body ) ); } /** * User: one or several; User Array, User Object or User ID. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function user( array $field, array $args ): string { $v = $args['var']; $list = ! empty( $field['multiple'] ); $item = $list ? Code::suffix( $v, 'item' ) : $v; switch ( $field['return_format'] ?? 'array' ) { case 'object': $body = array( 'display_name ); ?>' ); break; case 'id': $user = Code::suffix( $v, 'user' ); $body = array( '', '', "\tdisplay_name ); ?>', '', ); break; default: $body = array( '" ); } if ( $list ) { return self::guarded( $args, $v, self::list_of( $v, $item, self::wrap( 'li', $body ) ) ); } return self::guarded( $args, $v, self::wrap( 'p', $body ) ); } /** * Google Map: the address and the coordinates. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function google_map( array $field, array $args ): string { $v = $args['var']; return self::guarded( $args, $v, array( '

    ", '', '
    \" data-lng=\"\">
    ", ) ); } /** * Color Picker: Hex String (or RGBA string with transparency) or RGBA Array. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function color_picker( array $field, array $args ): string { $v = $args['var']; if ( 'array' === ( $field['return_format'] ?? 'string' ) ) { $css = Code::suffix( $v, 'css' ); return implode( "\n", array( '', '', ' 0 ) : ?>", "\t", "\t" . '', '', ) ); } return self::guarded( $args, $v, array( '' ) ); } /** * Icon Picker: String or Array, with a branch for each tab the field offers (Dashicons, Media Library, URL). * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function icon_picker( array $field, array $args ): string { $v = $args['var']; $tabs = isset( $field['tabs'] ) && is_array( $field['tabs'] ) ? array_values( array_intersect( array( 'dashicons', 'media_library', 'url' ), $field['tabs'] ) ) : array(); if ( ! $tabs ) { $tabs = array( 'dashicons', 'media_library', 'url' ); } $dashicons = in_array( 'dashicons', $tabs, true ); $images = array_diff( $tabs, array( 'dashicons' ) ); $notes = array(); if ( $dashicons ) { $notes[] = ''; } $span = ''; if ( 'array' !== ( $field['return_format'] ?? 'string' ) ) { // A dashicon class, or the URL of an image; an empty value comes back as the default (an array). $body = array(); if ( $dashicons && $images ) { $body = array( '", "\t" . sprintf( $span, $v ), '', "\t" . '', '', ); } elseif ( $dashicons ) { $body = array( sprintf( $span, $v ) ); } else { $body = array( '' ); } return self::guarded( $args, 'is_string( ' . $v . ' ) && \'\' !== ' . $v, array_merge( $notes, $body ) ); } $value = $v . "['value']"; $branches = array(); if ( $dashicons ) { $branches['dashicons'] = array( sprintf( $span, $value ) ); } if ( in_array( 'media_library', $tabs, true ) ) { $branches['media_library'] = array( '" ); } if ( in_array( 'url', $tabs, true ) ) { $branches['url'] = array( '' ); } $body = array(); $first = true; foreach ( $branches as $type => $lines ) { if ( 1 === count( $branches ) ) { $body = $lines; break; } $body[] = '"; foreach ( $lines as $line ) { $body[] = "\t" . $line; } $first = false; } if ( count( $branches ) > 1 ) { $body[] = ''; } return self::guarded( $args, '! empty( ' . $value . ' )', array_merge( $notes, $body ) ); } /** * Group: its sub-fields, read from the group's value. * * @param array $field Field. * @param array $args Access to the value. * @return string */ public static function group( array $field, array $args ): string { $v = $args['var']; $parts = array(); $subs = isset( $field['sub_fields'] ) && is_array( $field['sub_fields'] ) ? $field['sub_fields'] : array(); foreach ( $subs as $sub ) { if ( ! is_array( $sub ) ) { continue; } $sub_args = array( 'var' => Code::suffix( $v, Code::name( (string) ( $sub['name'] ?? '' ), false ) ), 'value' => $v . '[' . Code::string( (string) ( $sub['name'] ?? '' ) ) . '] ?? null', 'post_id' => $args['post_id'], // The array the sub-field is read from. The keys of the group's own $args stay with the group. 'container' => $v, 'generator' => $args['generator'], ); $code = $args['generator']->field_code( $sub, $sub_args ); if ( null === $code ) { $parts[] = ''; } elseif ( '' !== $code ) { $parts[] = $code; } } if ( ! $parts ) { $parts[] = ''; } return self::guarded( $args, $v, explode( "\n", implode( "\n", $parts ) ) ); } /** * Reads the value into its variable, then prints the body only when the condition holds. * * @param array $args Access to the value. * @param string $condition PHP condition. * @param string[] $body Lines. * @return string */ private static function guarded( array $args, string $condition, array $body ): string { $lines = array( '', '', ); foreach ( $body as $line ) { $lines[] = '' === $line ? '' : "\t" . $line; } $lines[] = ''; return implode( "\n", $lines ); } /** * Condition of a value printed as text: "0" is a value, an empty value is not. * * @param string $v Variable. * @return string */ private static function filled( string $v ): string { return 'is_scalar( ' . $v . " ) && '' !== (string) " . $v; } /** * A