Elementy formularza

Przegląd standardowych elementów formularza.

addText (string $name, $label=null, ?int $cols=null, ?int $maxLength=null): TextInput

Dodaje jednoliniowe pole tekstowe (klasa TextInput). Jeśli użytkownik pola nie wypełni, zwraca pusty ciąg '', albo użyj setNullable(), żeby zwracał zamiast tego null.

$form->addText('name', 'Imię:')
	->setRequired()
	->setNullable();

Automatycznie waliduje UTF-8, przycina białe znaki z lewej i prawej strony i usuwa złamania linii, które mógłby wysłać atakujący.

Maksymalną długość można ograniczyć metodą setMaxLength(). Metoda addFilter() pozwala zmodyfikować wartość wpisaną przez użytkownika.

Metodą setHtmlType() możesz zmienić wizualny charakter pola tekstowego na typy takie jak search, tel czy url zgodnie ze specyfikacją. Pamiętaj, że zmiana typu jest czysto wizualna i nie zastępuje funkcji walidacyjnej. Dla typu url warto dodać konkretną regułę walidacyjną URL.

Dla innych typów inputów, jak number, range, email, date, datetime-local, time czy color, użyj wyspecjalizowanych metod addInteger(), addFloat(), addEmail(), addDate(), addTime(), addDateTime()addColor(), które zapewniają walidację po stronie serwera. Typy month i week nie są jeszcze w pełni obsługiwane przez wszystkie przeglądarki.

Elementowi można ustawić “pustą wartość”. Działa ona trochę jak wartość domyślna, ale jeśli użytkownik jej nie zmieni, element zwraca pusty ciąg albo null.

$form->addText('phone', 'Telefon:')
	->setHtmlType('tel')
	->setEmptyValue('+48');

addTextArea (string $name, $label=null): TextArea

Dodaje wieloliniowe pole tekstowe (klasa TextArea). Jeśli użytkownik pola nie wypełni, zwraca pusty ciąg '', albo użyj setNullable(), żeby zwracał zamiast tego null.

$form->addTextArea('note', 'Notatka:')
	->addRule($form::MaxLength, 'Twoja notatka jest zdecydowanie za długa', 10000);

Automatycznie waliduje UTF-8 i normalizuje końce linii do \n. W przeciwieństwie do jednoliniowego pola tekstowego nie odbywa się tu przycinanie białych znaków.

Maksymalną długość można ograniczyć metodą setMaxLength(). Metoda addFilter() pozwala zmodyfikować wartość wpisaną przez użytkownika. Pustą wartość można ustawić metodą setEmptyValue().

addInteger (string $name, $label=null): TextInput

Dodaje pole do wpisywania liczby całkowitej (klasa TextInput). Zwraca albo liczbę całkowitą, albo null, jeśli użytkownik nic nie wpisze.

$form->addInteger('year', 'Rok:')
	->addRule($form::Range, 'Rok musi mieścić się między %d a %d.', [1900, 2023]);

Element renderuje się jako <input type="number">. Metodą setHtmlType() możesz zmienić typ na range dla wyświetlenia w postaci suwaka albo na text, jeśli wolisz standardowe pole tekstowe bez specjalnego zachowania typu number.

addFloat (string $name, $label=null): TextInput

Dodaje pole do wpisywania liczby zmiennoprzecinkowej (klasa TextInput). Zwraca albo liczbę zmiennoprzecinkową, albo null, jeśli użytkownik nic nie wpisze.

$form->addFloat('level', 'Poziom:')
	->setDefaultValue(0)
	->addRule($form::Range, 'Poziom musi mieścić się między %d a %d.', [0, 100]);

Element renderuje się jako <input type="number">. Metodą setHtmlType() możesz zmienić typ na range dla wyświetlenia w postaci suwaka albo na text, jeśli wolisz standardowe pole tekstowe bez specjalnego zachowania typu number.

Nette i przeglądarka Chrome akceptują jako separator dziesiętny zarówno przecinek, jak i kropkę. Żeby ta funkcjonalność działała także w Firefoksie, zaleca się ustawienie atrybutu lang albo dla konkretnego elementu, albo dla całej strony, na przykład <html lang="pl">.

addEmail (string $name, $label=null, int $maxLength=255): TextInput

