|null */ private $current_renderers = null; /** * Constructor. * * @param Locations|null $locations Location rules. */ public function __construct( ?Locations $locations = null ) { $this->locations = $locations ? $locations : new Locations(); } /** * Renderers by field type: built-in ones, and the ones other plugins add or replace. * * @return array */ public function renderers(): array { $defaults = Renderers::defaults(); /** * Renderers of field types: field type => callable( array $field, array $args ): string. * Add one to cover a field type of another plugin, or replace one. See docs/hooks.md for $args. * * @param array $renderers Renderers. */ $filtered = apply_filters( 'awfc_field_renderers', $defaults ); if ( ! is_array( $filtered ) ) { return $defaults; } return array_filter( $filtered, static function ( $renderer, $type ) { return is_string( $type ) && is_callable( $renderer ); }, ARRAY_FILTER_USE_BOTH ); } /** * Code of one field. * * @param array $field Field. * @param array $args Access to the value: var, value, post_id, container, generator (see Renderers). Other keys * (from the renderer of another plugin) reach the renderer unchanged. * @return string|null The code; '' for a field type that holds no value (Message, Tab...); null for a field type not covered. */ public function field_code( array $field, array $args ): ?string { $type = isset( $field['type'] ) && is_string( $field['type'] ) ? $field['type'] : ''; $renderers = null !== $this->current_renderers ? $this->current_renderers : $this->renderers(); if ( '' === $type || ! isset( $renderers[ $type ] ) ) { return null; } $args['generator'] = $this; $code = call_user_func( $renderers[ $type ], $field, $args ); return is_string( $code ) ? $code : null; } /** * Code of a field of a field group, with its header and footer. * * @param array $field Field. * @param string $post_id PHP expression of the post ID passed to get_field(), or '' for the current post. * @param array $group Field group. * @return string|null See field_code(). */ public function snippet( array $field, string $post_id = '', array $group = array() ): ?string { $name = isset( $field['name'] ) && is_scalar( $field['name'] ) ? (string) $field['name'] : ''; $args = array( 'var' => '$' . Code::name( $name ), 'value' => Code::get_field( $name, $post_id ), 'post_id' => $post_id, // The array the value is read from: none at the top of a group, where the value comes from get_field(). 'container' => '', ); $this->current_renderers = $this->renderers(); try { $code = $this->field_code( $field, $args ); } finally { $this->current_renderers = null; } if ( null === $code || '' === $code ) { return $code; } $context = array( 'post_id' => $post_id, 'group' => $group, ); /** * Code placed before the code of each field (a comment, a type annotation...). * * @param string $header Header ('' by default). * @param array $field Field. * @param array $context post_id (PHP expression) and group (field group). */ $header = apply_filters( 'awfc_snippet_header', '', $field, $context ); /** * Code placed after the code of each field. * * @param string $footer Footer ('' by default). * @param array $field Field. * @param array $context post_id (PHP expression) and group (field group). */ $footer = apply_filters( 'awfc_snippet_footer', '', $field, $context ); $parts = array( is_string( $header ) ? $header : '', $code, is_string( $footer ) ? $footer : '' ); return implode( "\n", array_filter( $parts, 'strlen' ) ); } /** * Code of every field of a field group. * * @param array $group Field group. * @param array $fields Its fields, in order. * @return array{post_id:string,uncovered_locations:string[],uncovered_rules:string[],fields:array} */ public function group_code( array $group, array $fields ): array { $location = $this->locations->read( $group ); $result = array(); foreach ( $fields as $field ) { if ( ! is_array( $field ) ) { continue; } $code = $this->snippet( $field, $location['post_id'], $group ); if ( '' === $code ) { continue; } $result[] = array( 'key' => isset( $field['key'] ) && is_scalar( $field['key'] ) ? (string) $field['key'] : '', 'name' => isset( $field['name'] ) && is_scalar( $field['name'] ) ? (string) $field['name'] : '', 'label' => isset( $field['label'] ) && is_scalar( $field['label'] ) ? (string) $field['label'] : '', 'type' => isset( $field['type'] ) && is_scalar( $field['type'] ) ? (string) $field['type'] : '', 'code' => $code, ); } return array( 'post_id' => $location['post_id'], 'uncovered_locations' => $location['uncovered'], 'uncovered_rules' => $location['uncovered_rules'], 'fields' => $result, ); } }