Added an array_merge_recursive for labels to combine $_inputDefaults
with specific view markup for labels like custom text.
Also removed null array for options existing in $_inputDefaults.

@param array $options Description
@return array
554219
protected function _parseOptions($options)
    {
        if (!empty($options['label']) && $options['label']) {
            if (!is_array($options['label'])) {
                $options['label'] = ['text' => $options['label']];
            }
            $options['label'] = array_merge_recursive($options['label'], ['class' => 'control-label']);
        }
        return parent::_parseOptions($options);
    }
303247
public function renderLabel(array $options = [])
    {
        $attrs['id'] = $this->getAttribute('labelId', $this->getId() . '-label');
        $attrs['class'] = $this->getAttribute('labelClass');
        $attrs['for'] = $this->getAttribute('for', $this->getId());
        $attrs['title'] = $this->getAttribute('description');

        $attrs = array_merge($attrs, (array) $this->getAttribute('labelAttribs'));

        if ($this->form && $this->form->getRenderer()) {
            return $this->form->getRenderer()->renderLabel($this, $attrs, $options);
        }

        $label = $this->getLabel();

        if ($this->required) {
            $label = '<span class="windwalker-input-required-hint">*</span> ' . $label;
        }

        return (string) new HtmlElement('label', $label, $attrs);
    }
1321570
public function options($options = []) {
		$options['__options'] = true;
		if (!empty($this->options_skip_i18n)) {
			$options['i18n'] = false;
		}
		// process options_map
		if (isset($options['options_map'])) {
			$options_map = $options['options_map'];
		} else if (!empty($this->options_map)) {
			$options_map = $this->options_map;
		} else {
			$options_map = [$this->column_prefix . 'name' => 'name'];
		}
		if (isset($options['options_map_addition'])) {
			$options_map = array_merge_hard($options_map, $options['options_map_addition']);
		}
		$options['options_map'] = $options_map;
		$data = $this->optionsQueryData($options);
		// if we need to filter options_active
		if (!empty($options['__options_active'])) {
			$options_active = $this->options_active ? $this->options_active : [$this->column_prefix . 'inactive' => 0];
			$data = \Object\Data\Common::filterActiveOptions($data, $options_active, $options['existing_values'] ?? [], $options['skip_values'] ?? []);
		} else {
			$data = \Object\Data\Common::filterActiveOptions($data, false, $options['existing_values'] ?? [], $options['skip_values'] ?? []);
		}
		// if we need to prepend values based on pk
		if (!empty($options['__prepend_if_key'])) {
			foreach ($options['__prepend_if_key'] as $k => $v) {
				if (!empty($data[$k])) {
					$data[$k]['__prepend_if_key'] = !empty($options['i18n']) ? i18n(null, $v) : $v;
					$options_map['__prepend_if_key'] = 'name';
				}
			}
		}
		// build options
		$options['column_prefix'] = $this->column_prefix;
		return \Object\Data\Common::buildOptions($data, $options_map, $this->orderby, $options);
	}
1250745
public function input($options = [])
    {
        if (is_string($options)) {
            $options = ['class' => $options];
        }

        $input = new Input($this->expression('input'), $options);

        return $input;
    }
1107554
public function monthAndYearInput($options = []) {
       $defaultOptions = [
		'id' => self::MONTH_YEAR_ID,
		'class' => 'form-control',
		'autocomplete' => self::AUTO_EXP_ATTR,
		'placeholder' => '•• / ••',
		'required' => true,
		'type' => 'tel',
	];
	$mergedOptions = array_merge($defaultOptions, $options);
	StripeHelper::secCheck($mergedOptions);
	$mergedOptions['data-stripe'] = self::MONTH_YEAR_ID;
	$inputs = Html::input('text', null, null, $mergedOptions);

	//Append hidden year and month inputs that will get value from mixed and send to stripe
	$inputs = $inputs . $this->monthInput(['type' => 'hidden']);
	$inputs = $inputs . $this->yearInput(['type' => 'hidden']);
	return $inputs;
    }
98897
static function formatOptions( $options, $default = false ) {
		$data = '';

		foreach( $options as $label => $value ){
			if( is_array( $value ) ) {
				$contents = self::formatOptions( $value, $default );
				$data .= self::tags( 'optgroup', array( 'label' => $label ), $contents ) . "\n";
			} else {
				$data .= self::option( $label, $value, $value === $default ) . "\n";
			}
		}

		return $data;
	}