Dodaje pole do wpisywania adresu e-mail (klasa TextInput). Jeśli użytkownik pola nie wypełni, zwraca pusty ciąg '', albo użyj setNullable(), żeby zwracał zamiast tego null.

$form->addEmail('email', 'E-mail:');

Waliduje, czy wartość jest poprawnym adresem e-mail. Nie sprawdza, czy domena faktycznie istnieje, weryfikowana jest tylko składnia. Automatycznie waliduje UTF-8 i przycina białe znaki z lewej i prawej strony.

Maksymalną długość można ograniczyć metodą setMaxLength(). Metoda addFilter() pozwala zmodyfikować wartość wpisaną przez użytkownika. Pustą wartość można ustawić metodą setEmptyValue().

addPassword (string $name, $label=null, ?int $cols=null, ?int $maxLength=null): TextInput

Dodaje pole do wpisywania hasła (klasa TextInput).

$form->addPassword('password', 'Hasło:')
	->setRequired()
	->addRule($form::MinLength, 'Hasło musi mieć co najmniej %d znaków', 8)
	->addRule($form::Pattern, 'Hasło musi zawierać cyfrę', '.*[0-9].*');

Przy ponownym wyświetleniu formularza pole będzie puste. Automatycznie waliduje UTF-8, przycina białe znaki z lewej i prawej strony i usuwa złamania linii, które mógłby wysłać atakujący.

addCheckbox (string $name, $caption=null): Checkbox

Dodaje checkbox (klasa Checkbox). Zwraca true albo false, zależnie od tego, czy jest zaznaczony.

$form->addCheckbox('agree', 'Zgadzam się z regulaminem')
	->setRequired('Musisz zaakceptować nasz regulamin');

addCheckboxList (string $name, $label=null, ?array $items=null): CheckboxList

Dodaje listę checkboxów do wyboru wielu pozycji (klasa CheckboxList). Zwraca tablicę kluczy wybranych pozycji. Metoda getSelectedItems() zwraca wybrane pozycje jako pary klucz-wartość.

$form->addCheckboxList('colors', 'Kolory:', [
	'r' => 'czerwony',
	'g' => 'zielony',
	'b' => 'niebieski',
]);

Tablicę oferowanych pozycji przekaż jako trzeci parametr albo metodą setItems(). Przekazując false jako drugi argument setItems(), wartości zostaną użyte także jako klucze.

Metodą setDisabled(['r', 'g']) wyłączysz poszczególne pozycje.

Element automatycznie sprawdza, czy nie doszło do podrobienia i czy wybrane pozycje rzeczywiście są wśród oferowanych i nie były wyłączone. Metodą getRawValue() można pobrać wysłane pozycje bez tej ważnej kontroli.

Przy ustawianiu domyślnie wybranych pozycji również sprawdza, czy są wśród oferowanych, w przeciwnym razie rzuca wyjątek. Tę kontrolę można wyłączyć metodą checkDefaultValue(false).

Jeśli wysyłasz formularz metodą GET, możesz wybrać bardziej zwięzły sposób przesyłania danych, który oszczędza rozmiar query stringu. Aktywujesz go, ustawiając formularzowi atrybut HTML:

$form->setHtmlAttribute('data-nette-compact');

addRadioList (string $name, $label=null, ?array $items=null): RadioList

Dodaje radio buttony (klasa RadioList). Zwraca klucz wybranej pozycji albo null, jeśli użytkownik nic nie wybrał. Metoda getSelectedItem() zwraca zamiast klucza wartość.

$sex = [
	'm' => 'mężczyzna',
	'f' => 'kobieta',
	'o' => 'inna',
];
$form->addRadioList('gender', 'Płeć:', $sex);

Tablicę oferowanych pozycji przekaż jako trzeci parametr albo metodą setItems().

Metodą setDisabled(['m']) wyłączysz poszczególne pozycje.

Element automatycznie sprawdza, czy nie doszło do podrobienia i czy wybrana pozycja rzeczywiście jest jedną z oferowanych i nie była wyłączona. Metodą getRawValue() można pobrać wysłaną pozycję bez tej ważnej kontroli.

Przy ustawianiu domyślnie wybranej pozycji również sprawdza, czy jest jedną z oferowanych, w przeciwnym razie rzuca wyjątek. Tę kontrolę można wyłączyć metodą checkDefaultValue(false).

