*/
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(
'',
'',
"\tYes
",
'',
"\tNo
",
'',
)
);
}
/**
* 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 with one item per entry of a list.
*
* @param string $items Variable of the list.
* @param string $item Variable of each entry.
* @param string[] $body Lines of each item.
* @return string[]
*/
private static function list_of( string $items, string $item, array $body ): array {
$lines = array( '', "\t' );
foreach ( $body as $line ) {
$lines[] = "\t\t" . $line;
}
return array_merge( $lines, array( "\t", '
' ) );
}
/**
* Wraps lines in an element: on one line when there is one line, around them otherwise.
*
* @param string $tag Element.
* @param string[] $lines Lines.
* @return string[]
*/
private static function wrap( string $tag, array $lines ): array {
if ( 1 === count( $lines ) ) {
return array( '<' . $tag . '>' . $lines[0] . '' . $tag . '>' );
}
$wrapped = array( '<' . $tag . '>' );
foreach ( $lines as $line ) {
$wrapped[] = "\t" . $line;
}
$wrapped[] = '' . $tag . '>';
return $wrapped;
}
/**
* A link whose address and text are PHP expressions.
*
* @param string $url PHP expression of the address.
* @param string $text PHP expression of the text.
* @return string
*/
private static function link_tag( string $url, string $text ): string {
return '';
}
}