/** * {@inheritdoc} */ public function getSetting($name) { if (is_array($name)) { if (NestedArray::keyExists($this->settings, $name)) { return NestedArray::getValue($this->settings, $name); } elseif ($plugin = $this->getPlugin()) { $defaults = $plugin->defaultSettings(); return NestedArray::getValue($defaults, $name); } } else { if (isset($this->settings[$name])) { return $this->settings[$name]; } elseif ($plugin = $this->getPlugin()) { $defaults = $plugin->defaultSettings(); if (isset($defaults[$name])) { return $defaults[$name]; } } } }
/** * Adds the #name and #value properties of an input element before rendering. */ protected function handleInputElement($form_id, &$element, FormStateInterface &$form_state) { if (!isset($element['#name'])) { $name = array_shift($element['#parents']); $element['#name'] = $name; if ($element['#type'] == 'file') { // To make it easier to handle files in file.inc, we place all // file fields in the 'files' array. Also, we do not support // nested file names. // @todo Remove this files prefix now? $element['#name'] = 'files[' . $element['#name'] . ']'; } elseif (count($element['#parents'])) { $element['#name'] .= '[' . implode('][', $element['#parents']) . ']'; } array_unshift($element['#parents'], $name); } // Setting #disabled to TRUE results in user input being ignored regardless // of how the element is themed or whether JavaScript is used to change the // control's attributes. However, it's good UI to let the user know that // input is not wanted for the control. HTML supports two attributes for: // this: http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form // wants to start a control off with one of these attributes for UI // purposes, only, but still allow input to be processed if it's submitted, // it can set the desired attribute in #attributes directly rather than // using #disabled. However, developers should think carefully about the // accessibility implications of doing so: if the form expects input to be // enterable under some condition triggered by JavaScript, how would someone // who has JavaScript disabled trigger that condition? Instead, developers // should consider whether a multi-step form would be more appropriate // (#disabled can be changed from step to step). If one still decides to use // JavaScript to affect when a control is enabled, then it is best for // accessibility for the control to be enabled in the HTML, and disabled by // JavaScript on document ready. if (!empty($element['#disabled'])) { if (!empty($element['#allow_focus'])) { $element['#attributes']['readonly'] = 'readonly'; } else { $element['#attributes']['disabled'] = 'disabled'; } } // With JavaScript or other easy hacking, input can be submitted even for // elements with #access=FALSE or #disabled=TRUE. For security, these must // not be processed. Forms that set #disabled=TRUE on an element do not // expect input for the element, and even forms submitted with // self::submitForm() must not be able to get around this. Forms that set // #access=FALSE on an element usually allow access for some users, so forms // submitted with self::submitForm() may bypass access restriction and be // treated as high-privilege users instead. $process_input = empty($element['#disabled']) && ($form_state->isProgrammed() && $form_state->isBypassingProgrammedAccessChecks() || $form_state->isProcessingInput() && (!isset($element['#access']) || $element['#access'])); // Set the element's #value property. if (!isset($element['#value']) && !array_key_exists('#value', $element)) { // @todo Once all elements are converted to plugins in // https://www.drupal.org/node/2311393, rely on // $element['#value_callback'] directly. $value_callable = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value'; if (!is_callable($value_callable)) { $value_callable = '\\Drupal\\Core\\Render\\Element\\FormElement::valueCallback'; } if ($process_input) { // Get the input for the current element. NULL values in the input need // to be explicitly distinguished from missing input. (see below) $input_exists = NULL; $input = NestedArray::getValue($form_state->getUserInput(), $element['#parents'], $input_exists); // For browser-submitted forms, the submitted values do not contain // values for certain elements (empty multiple select, unchecked // checkbox). During initial form processing, we add explicit NULL // values for such elements in FormState::$input. When rebuilding the // form, we can distinguish elements having NULL input from elements // that were not part of the initially submitted form and can therefore // use default values for the latter, if required. Programmatically // submitted forms can submit explicit NULL values when calling // self::submitForm() so we do not modify FormState::$input for them. if (!$input_exists && !$form_state->isRebuilding() && !$form_state->isProgrammed()) { // Add the necessary parent keys to FormState::$input and sets the // element's input value to NULL. NestedArray::setValue($form_state->getUserInput(), $element['#parents'], NULL); $input_exists = TRUE; } // If we have input for the current element, assign it to the #value // property, optionally filtered through $value_callback. if ($input_exists) { // Skip all value callbacks except safe ones like text if the CSRF // token was invalid. if (!$form_state->hasInvalidToken() || $this->valueCallableIsSafe($value_callable)) { $element['#value'] = call_user_func_array($value_callable, array(&$element, $input, &$form_state)); } else { $input = NULL; } if (!isset($element['#value']) && isset($input)) { $element['#value'] = $input; } } // Mark all posted values for validation. if (isset($element['#value']) || !empty($element['#required'])) { $element['#needs_validation'] = TRUE; } } // Load defaults. if (!isset($element['#value'])) { // Call #type_value without a second argument to request default_value // handling. $element['#value'] = call_user_func_array($value_callable, array(&$element, FALSE, &$form_state)); // Final catch. If we haven't set a value yet, use the explicit default // value. Avoid image buttons (which come with garbage value), so we // only get value for the button actually clicked. if (!isset($element['#value']) && empty($element['#has_garbage_value'])) { $element['#value'] = isset($element['#default_value']) ? $element['#default_value'] : ''; } } } // Determine which element (if any) triggered the submission of the form and // keep track of all the clickable buttons in the form for // \Drupal\Core\Form\FormState::cleanValues(). Enforce the same input // processing restrictions as above. if ($process_input) { // Detect if the element triggered the submission via Ajax. if ($this->elementTriggeredScriptedSubmission($element, $form_state)) { $form_state->setTriggeringElement($element); } // If the form was submitted by the browser rather than via Ajax, then it // can only have been triggered by a button, and we need to determine // which button within the constraints of how browsers provide this // information. if (!empty($element['#is_button'])) { // All buttons in the form need to be tracked for // \Drupal\Core\Form\FormState::cleanValues() and for the // self::doBuildForm() code that handles a form submission containing no // button information in \Drupal::request()->request. $buttons = $form_state->getButtons(); $buttons[] = $element; $form_state->setButtons($buttons); if ($this->buttonWasClicked($element, $form_state)) { $form_state->setTriggeringElement($element); } } } // Set the element's value in $form_state->getValues(), but only, if its key // does not exist yet (a #value_callback may have already populated it). if (!NestedArray::keyExists($form_state->getValues(), $element['#parents'])) { $form_state->setValueForElement($element, $element['#value']); } }
/** * Tests if destination property exists. * * @param array|string $property * An array of properties on the destination. * * @return bool * TRUE if the destination property exists. */ public function hasDestinationProperty($property) { return NestedArray::keyExists($this->destination, explode(static::PROPERTY_SEPARATOR, $property)); }
/** * Handles validation errors for forms with limited validation. * * If validation errors are limited then remove any non validated form values, * so that only values that passed validation are left for submit callbacks. * * @param array $form * An associative array containing the structure of the form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. * @param string $form_id * The unique string identifying the form. */ protected function handleErrorsWithLimitedValidation(&$form, FormStateInterface &$form_state, $form_id) { // If validation errors are limited then remove any non validated form values, // so that only values that passed validation are left for submit callbacks. $triggering_element = $form_state->getTriggeringElement(); if (isset($triggering_element['#limit_validation_errors']) && $triggering_element['#limit_validation_errors'] !== FALSE) { $values = array(); foreach ($triggering_element['#limit_validation_errors'] as $section) { // If the section exists within $form_state->getValues(), even if the // value is NULL, copy it to $values. $section_exists = NULL; $value = NestedArray::getValue($form_state->getValues(), $section, $section_exists); if ($section_exists) { NestedArray::setValue($values, $section, $value); } } // A button's #value does not require validation, so for convenience we // allow the value of the clicked button to be retained in its normal // $form_state->getValues() locations, even if these locations are not // included in #limit_validation_errors. if (!empty($triggering_element['#is_button'])) { $button_value = $triggering_element['#value']; // Like all input controls, the button value may be in the location // dictated by #parents. If it is, copy it to $values, but do not // override what may already be in $values. $parents = $triggering_element['#parents']; if (!NestedArray::keyExists($values, $parents) && NestedArray::getValue($form_state->getValues(), $parents) === $button_value) { NestedArray::setValue($values, $parents, $button_value); } // Additionally, self::doBuildForm() places the button value in // $form_state->getValue(BUTTON_NAME). If it's still there, after // validation handlers have run, copy it to $values, but do not override // what may already be in $values. $name = $triggering_element['#name']; if (!isset($values[$name]) && $form_state->getValue($name) === $button_value) { $values[$name] = $button_value; } } $form_state->setValues($values); } }
/** * Tests existence of array key. */ public function testKeyExists() { // Verify that existing key is found. $this->assertTrue(NestedArray::keyExists($this->form, $this->parents), 'Nested key found.'); // Verify that non-existing keys are not found. $parents = $this->parents; $parents[] = 'foo'; $this->assertFalse(NestedArray::keyExists($this->form, $parents), 'Non-existing nested key not found.'); }
public function has($property) { return NestedArray::keyExists($this->data, $this->toKey($property)); }