addSelect (string $name, $label=null, ?array $items=null, ?int $size=null): SelectBox

Dodaje selectbox (klasa SelectBox). Zwraca klucz wybranej pozycji albo null, jeśli użytkownik nic nie wybrał. Metoda getSelectedItem() zwraca zamiast klucza wartość.

$countries = [
	'CZ' => 'Czechy',
	'SK' => 'Słowacja',
	'GB' => 'Wielka Brytania',
];

$form->addSelect('country', 'Kraj:', $countries)
	->setDefaultValue('SK');

Tablicę oferowanych pozycji przekaż jako trzeci parametr albo metodą setItems(). Pozycje mogą być też tablicą dwuwymiarową (reprezentującą optgroupy):

$countries = [
	'Europe' => [
		'CZ' => 'Czechy',
		'SK' => 'Słowacja',
		'GB' => 'Wielka Brytania',
	],
	'CA' => 'Kanada',
	'US' => 'USA',
	'?'  => 'inny',
];

W selectboxach pierwsza pozycja często ma specjalne znaczenie, służy jako zachęta do działania. Do dodania takiej pozycji służy metoda setPrompt().

$form->addSelect('country', 'Kraj:', $countries)
	->setPrompt('Wybierz kraj');

Metodą setDisabled(['CZ', 'SK']) wyłączysz poszczególne pozycje.

Element automatycznie sprawdza, czy nie doszło do podrobienia i czy wybrana pozycja rzeczywiście jest jedną z oferowanych i nie była wyłączona. Metodą getRawValue() można pobrać wysłaną pozycję bez tej ważnej kontroli.

Przy ustawianiu domyślnie wybranej pozycji również sprawdza, czy jest jedną z oferowanych, w przeciwnym razie rzuca wyjątek. Tę kontrolę można wyłączyć metodą checkDefaultValue(false).

addMultiSelect (string $name, $label=null, ?array $items=null, ?int $size=null): MultiSelectBox

Dodaje selectbox do wyboru wielu pozycji (klasa MultiSelectBox). Zwraca tablicę kluczy wybranych pozycji. Metoda getSelectedItems() zwraca wybrane pozycje jako pary klucz-wartość.

$form->addMultiSelect('countries', 'Kraje:', $countries);

Tablicę oferowanych pozycji przekaż jako trzeci parametr albo metodą setItems(). Pozycje mogą być też tablicą dwuwymiarową.

Metodą setDisabled(['CZ', 'SK']) wyłączysz poszczególne pozycje.

Element automatycznie sprawdza, czy nie doszło do podrobienia i czy wybrane pozycje rzeczywiście są wśród oferowanych i nie były wyłączone. Metodą getRawValue() można pobrać wysłane pozycje bez tej ważnej kontroli.

Przy ustawianiu domyślnie wybranych pozycji również sprawdza, czy są wśród oferowanych, w przeciwnym razie rzuca wyjątek. Tę kontrolę można wyłączyć metodą checkDefaultValue(false).

addUpload (string $name, $label=null): UploadControl

Dodaje pole do wysyłania pliku (klasa UploadControl). Zwraca obiekt FileUpload, nawet jeśli użytkownik żadnego pliku nie wysłał, co można sprawdzić metodą FileUpload::hasFile(). Metodą setNullable() możesz sprawić, żeby element zwracał zamiast obiektu FileUpload wartość null, gdy żaden plik nie został wysłany.

$form->addUpload('avatar', 'Awatar:')
	->addRule($form::Image, 'Awatar musi być w formacie JPEG, PNG, GIF, WebP albo AVIF.')
	->addRule($form::MaxFileSize, 'Maksymalny rozmiar to 1 MB.', 1024 * 1024);

Jeśli plik nie wyśle się poprawnie, formularz nie zostanie pomyślnie wysłany i wyświetli się błąd. Czyli po udanym wysłaniu nie trzeba sprawdzać metody FileUpload::isOk().

Nigdy nie ufaj oryginalnej nazwie pliku zwracanej metodą FileUpload::getName(); klient mógł wysłać złośliwą nazwę pliku z zamiarem uszkodzenia albo zhakowania Twojej aplikacji.

Reguły MimeType i Image wykrywają wymagany typ na podstawie sygnatury pliku i nie weryfikują jego integralności. To, czy obrazek nie jest uszkodzony, można ustalić na przykład, próbując go wczytać.