1077917
protected function mergeOptions(array $options)
    {
        $defaults = $options + $this->defaultOptions;
        $defaults['params'] = [];
        if (!$defaults['filter_action'] || $this->application->input->{$defaults['method']}($defaults['action']) === $defaults['filter_action']) {
            //Action filter
            //cleaning post/get data
            foreach ($defaults['filters'] as $params => $column) {
                switch ($defaults['method']) {
                    case 'get':
                        if (isset($_GET[$params])) {
                            if (is_array($_GET[$params])) {
                                $_GET[$params] = array_filter(
                                    $_GET[$params],
                                    function ($value) {
                                        return (trim($value) !== '');
                                    }
                                );
                                if (empty($_GET[$params])) {
                                    unset($_GET[$params]);
                                }
                            } elseif (trim($_GET[$params]) === '') {
                                unset($_GET[$params]);
                            }
                        }
                        break;
                    case 'post':
                        if (isset($_POST[$params]) && trim($_POST[$params]) === '') {
                            if (is_array($_POST[$params])) {
                                $_POST[$params] = array_filter(
                                    $_POST[$params],
                                    function ($value) {
                                        return (trim($value) !== '');
                                    }
                                );
                                if (empty($_POST[$params])) {
                                    unset($_POST[$params]);
                                }
                            } elseif (trim($_POST[$params]) === '') {
                                unset($_POST[$params]);
                            }
                            unset($_POST[$params]);
                        }
                        break;
                }
            }
            $defaults['params'] = array_intersect_key($this->application->input->{$defaults['method']}(), $defaults['filters']);
        } elseif ($this->application->input->{$defaults['method']}($defaults['action']) === $defaults['reset_action']) {
            foreach ($defaults['filters'] as $params => $callback) {
                switch ($defaults['method']) {
                    case 'get':
                        unset($_GET[$params]);
                        break;
                    case 'post':
                        unset($_POST[$params]);
                        break;
                }
            }
            //Reset action
            $defaults['params'] = $defaults['default_filters'];
        } elseif ($defaults['save']) {
            $defaults['params'] = $this->application->session->userdata($options['save']);
        }

        // Si aucun filtre n'a été soumis (session non initialisé, filtre par défaut
        $session_exist = false;
        if ($defaults['save']) {
            $session_exist =  $this->application->session->has_userdata($options['save']);
        }
        if (empty($defaults['params']) && !$session_exist) {
             $defaults['params'] = $defaults['default_filters'];
        }
        return $defaults;
    }
702158
private function addDefaultOptions(array $options)
    {
        $defaultOptions = [];
        // hack to prevent validation error "The CSRF token is invalid."
        foreach ($this->formRegistry->getExtensions() as $extension) {
            foreach ($extension->getTypeExtensions(FormType::class) as $typeExtension) {
                if ($typeExtension instanceof FormTypeCsrfExtension) {
                    $defaultOptions['csrf_protection'] = false;
                }
            }
        }

        return array_replace(
            $defaultOptions,
            $options
        );
    }
1101851
public function options($options = [])
    {
        if (empty($options)) {
            return $this->options;
        }

        $this->options = array_merge($this->options, $options);
    }
216105
public function numberInput($options = []) {
	$defaultOptions = [
		'id' => self::NUMBER_ID,
		'class' => 'form-control',
		'autocomplete' => self::AUTO_CC_ATTR,
		'placeholder' => '•••• •••• •••• ••••',
		'required' => true,
		'type' => 'tel',
		'size' => 20
	];
	$mergedOptions = array_merge($defaultOptions, $options);
	StripeHelper::secCheck($mergedOptions);
	$mergedOptions['data-stripe'] = self::NUMBER_ID;
	return Html::input('text', null, null, $mergedOptions);
    }
388997
public function cvcInput($options = []) {
	$defaultOptions = [
		'id' => self::CVC_ID,
		'class' => 'form-control',
		'autocomplete' => 'off',
		'placeholder' => '•••',
		'required' => true,
		'type' => 'tel',
		'size' => 4
	];
	$mergedOptions = array_merge($defaultOptions, $options);
	StripeHelper::secCheck($mergedOptions);
	$mergedOptions['data-stripe'] = self::CVC_ID;
	return Html::input('text', null, null, $mergedOptions);
    }
23630
protected function parseOptions(array $options): array
    {
        $this->setTransformer(new StringToBooleanTransformer());

        $options = parent::parseOptions($options);

        // Set some default CSS classes if chosen theme is "bootstrap".
        if (isset($options['theme']) && 'bootstrap' === $options['theme']) {
            $options['attributes']['class'] = isset($options['attributes']['class']) ?
                'form-check-input '.$options['attributes']['class'] : 'form-check-input';
            $options['label_attr']['class'] = isset($options['label_attr']['class']) ?
                'form-check-label '.$options['label_attr']['class'] : 'form-check-label';
        }

        return $options;
    }
