$fields Fields of the form. * @param array $values Submitted values keyed by field key. * * @return array States keyed by field key. */ public function resolve( array $fields, array $values ): array { $states = array(); foreach ( $fields as $field ) { if ( $field instanceof Field ) { $states[ $field->field_key() ] = new FieldState( true, true, $field->is_required() ); } } for ( $pass = 0; $pass < self::MAX_PASSES; $pass++ ) { $next = $this->pass( $fields, $values, $states ); if ( $this->same( $states, $next ) ) { break; } $states = $next; } /** * Filters the resolved state of every field. * * @since 1.0.0 * * @param array $states States keyed by field key. * @param array $fields Fields of the form. * @param array $values Submitted values keyed by field key. */ return (array) apply_filters( 'formpilot_field_states', $states, $fields, $values ); } /** * Runs one settling pass. * * Values belonging to fields that are currently inactive are hidden from the * comparison, so a condition keyed to a hidden field cannot hold. * * @param array $fields Fields of the form. * @param array $values Submitted values keyed by field key. * @param array $states States from the previous pass. * * @return array The new states. */ private function pass( array $fields, array $values, array $states ): array { $visible_values = $this->active_values( $values, $states ); $next = array(); foreach ( $fields as $field ) { if ( ! $field instanceof Field ) { continue; } $key = $field->field_key(); $state = new FieldState( true, true, $field->is_required() ); $rule = LogicRule::from_array( $field->setting( 'logic', array() ) ); if ( ! $rule->is_active() ) { $next[ $key ] = $state; continue; } $holds = $rule->holds( $visible_values ); $next[ $key ] = match ( $rule->action() ) { LogicRule::SHOW => $state->with_visible( $holds ), LogicRule::HIDE => $state->with_visible( ! $holds ), LogicRule::REQUIRE_FIELD => $state->with_required( $holds ? true : $field->is_required() ), LogicRule::ENABLE => $state->with_enabled( $holds ), LogicRule::DISABLE => $state->with_enabled( ! $holds ), default => $state, }; } return $next; } /** * Returns the values of fields that are currently taking part. * * @param array $values Submitted values keyed by field key. * @param array $states Current states keyed by field key. * * @return array The values still in play. */ private function active_values( array $values, array $states ): array { $active = array(); foreach ( $values as $key => $value ) { $state = $states[ $key ] ?? null; if ( ! $state instanceof FieldState || $state->is_active() ) { $active[ $key ] = $value; } } return $active; } /** * Determines whether two sets of states are identical. * * @param array $before States before a pass. * @param array $after States after a pass. * * @return bool True when nothing changed. */ private function same( array $before, array $after ): bool { if ( count( $before ) !== count( $after ) ) { return false; } foreach ( $before as $key => $state ) { $other = $after[ $key ] ?? null; if ( ! $other instanceof FieldState ) { return false; } if ( $state->is_visible() !== $other->is_visible() || $state->is_enabled() !== $other->is_enabled() || $state->is_required() !== $other->is_required() ) { return false; } } return true; } }