addMultiUpload (string $name, $label=null): UploadControl

Dodaje pole do wysyłania wielu plików naraz (klasa UploadControl). Zwraca tablicę obiektów FileUpload. Metoda FileUpload::hasFile() dla każdego z nich zwróci true.

$form->addMultiUpload('files', 'Pliki:')
	->addRule($form::MaxLength, 'Można wysłać maksymalnie %d plików.', 10);

Jeśli któryś plik nie wyśle się poprawnie, formularz nie zostanie pomyślnie wysłany i wyświetli się błąd. Czyli po udanym wysłaniu nie trzeba sprawdzać metody FileUpload::isOk() dla każdego pliku.

Nigdy nie ufaj oryginalnym nazwom plików zwracanym metodą FileUpload::getName(); klient mógł wysłać złośliwe nazwy plików z zamiarem uszkodzenia albo zhakowania Twojej aplikacji.

Reguły MimeType i Image wykrywają wymagany typ na podstawie sygnatury pliku i nie weryfikują jego integralności. To, czy obrazek nie jest uszkodzony, można ustalić na przykład, próbując go wczytać.

addDate (string $name, $label=null): DateTimeControl

Dodaje pole pozwalające użytkownikowi wygodnie wpisać datę złożoną z roku, miesiąca i dnia (klasa DateTimeControl).

Jako wartość domyślną przyjmuje obiekty implementujące DateTimeInterface, ciąg zawierający czas albo liczbę reprezentującą uniksowy timestamp. To samo dotyczy argumentów reguł Min, Max czy Range, które definiują minimalną i maksymalną dozwoloną datę.

$form->addDate('date', 'Data:')
	->setDefaultValue(new DateTime)
	->addRule($form::Min, 'Data musi mieć co najmniej miesiąc.', new DateTime('-1 month'));

Domyślnie zwraca obiekt DateTimeImmutable. Metodą setFormat() możesz podać format tekstowy albo timestamp:

$form->addDate('date', 'Data:')
	->setFormat('Y-m-d');

addTime (string $name, $label=null, bool $withSeconds=false): DateTimeControl

Dodaje pole pozwalające użytkownikowi wygodnie wpisać czas złożony z godzin, minut i opcjonalnie sekund (klasa DateTimeControl).

Jako wartość domyślną przyjmuje obiekty implementujące DateTimeInterface, ciąg zawierający czas albo liczbę reprezentującą uniksowy timestamp. Z tych wejść wykorzystywana jest tylko informacja o czasie, data jest ignorowana. To samo dotyczy argumentów reguł Min, Max czy Range, które definiują minimalny i maksymalny dozwolony czas. Jeśli ustawiona wartość minimalna jest wyższa niż maksymalna, powstaje przedział czasowy przechodzący przez północ.

$form->addTime('time', 'Czas:', withSeconds: true)
	->addRule($form::Range, 'Czas musi mieścić się między %d a %d.', ['12:30', '13:30']);

Domyślnie zwraca obiekt DateTimeImmutable (z datą ustawioną na 1 stycznia roku 1). Metodą setFormat() możesz podać format tekstowy:

$form->addTime('time', 'Czas:')
	->setFormat('H:i');

addDateTime (string $name, $label=null, bool $withSeconds=false): DateTimeControl

Dodaje pole pozwalające użytkownikowi wygodnie wpisać datę i czas złożone z roku, miesiąca, dnia, godzin, minut i opcjonalnie sekund (klasa DateTimeControl).

Jako wartość domyślną przyjmuje obiekty implementujące DateTimeInterface, ciąg zawierający czas albo liczbę reprezentującą uniksowy timestamp. To samo dotyczy argumentów reguł Min, Max czy Range, które definiują minimalną i maksymalną dozwoloną datę i czas.

$form->addDateTime('datetime', 'Data i czas:')
	->setDefaultValue(new DateTime)
	->addRule($form::Min, 'Data musi mieć co najmniej miesiąc.', new DateTime('-1 month'));

Domyślnie zwraca obiekt DateTimeImmutable. Metodą setFormat() możesz podać format tekstowy albo timestamp:

$form->addDateTime('datetime')
	->setFormat(DateTimeControl::FormatTimestamp);

