Added the zend framework 2 library, the path is specified in line no.26 in zend_modul...
[openemr.git] / interface / modules / zend_modules / library / Zend / Form / Form.php
blob5f06a7ed576c024d30ff2760d424fbdba5c014bc
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\Form;
12 use Traversable;
13 use Zend\Form\Element\Collection;
14 use Zend\Form\Exception;
15 use Zend\InputFilter\CollectionInputFilter;
16 use Zend\InputFilter\InputFilter;
17 use Zend\InputFilter\InputFilterAwareInterface;
18 use Zend\InputFilter\InputFilterInterface;
19 use Zend\InputFilter\InputFilterProviderInterface;
20 use Zend\InputFilter\InputProviderInterface;
21 use Zend\Stdlib\ArrayUtils;
22 use Zend\Stdlib\Hydrator\HydratorInterface;
24 class Form extends Fieldset implements FormInterface
26 /**
27 * Seed attributes
29 * @var array
31 protected $attributes = array(
32 'method' => 'POST',
35 /**
36 * How to bind values to the attached object
38 * @var int
40 protected $bindAs = FormInterface::VALUES_NORMALIZED;
42 /**
43 * Whether or not to bind values to the bound object on successful validation
45 * @var int
47 protected $bindOnValidate = FormInterface::BIND_ON_VALIDATE;
49 /**
50 * Base fieldset to use for hydrating (if none specified, directly hydrate elements)
52 * @var FieldsetInterface
54 protected $baseFieldset;
56 /**
57 * Data being validated
59 * @var null|array|Traversable
61 protected $data;
63 /**
64 * @var null|InputFilterInterface
66 protected $filter;
68 /**
69 * Whether or not to automatically scan for input filter defaults on
70 * attached fieldsets and elements
72 * @var bool
74 protected $useInputFilterDefaults = true;
76 /**
77 * Has the input filter defaults been added already ?
79 * @var bool
81 protected $hasAddedInputFilterDefaults = false;
83 /**
84 * Whether or not validation has occurred
86 * @var bool
88 protected $hasValidated = false;
90 /**
91 * Result of last validation operation
93 * @var bool
95 protected $isValid = false;
97 /**
98 * Is the form prepared ?
100 * @var bool
102 protected $isPrepared = false;
105 * Prefer form input filter over input filter defaults
107 * @var bool
109 protected $preferFormInputFilter = true;
112 * Are the form elements/fieldsets wrapped by the form name ?
114 * @var bool
116 protected $wrapElements = false;
119 * Validation group, if any
121 * @var null|array
123 protected $validationGroup;
127 * Set options for a form. Accepted options are:
128 * - prefer_form_input_filter: is form input filter is preferred?
130 * @param array|Traversable $options
131 * @return Element|ElementInterface
132 * @throws Exception\InvalidArgumentException
134 public function setOptions($options)
136 parent::setOptions($options);
138 if (isset($options['prefer_form_input_filter'])) {
139 $this->setPreferFormInputFilter($options['prefer_form_input_filter']);
142 return $this;
146 * Add an element or fieldset
148 * If $elementOrFieldset is an array or Traversable, passes the argument on
149 * to the composed factory to create the object before attaching it.
151 * $flags could contain metadata such as the alias under which to register
152 * the element or fieldset, order in which to prioritize it, etc.
154 * @param array|Traversable|ElementInterface $elementOrFieldset
155 * @param array $flags
156 * @return \Zend\Form\Fieldset|\Zend\Form\FieldsetInterface|\Zend\Form\FormInterface
158 public function add($elementOrFieldset, array $flags = array())
160 // TODO: find a better solution than duplicating the factory code, the problem being that if $elementOrFieldset is an array,
161 // it is passed by value, and we don't get back the concrete ElementInterface
162 if (is_array($elementOrFieldset)
163 || ($elementOrFieldset instanceof Traversable && !$elementOrFieldset instanceof ElementInterface)
165 $factory = $this->getFormFactory();
166 $elementOrFieldset = $factory->create($elementOrFieldset);
169 parent::add($elementOrFieldset, $flags);
171 if ($elementOrFieldset instanceof Fieldset && $elementOrFieldset->useAsBaseFieldset()) {
172 $this->baseFieldset = $elementOrFieldset;
175 return $this;
179 * Ensures state is ready for use
181 * Marshalls the input filter, to ensure validation error messages are
182 * available, and prepares any elements and/or fieldsets that require
183 * preparation.
185 * @return Form
187 public function prepare()
189 if ($this->isPrepared) {
190 return $this;
193 $this->getInputFilter();
195 // If the user wants to, elements names can be wrapped by the form's name
196 if ($this->wrapElements()) {
197 $this->prepareElement($this);
198 } else {
199 foreach ($this->getIterator() as $elementOrFieldset) {
200 if ($elementOrFieldset instanceof FormInterface) {
201 $elementOrFieldset->prepare();
202 } elseif ($elementOrFieldset instanceof ElementPrepareAwareInterface) {
203 $elementOrFieldset->prepareElement($this);
208 $this->isPrepared = true;
209 return $this;
213 * Ensures state is ready for use. Here, we append the name of the fieldsets to every elements in order to avoid
214 * name clashes if the same fieldset is used multiple times
216 * @param FormInterface $form
217 * @return mixed|void
219 public function prepareElement(FormInterface $form)
221 $name = $this->getName();
223 foreach ($this->byName as $elementOrFieldset) {
224 if ($form->wrapElements()) {
225 $elementOrFieldset->setName($name . '[' . $elementOrFieldset->getName() . ']');
228 // Recursively prepare elements
229 if ($elementOrFieldset instanceof ElementPrepareAwareInterface) {
230 $elementOrFieldset->prepareElement($form);
236 * Set data to validate and/or populate elements
238 * Typically, also passes data on to the composed input filter.
240 * @param array|\ArrayAccess|Traversable $data
241 * @return Form|FormInterface
242 * @throws Exception\InvalidArgumentException
244 public function setData($data)
246 if ($data instanceof Traversable) {
247 $data = ArrayUtils::iteratorToArray($data);
249 if (!is_array($data)) {
250 throw new Exception\InvalidArgumentException(sprintf(
251 '%s expects an array or Traversable argument; received "%s"',
252 __METHOD__,
253 (is_object($data) ? get_class($data) : gettype($data))
257 $this->hasValidated = false;
258 $this->data = $data;
259 $this->populateValues($data);
261 return $this;
265 * Bind an object to the form
267 * Ensures the object is populated with validated values.
269 * @param object $object
270 * @param int $flags
271 * @return mixed|void
272 * @throws Exception\InvalidArgumentException
274 public function bind($object, $flags = FormInterface::VALUES_NORMALIZED)
276 if (!in_array($flags, array(FormInterface::VALUES_NORMALIZED, FormInterface::VALUES_RAW))) {
277 throw new Exception\InvalidArgumentException(sprintf(
278 '%s expects the $flags argument to be one of "%s" or "%s"; received "%s"',
279 __METHOD__,
280 'Zend\Form\FormInterface::VALUES_NORMALIZED',
281 'Zend\Form\FormInterface::VALUES_RAW',
282 $flags
286 if ($this->baseFieldset !== null) {
287 $this->baseFieldset->setObject($object);
290 $this->bindAs = $flags;
291 $this->setObject($object);
292 $this->extract();
294 return $this;
298 * Set the hydrator to use when binding an object to the element
300 * @param HydratorInterface $hydrator
301 * @return FieldsetInterface
303 public function setHydrator(HydratorInterface $hydrator)
305 if ($this->baseFieldset !== null) {
306 $this->baseFieldset->setHydrator($hydrator);
309 return parent::setHydrator($hydrator);
313 * Bind values to the bound object
315 * @param array $values
316 * @return mixed
318 public function bindValues(array $values = array())
320 if (!is_object($this->object)) {
321 if ($this->baseFieldset === null || $this->baseFieldset->allowValueBinding() == false) {
322 return;
325 if (!$this->hasValidated() && !empty($values)) {
326 $this->setData($values);
327 if (!$this->isValid()) {
328 return;
330 } elseif (!$this->isValid) {
331 return;
334 $filter = $this->getInputFilter();
336 switch ($this->bindAs) {
337 case FormInterface::VALUES_RAW:
338 $data = $filter->getRawValues();
339 break;
340 case FormInterface::VALUES_NORMALIZED:
341 default:
342 $data = $filter->getValues();
343 break;
346 $data = $this->prepareBindData($data, $this->data);
348 // If there is a base fieldset, only hydrate beginning from the base fieldset
349 if ($this->baseFieldset !== null) {
350 $data = $data[$this->baseFieldset->getName()];
351 $this->object = $this->baseFieldset->bindValues($data);
352 } else {
353 $this->object = parent::bindValues($data);
358 * Parse filtered values and return only posted fields for binding
360 * @param array $values
361 * @param array $match
362 * @return array
364 protected function prepareBindData(array $values, array $match)
366 $data = array();
367 foreach ($values as $name => $value) {
368 if (!array_key_exists($name, $match)) {
369 continue;
372 if (is_array($value) && is_array($match[$name])) {
373 $data[$name] = $this->prepareBindData($value, $match[$name]);
374 } else {
375 $data[$name] = $value;
378 return $data;
382 * Set flag indicating whether or not to bind values on successful validation
384 * @param int $bindOnValidateFlag
385 * @return void|Form
386 * @throws Exception\InvalidArgumentException
388 public function setBindOnValidate($bindOnValidateFlag)
390 if (!in_array($bindOnValidateFlag, array(self::BIND_ON_VALIDATE, self::BIND_MANUAL))) {
391 throw new Exception\InvalidArgumentException(sprintf(
392 '%s expects the flag to be one of %s::%s or %s::%s',
393 __METHOD__,
394 get_class($this),
395 'BIND_ON_VALIDATE',
396 get_class($this),
397 'BIND_MANUAL'
400 $this->bindOnValidate = $bindOnValidateFlag;
401 return $this;
405 * Will we bind values to the bound object on successful validation?
407 * @return bool
409 public function bindOnValidate()
411 return (static::BIND_ON_VALIDATE === $this->bindOnValidate);
415 * Set the base fieldset to use when hydrating
417 * @param FieldsetInterface $baseFieldset
418 * @return Form
419 * @throws Exception\InvalidArgumentException
421 public function setBaseFieldset(FieldsetInterface $baseFieldset)
423 $this->baseFieldset = $baseFieldset;
424 return $this;
428 * Get the base fieldset to use when hydrating
430 * @return FieldsetInterface
432 public function getBaseFieldset()
434 return $this->baseFieldset;
438 * Check if the form has been validated
440 * @return bool
442 public function hasValidated()
444 return $this->hasValidated;
448 * Validate the form
450 * Typically, will proxy to the composed input filter.
452 * @return bool
453 * @throws Exception\DomainException
455 public function isValid()
457 if ($this->hasValidated) {
458 return $this->isValid;
461 $this->isValid = false;
463 if (!is_array($this->data) && !is_object($this->object)) {
464 throw new Exception\DomainException(sprintf(
465 '%s is unable to validate as there is no data currently set',
466 __METHOD__
470 if (!is_array($this->data)) {
471 $data = $this->extract();
472 if (!is_array($data)) {
473 throw new Exception\DomainException(sprintf(
474 '%s is unable to validate as there is no data currently set',
475 __METHOD__
478 $this->data = $data;
481 $filter = $this->getInputFilter();
482 if (!$filter instanceof InputFilterInterface) {
483 throw new Exception\DomainException(sprintf(
484 '%s is unable to validate as there is no input filter present',
485 __METHOD__
489 $filter->setData($this->data);
490 $filter->setValidationGroup(InputFilterInterface::VALIDATE_ALL);
492 $validationGroup = $this->getValidationGroup();
493 if ($validationGroup !== null) {
494 $this->prepareValidationGroup($this, $this->data, $validationGroup);
495 $filter->setValidationGroup($validationGroup);
498 $this->isValid = $result = $filter->isValid();
499 $this->hasValidated = true;
501 if ($result && $this->bindOnValidate()) {
502 $this->bindValues();
505 if (!$result) {
506 $this->setMessages($filter->getMessages());
509 return $result;
513 * Retrieve the validated data
515 * By default, retrieves normalized values; pass one of the
516 * FormInterface::VALUES_* constants to shape the behavior.
518 * @param int $flag
519 * @return array|object
520 * @throws Exception\DomainException
522 public function getData($flag = FormInterface::VALUES_NORMALIZED)
524 if (!$this->hasValidated) {
525 throw new Exception\DomainException(sprintf(
526 '%s cannot return data as validation has not yet occurred',
527 __METHOD__
531 if (($flag !== FormInterface::VALUES_AS_ARRAY) && is_object($this->object)) {
532 return $this->object;
535 $filter = $this->getInputFilter();
537 if ($flag === FormInterface::VALUES_RAW) {
538 return $filter->getRawValues();
541 return $filter->getValues();
545 * Set the validation group (set of values to validate)
547 * Typically, proxies to the composed input filter
549 * @throws Exception\InvalidArgumentException
550 * @return Form|FormInterface
552 public function setValidationGroup()
554 $argc = func_num_args();
555 if (0 === $argc) {
556 throw new Exception\InvalidArgumentException(sprintf(
557 '%s expects at least one argument; none provided',
558 __METHOD__
562 $argv = func_get_args();
563 $this->hasValidated = false;
565 if ($argc > 1) {
566 $this->validationGroup = $argv;
567 return $this;
570 $arg = array_shift($argv);
571 if ($arg === FormInterface::VALIDATE_ALL) {
572 $this->validationGroup = null;
573 return $this;
576 if (!is_array($arg)) {
577 $arg = (array) $arg;
580 $this->validationGroup = $arg;
581 return $this;
585 * Retrieve the current validation group, if any
587 * @return null|array
589 public function getValidationGroup()
591 return $this->validationGroup;
595 * Prepare the validation group in case Collection elements were used (this function also handle the case where elements
596 * could have been dynamically added or removed from a collection using JavaScript)
598 * @param FieldsetInterface $formOrFieldset
599 * @param array $data
600 * @param array $validationGroup
602 protected function prepareValidationGroup(FieldsetInterface $formOrFieldset, array $data, array &$validationGroup)
604 foreach ($validationGroup as $key => &$value) {
605 if (!$formOrFieldset->has($key)) {
606 continue;
609 $fieldset = $formOrFieldset->byName[$key];
611 if ($fieldset instanceof Collection) {
612 if (!isset($data[$key]) && $fieldset->getCount() == 0) {
613 unset ($validationGroup[$key]);
614 continue;
617 $values = array();
619 if (isset($data[$key])) {
620 foreach (array_keys($data[$key]) as $cKey) {
621 $values[$cKey] = $value;
625 $value = $values;
626 } else {
627 if (!isset($data[$key])) {
628 $data[$key] = array();
630 $this->prepareValidationGroup($fieldset, $data[$key], $validationGroup[$key]);
636 * Set the input filter used by this form
638 * @param InputFilterInterface $inputFilter
639 * @return FormInterface
641 public function setInputFilter(InputFilterInterface $inputFilter)
643 $this->hasValidated = false;
644 $this->hasAddedInputFilterDefaults = false;
645 $this->filter = $inputFilter;
646 return $this;
650 * Retrieve input filter used by this form
652 * @return null|InputFilterInterface
654 public function getInputFilter()
656 if ($this->object instanceof InputFilterAwareInterface) {
657 if (null == $this->baseFieldset) {
658 $this->filter = $this->object->getInputFilter();
659 } else {
660 $name = $this->baseFieldset->getName();
661 if (!$this->filter instanceof InputFilterInterface || !$this->filter->has($name)) {
662 $filter = new InputFilter();
663 $filter->add($this->object->getInputFilter(), $name);
664 $this->filter = $filter;
669 if (!isset($this->filter)) {
670 $this->filter = new InputFilter();
673 if (!$this->hasAddedInputFilterDefaults
674 && $this->filter instanceof InputFilterInterface
675 && $this->useInputFilterDefaults()
677 $this->attachInputFilterDefaults($this->filter, $this);
678 $this->hasAddedInputFilterDefaults = true;
681 return $this->filter;
685 * Set flag indicating whether or not to scan elements and fieldsets for defaults
687 * @param bool $useInputFilterDefaults
688 * @return Form
690 public function setUseInputFilterDefaults($useInputFilterDefaults)
692 $this->useInputFilterDefaults = (bool) $useInputFilterDefaults;
693 return $this;
697 * Should we use input filter defaults from elements and fieldsets?
699 * @return bool
701 public function useInputFilterDefaults()
703 return $this->useInputFilterDefaults;
707 * Set flag indicating whether or not to prefer the form input filter over element and fieldset defaults
709 * @param bool $preferFormInputFilter
710 * @return Form
712 public function setPreferFormInputFilter($preferFormInputFilter)
714 $this->preferFormInputFilter = (bool) $preferFormInputFilter;
715 return $this;
719 * Should we use form input filter over element input filter defaults from elements and fieldsets?
721 * @return bool
723 public function getPreferFormInputFilter()
725 return $this->preferFormInputFilter;
729 * Attach defaults provided by the elements to the input filter
731 * @param InputFilterInterface $inputFilter
732 * @param FieldsetInterface $fieldset Fieldset to traverse when looking for default inputs
733 * @return void
735 public function attachInputFilterDefaults(InputFilterInterface $inputFilter, FieldsetInterface $fieldset)
737 $formFactory = $this->getFormFactory();
738 $inputFactory = $formFactory->getInputFilterFactory();
740 if ($fieldset instanceof Collection && $fieldset->getTargetElement() instanceof FieldsetInterface) {
741 $elements = $fieldset->getTargetElement()->getElements();
742 } else {
743 $elements = $fieldset->getElements();
746 if (!$fieldset instanceof Collection || $inputFilter instanceof CollectionInputFilter) {
747 foreach ($elements as $element) {
748 $name = $element->getName();
750 if ($this->preferFormInputFilter && $inputFilter->has($name)) {
751 continue;
754 if (!$element instanceof InputProviderInterface) {
755 if ($inputFilter->has($name)) {
756 continue;
758 // Create a new empty default input for this element
759 $spec = array('name' => $name, 'required' => false);
760 } else {
761 // Create an input based on the specification returned from the element
762 $spec = $element->getInputSpecification();
765 $input = $inputFactory->createInput($spec);
766 $inputFilter->add($input, $name);
769 if ($fieldset === $this && $fieldset instanceof InputFilterProviderInterface) {
770 foreach ($fieldset->getInputFilterSpecification() as $name => $spec) {
771 $input = $inputFactory->createInput($spec);
772 $inputFilter->add($input, $name);
777 foreach ($fieldset->getFieldsets() as $childFieldset) {
778 $name = $childFieldset->getName();
780 if (!$childFieldset instanceof InputFilterProviderInterface) {
781 if (!$inputFilter->has($name)) {
782 // Add a new empty input filter if it does not exist (or the fieldset's object input filter),
783 // so that elements of nested fieldsets can be recursively added
784 if ($childFieldset->getObject() instanceof InputFilterAwareInterface) {
785 $inputFilter->add($childFieldset->getObject()->getInputFilter(), $name);
786 } else {
787 if ($fieldset instanceof Collection && $inputFilter instanceof CollectionInputFilter) {
788 continue;
789 } else {
790 $inputFilter->add(new InputFilter(), $name);
795 $fieldsetFilter = $inputFilter->get($name);
797 if (!$fieldsetFilter instanceof InputFilterInterface) {
798 // Input attached for fieldset, not input filter; nothing more to do.
799 continue;
802 // Traverse the elements of the fieldset, and attach any
803 // defaults to the fieldset's input filter
804 $this->attachInputFilterDefaults($fieldsetFilter, $childFieldset);
805 continue;
808 if ($inputFilter->has($name)) {
809 // if we already have an input/filter by this name, use it
810 continue;
813 // Create an input filter based on the specification returned from the fieldset
814 $spec = $childFieldset->getInputFilterSpecification();
815 $filter = $inputFactory->createInputFilter($spec);
816 $inputFilter->add($filter, $name);
818 // Recursively attach sub filters
819 $this->attachInputFilterDefaults($filter, $childFieldset);
824 * Are the form elements/fieldsets names wrapped by the form name ?
826 * @param bool $wrapElements
827 * @return Form
829 public function setWrapElements($wrapElements)
831 $this->wrapElements = (bool) $wrapElements;
832 return $this;
836 * If true, form elements/fieldsets name's are wrapped around the form name itself
838 * @return bool
840 public function wrapElements()
842 return $this->wrapElements;
846 * Recursively extract values for elements and sub-fieldsets, and populate form values
848 * @return array
850 protected function extract()
852 if (null !== $this->baseFieldset) {
853 $name = $this->baseFieldset->getName();
854 $values[$name] = $this->baseFieldset->extract();
855 $this->baseFieldset->populateValues($values[$name]);
856 } else {
857 $values = parent::extract();
858 $this->populateValues($values);
861 return $values;