*/ public static function parse_options($raw): array { $out = []; foreach (preg_split('/\r?\n/', is_scalar($raw) ? (string) $raw : '') ?: [] as $line) { $line = trim($line); if ($line === '') { continue; } $bar = strpos($line, '|'); if ($bar !== false) { $value = trim(substr($line, 0, $bar)); $label = trim(substr($line, $bar + 1)); $out[] = ['value' => $value, 'label' => $label !== '' ? $label : $value]; } else { $out[] = ['value' => $line, 'label' => $line]; } } return $out; } /** * @param mixed $raw repeater value * @return array> */ public static function normalize($raw): array { $out = []; $used = []; $str = static fn($v): string => is_scalar($v) ? (string) $v : ''; foreach (is_array($raw) ? array_values($raw) : [] as $i => $item) { if (!is_array($item)) { continue; } $type = in_array($item['type'] ?? '', self::TYPES, true) ? (string) $item['type'] : 'text'; $id = self::sanitize_id($item['custom_id'] ?? ''); if ($id === '') { $id = self::sanitize_id($item['_id'] ?? ''); } if ($id === '') { $id = 'field_' . ($i + 1); } if (isset($used[$id])) { $n = 2; while (isset($used[$id . '_' . $n])) { $n++; } $id .= '_' . $n; } $used[$id] = true; $max_size = isset($item['max_size']) && is_numeric($item['max_size']) && (float) $item['max_size'] > 0 ? (float) $item['max_size'] : 5.0; $rows = isset($item['rows']) && is_numeric($item['rows']) && (int) $item['rows'] > 0 ? min(30, (int) $item['rows']) : 4; $out[] = [ 'id' => $id, 'type' => $type, 'label' => $str($item['label'] ?? ''), 'placeholder' => $str($item['placeholder'] ?? ''), 'required' => !empty($item['required']) && !in_array($type, ['html', 'step', 'hidden'], true), 'width' => in_array((string) ($item['width'] ?? ''), self::WIDTHS, true) ? (string) $item['width'] : '100', 'options' => self::parse_options($item['options'] ?? ''), 'default' => $str($item['default'] ?? ''), 'min' => $str($item['min'] ?? ''), 'max' => $str($item['max'] ?? ''), 'step' => $str($item['step'] ?? ''), 'accept' => $str($item['accept'] ?? ''), 'max_size' => $max_size, 'multiple' => !empty($item['multiple']), 'rows' => $rows, 'html' => $str($item['html'] ?? ''), 'acceptance_text' => $str($item['acceptance_text'] ?? ''), ]; } return $out; } /** * Groups fields into steps: every `step` item starts one (its label is the title); fields placed before * the first step item form an untitled first step. Without step items there is a single step. * * @param array> $fields * @return array>}> */ public static function steps(array $fields): array { $steps = []; $open = false; foreach ($fields as $field) { if ($field['type'] === 'step') { $steps[] = ['index' => count($steps), 'title' => (string) $field['label'], 'fields' => []]; $open = true; continue; } if (!$open) { $steps[] = ['index' => count($steps), 'title' => '', 'fields' => []]; $open = true; } $steps[count($steps) - 1]['fields'][] = $field; } if (!$steps) { $steps[] = ['index' => 0, 'title' => '', 'fields' => []]; } return $steps; } /** * Fields that carry a value (no html / step), keyed by id. * * @param array> $fields * @return array> */ public static function inputs(array $fields): array { $out = []; foreach ($fields as $field) { if (!in_array($field['type'], self::NO_VALUE, true)) { $out[(string) $field['id']] = $field; } } return $out; } }