addColor (string $name, $label=null): ColorPicker

Dodaje pole wyboru koloru (klasa ColorPicker). Kolor zwracany jest jako ciąg w formacie #rrggbb. Jeśli użytkownik nic nie wybierze, zwraca czarny #000000.

$form->addColor('color', 'Kolor:')
	->setDefaultValue('#3C8ED7');

addHidden (string $name, mixed $default=null): HiddenField

Dodaje ukryte pole (klasa HiddenField).

$form->addHidden('userid');

Metodą setNullable() sprawisz, że zamiast pustego ciągu zwróci null. Metoda addFilter() pozwala zmodyfikować wysłaną wartość.

Chociaż element jest ukryty, ważne jest, żeby zdać sobie sprawę, że jego wartość i tak może zostać zmodyfikowana albo podrobiona przez atakującego. Zawsze dokładnie weryfikuj i waliduj wszystkie otrzymane wartości po stronie serwera, żeby zapobiec zagrożeniom bezpieczeństwa związanym z manipulacją danymi.

addSubmit (string $name, $caption=null): SubmitButton

Dodaje przycisk wysyłający (klasa SubmitButton).

$form->addSubmit('submit', 'Wyślij');

Handler można przekazać przyciskowi bezpośrednio jako trzeci parametr $onSubmit zamiast podpinać go do zdarzenia onClick:

$form->addSubmit('submit', 'Wyślij', function (SubmitButton $button, $data): void {
	// ...
});

W formularzu może być więcej niż jeden przycisk wysyłający:

$form->addSubmit('register', 'Zarejestruj');
$form->addSubmit('cancel', 'Anuluj');

Żeby ustalić, który z nich został kliknięty, użyj:

if ($form['register']->isSubmittedBy()) {
  // ...
}

Jeśli po naciśnięciu przycisku nie chcesz walidować całego formularza (na przykład dla przycisków Anuluj albo Podgląd), użyj setValidationScope().

addButton (string $name, $caption=null)Button

Dodaje przycisk (klasa Button), który nie ma funkcji wysyłającej. Można go więc wykorzystać do innych funkcji, np. wywołania funkcji JavaScriptowej po kliknięciu.

$form->addButton('raise', 'Podnieś pensję')
	->setHtmlAttribute('onclick', 'raiseSalary()');

addImageButton (string $name, ?string $src=null, ?string $alt=null): ImageButton

Dodaje przycisk wysyłający w postaci obrazka (klasa ImageButton).

$form->addImageButton('submit', '/path/to/image.png', 'Wyślij');

Przy użyciu wielu przycisków wysyłających możesz ustalić, który został kliknięty, za pomocą $form['submit']->isSubmittedBy().

addContainer (string|int $name): Container

Dodaje podformularz (klasa Container), czyli kontener, do którego można dodawać kolejne elementy w ten sam sposób, w jaki dodaje się je do formularza. Działają też metody takie jak setDefaults() czy getValues().

$sub1 = $form->addContainer('first');
$sub1->addText('name', 'Twoje imię:');
$sub1->addEmail('email', 'Email:');

$sub2 = $form->addContainer('second');
$sub2->addText('name', 'Twoje imię:');
$sub2->addEmail('email', 'Email:');

Wysłane dane zwracane są potem jako struktura wielowymiarowa:

[
	'first' => [
		'name' => /* ... */,
		'email' => /* ... */,
	],
	'second' => [
		'name' => /* ... */,
		'email' => /* ... */,
	],
]

Przegląd ustawień

Dla wszystkich elementów możemy wywołać poniższe metody (kompletny przegląd znajdziesz w dokumentacji API):

setDefaultValue($value) ustawia wartość domyślną
getValue() pobiera bieżącą wartość
setOmitted() Pomijane wartości
setDisabled() Wyłączanie elementów

Renderowanie:

setCaption($caption) zmienia etykietę elementu
setTranslator($translator) ustawia translator
setHtmlAttribute($name, $value) ustawia atrybut HTML elementu
setHtmlId($id) ustawia atrybut HTML id
setOption($key, $value) ustawia opcje renderowania

Walidacja:

setRequired() czyni element obowiązkowym
addRule() dodaje regułę walidacyjną
addCondition(), addConditionOn() ustawia warunek walidacyjny
addError($message) dodaje komunikat o błędzie