809317
private function getNodeChildren(array $options)
    {
        /** @noinspection PhpUnusedParameterInspection */
        $options = (new OptionsResolver)->setDefaults([
            'depth'           => 1,
            'refName'         => null,
            'parent'          => null,
            'lang'            => $this->currentLocale->getCurrentLocale(),
            'include_root'    => false,
            'include_hidden'  => false,
            'include_offline' => false,
            'flatten'         => false,
            'limit'           => 0,
        ])
            ->setNormalizer('refName', function ($options, $value) {
                return $value ? (array)$value : [];
            })
            ->setAllowedTypes('parent', ['null', Node::class])
            ->setAllowedTypes('depth', 'integer')
            ->setAllowedTypes('limit', 'integer')
            ->setAllowedTypes('include_root', 'bool')
            ->setAllowedTypes('include_hidden', 'bool')
            ->setAllowedTypes('flatten', 'bool')
            ->resolve($options);

        $qb = $this->nodeRepository
            ->createQueryBuilder('node')
            ->leftJoin('node.nodeTranslations', 'nt', Join::WITH, 'nt.node = node and nt.lang = :lang')
            ->leftJoin('nt.publicNodeVersion', 'nv')
            ->leftJoin('node.parent', 'parent')
            ->select('parent.id as parentId', 'node.id', 'nt.title', 'nt.url', 'nt.lang', 'nv.refId', 'nv.refEntityName', 'node.internalName')
            ->where('nt.lang = :lang')
            ->andWhere('node.deleted = 0')
            ->orderBy('node.lvl, nt.weight')
            ->setParameter('lang', $options['lang']);

        if ($options['limit']) {
            $qb->setMaxResults($options['limit']);
        }

        if (false === $options['include_hidden']) {
            $qb->andWhere('node.hiddenFromNav = 0');
        }

        if (false === $options['include_offline']) {
            $qb->andWhere('nt.online = 1');
        }

        if ($options['refName']) {
            $qb
                ->andWhere('node.refEntityName in (:refName)')
                ->setParameter('refName', $options['refName']);
        }

        $level = 0;
        $nodeId = null;
        if ($options['parent']) {
            /** @var Node $parent */
            $parent = $options['parent'];
            $nodeId = $parent->getId();
            $level = $parent->getLevel();


            $qb->andWhere('node.lft >= :minLeft')
                ->andWhere('node.rgt <= :maxRight')
                ->setParameter('minLeft', $parent->getLeft())
                ->setParameter('maxRight', $parent->getRight());

            if (false === $options['include_root']) {
                $qb->andWhere('node.id != :nodeId')
                    ->setParameter('nodeId', $nodeId);
            }
        }

        if ($options['depth']) {
            $qb->andWhere('node.lvl <= :maxLevel')
                ->setParameter('maxLevel', $level + $options['depth']);
        }

        $children = $qb->getQuery()->getResult();

        $flatten = $options['flatten'];

        /** @noinspection PhpInternalEntityUsedInspection */
        return array_reduce($children, function (TreeBuilder $treeBuilder, $item) use ($nodeId, $flatten) {
            $branch = new Branch($item['title'], $item['id'], $item['url'], $item['lang'], $item['refId'], $item['refEntityName'], $item['internalName']);

            if ($branch->getNodeId() === $nodeId) {
                $item['parentId'] = null;
            } elseif ($flatten) {
                $item['parentId'] = $nodeId;
            }

            return $treeBuilder->add($item['parentId'], $branch);
        }, new TreeBuilder)->getRoot();

    }
206161
public function generate(array $options = [])
    {
        $defaults = [
            'pattern'   => null,
            'extension' => null,
            'index'     => null
        ];
        $options = array_merge($defaults, $options);

        $ext = (string)$options['extension'];
        $pattern = self::normalizePattern($options['pattern']);
        $index = (int)$options['index'];

        $nameComponents = [];
        if ($pattern) {
            $nameComponents[] = $pattern;
        }
        if ($index || (! $pattern)) {
            $nameComponents[] = $index;
        }
        return implode('_', $nameComponents) . ($ext ? '.' . $ext : '');
    }
392839
protected function parseOptions(array $options)
    {
        $options = array_merge($this->defaultOptions, $options);

        foreach ($options as $option => $value) {
            if (! in_array($option, $this->allowedOptions, true)) {
                throw new \InvalidArgumentException("Option [$option] is not allowed.");
            }
        }

        return $options;
    }
984123
protected function setDefaultOptions(array $options = [])
    {
        $this->options = $this->formHelper->mergeOptions($this->allDefaults(), $this->getDefaults());
        $this->options = $this->prepareOptions($options);
    }
619308
public function setOptions( $options ) {
		$this->fields = [];

		// Rebuild the checkboxes
		$this->clearContent();
		$name = $this->name;
		foreach ( $options as $opt ) {
			$optValue = parent::cleanUpValue( $opt['data'] );
			$optDisabled = $opt['disabled'] ?? false;
			$field = new FieldLayout(
				new CheckboxInputWidget( [
					'name' => $name,
					'value' => $optValue,
					'disabled' => $this->isDisabled() || $optDisabled,
				] ),
				[
					'label' => $opt['label'] ?? $optValue,
					'align' => 'inline',
				]
			);

			$this->fields[ $optValue ] = $field;
			$this->appendContent( $field );
		}

		// Re-set the value, checking the checkboxes as needed.
		// This will also get rid of any stale options that we just removed.
		$this->setValue( $this->getValue() );

		return $this;
	}