public function bind($clientData)
{
if ($this->readOnly) {
$this->bound = true;
return $this;
}
if (is_scalar($clientData) || null === $clientData) {
$clientData = (string) $clientData;
}
// Initialize errors in the very beginning so that we don't lose any
// errors added during listeners
$this->errors = array();
$event = new DataEvent($this, $clientData);
$this->dispatcher->dispatch(FormEvents::PRE_BIND, $event);
$appData = null;
$normData = null;
$extraData = array();
$synchronized = false;
// Hook to change content of the data bound by the browser
$event = new FilterDataEvent($this, $clientData);
$this->dispatcher->dispatch(FormEvents::BIND_CLIENT_DATA, $event);
$clientData = $event->getData();
if (count($this->children) > 0) {
if (null === $clientData || '' === $clientData) {
$clientData = array();
}
if (!is_array($clientData)) {
throw new UnexpectedTypeException($clientData, 'array');
}
foreach ($this->children as $name => $child) {
if (!isset($clientData[$name])) {
$clientData[$name] = null;
}
}
foreach ($clientData as $name => $value) {
if ($this->has($name)) {
$this->children[$name]->bind($value);
} else {
$extraData[$name] = $value;
}
}
// If we have a data mapper, use old client data and merge
// data from the children into it later
if ($this->dataMapper) {
$clientData = $this->getClientData();
}
}
if (null === $clientData || '' === $clientData) {
$clientData = $this->emptyData;
if ($clientData instanceof \Closure) {
$clientData = $clientData($this);
}
}
// Merge form data from children into existing client data
if (count($this->children) > 0 && $this->dataMapper) {
$this->dataMapper->mapFormsToData($this->children, $clientData);
}
try {
// Normalize data to unified representation
$normData = $this->clientToNorm($clientData);
$synchronized = true;
} catch (TransformationFailedException $e) {
}
if ($synchronized) {
// Hook to change content of the data in the normalized
// representation
$event = new FilterDataEvent($this, $normData);
$this->dispatcher->dispatch(FormEvents::BIND_NORM_DATA, $event);
$normData = $event->getData();
// Synchronize representations - must not change the content!
$appData = $this->normToApp($normData);
$clientData = $this->normToClient($normData);
}
$this->bound = true;
$this->appData = $appData;
$this->normData = $normData;
$this->clientData = $clientData;
$this->extraData = $extraData;
$this->synchronized = $synchronized;
$event = new DataEvent($this, $clientData);
$this->dispatcher->dispatch(FormEvents::POST_BIND, $event);
foreach ($this->validators as $validator) {
$validator->validate($this);
}
return $this;
}