Dla elementów addText(), addPassword(), addTextArea(), addEmail(), addInteger(), addFloat() można wywołać poniższe metody:

setNullable() ustawia, czy getValue() zwraca null zamiast pustego ciągu
setEmptyValue($value) ustawia specjalną wartość, która jest traktowana jak pusty ciąg
setMaxLength($length) ustawia maksymalną dozwoloną liczbę znaków
addFilter($filter) modyfikuje wejście

Pomijane wartości

Jeśli nie interesuje nas wartość wpisana przez użytkownika, możemy metodą setOmitted() wykluczyć ją z wyniku metody $form->getValues() albo z danych przekazywanych handlerom. Przydaje się to przy różnych polach do potwierdzania hasła, elementach antyspamowych itd.

$form->addPassword('passwordVerify', 'Hasło ponownie:')
	->setRequired('Wpisz hasło jeszcze raz dla kontroli')
	->addRule($form::Equal, 'Hasła nie są zgodne', $form['password'])
	->setOmitted();

Wyłączanie elementów

Elementy można wyłączyć metodą setDisabled(). Wyłączonego elementu użytkownik nie może edytować.

$form->addText('username', 'Nazwa użytkownika:')
	->setDisabled();

Wyłączone elementy nie są w ogóle wysyłane przez przeglądarkę na serwer, więc nie znajdziesz ich w danych zwracanych przez funkcję $form->getValues(). Jeśli jednak ustawisz setOmitted(false), Nette umieści w tych danych ich wartość domyślną.

Przy wywołaniu setDisabled() ze względów bezpieczeństwa wartość elementu jest czyszczona. Jeśli ustawiasz wartość domyślną, trzeba zrobić to po wyłączeniu:

$form->addText('username', 'Nazwa użytkownika:')
	->setDisabled()
	->setDefaultValue($userName);

Alternatywą dla elementów wyłączonych są elementy z atrybutem HTML readonly, które przeglądarka na serwer wysyła. Chociaż element jest tylko do odczytu, ważne jest, żeby zdać sobie sprawę, że jego wartość i tak może zostać zmodyfikowana albo podrobiona przez atakującego.

Własne elementy

Oprócz szerokiej palety wbudowanych elementów formularza możesz dodawać do formularza własne elementy:

$form->addComponent(new DateInput('Data:'), 'date');
// alternatywna składnia: $form['date'] = new DateInput('Data:');

Jak napisać taki element wraz z odczytem wysłanych danych, walidacją i renderowaniem, opisuje osobny rozdział. Dowiesz się tam także o metodach rozszerzających, które pozwalają utworzyć własną metodę dodającą, jak $form->addZip().

Elementy niskopoziomowe

Można używać także elementów, które są zapisane tylko w szablonie i nie zostały dodane do formularza żadną z metod $form->addXyz(). Na przykład przy wypisywaniu rekordów z bazy danych, gdy z góry nie wiemy, ile ich będzie ani jakie będą ich ID, i chcemy dla każdego wiersza wyświetlić checkbox albo radio button, wystarczy zakodować to w szablonie:

{foreach $items as $item}
	<p><input type=checkbox name="sel[]" value={$item->id}> {$item->name}</p>
{/foreach}

A po wysłaniu odczytamy wartość:

$data = $form->getHttpData($form::DataText, 'sel[]');
$data = $form->getHttpData($form::DataText | $form::DataKeys, 'sel[]');

gdzie pierwszy parametr to typ elementu (DataFile dla type=file, DataLine dla inputów jednoliniowych, jak text, password, email itd., a DataText dla pozostałych), a drugi parametr sel[] odpowiada atrybutowi HTML name. Typ elementu możemy połączyć z wartością DataKeys, która zachowuje klucze elementów. Przydaje się to zwłaszcza przy select, radioList i checkboxList.

Co istotne, getHttpData() zwraca oczyszczoną wartość. W tym przypadku będzie to zawsze tablica poprawnych ciągów UTF-8, niezależnie od tego, co atakujący spróbowałby wysłać na serwer. Jest to analogiczne do bezpośredniej pracy z $_POST albo $_GET, z tą istotną różnicą, że zawsze zwraca czyste dane, tak jak jesteś przyzwyczajony przy standardowych elementach formularzy Nette.

wersja: 4.x