1<?php 2/** 3 * Zend Framework 4 * 5 * LICENSE 6 * 7 * This source file is subject to the new BSD license that is bundled 8 * with this package in the file LICENSE.txt. 9 * It is also available through the world-wide-web at this URL: 10 * http://framework.zend.com/license/new-bsd 11 * If you did not receive a copy of the license and are unable to 12 * obtain it through the world-wide-web, please send an email 13 * to license@zend.com so we can send you a copy immediately. 14 * 15 * @category Zend 16 * @package Zend_Form 17 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 18 * @license http://framework.zend.com/license/new-bsd New BSD License 19 */ 20 21/** @see Zend_Validate_Interface */ 22 23/** 24 * Zend_Form 25 * 26 * @category Zend 27 * @package Zend_Form 28 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 29 * @license http://framework.zend.com/license/new-bsd New BSD License 30 * @version $Id$ 31 */ 32class Zend_Form implements Iterator, Countable, Zend_Validate_Interface 33{ 34 /**#@+ 35 * Plugin loader type constants 36 */ 37 const DECORATOR = 'DECORATOR'; 38 const ELEMENT = 'ELEMENT'; 39 /**#@-*/ 40 41 /**#@+ 42 * Method type constants 43 */ 44 const METHOD_DELETE = 'delete'; 45 const METHOD_GET = 'get'; 46 const METHOD_POST = 'post'; 47 const METHOD_PUT = 'put'; 48 /**#@-*/ 49 50 /**#@+ 51 * Encoding type constants 52 */ 53 const ENCTYPE_URLENCODED = 'application/x-www-form-urlencoded'; 54 const ENCTYPE_MULTIPART = 'multipart/form-data'; 55 /**#@-*/ 56 57 /** 58 * Form metadata and attributes 59 * @var array 60 */ 61 protected $_attribs = array(); 62 63 /** 64 * Decorators for rendering 65 * @var array 66 */ 67 protected $_decorators = array(); 68 69 /** 70 * Default display group class 71 * @var string 72 */ 73 protected $_defaultDisplayGroupClass = 'Zend_Form_DisplayGroup'; 74 75 /** 76 * Form description 77 * @var string 78 */ 79 protected $_description; 80 81 /** 82 * Should we disable loading the default decorators? 83 * @var bool 84 */ 85 protected $_disableLoadDefaultDecorators = false; 86 87 /** 88 * Display group prefix paths 89 * @var array 90 */ 91 protected $_displayGroupPrefixPaths = array(); 92 93 /** 94 * Groups of elements grouped for display purposes 95 * @var array 96 */ 97 protected $_displayGroups = array(); 98 99 /** 100 * Global decorators to apply to all elements 101 * @var null|array 102 */ 103 protected $_elementDecorators; 104 105 /** 106 * Prefix paths to use when creating elements 107 * @var array 108 */ 109 protected $_elementPrefixPaths = array(); 110 111 /** 112 * Form elements 113 * @var array 114 */ 115 protected $_elements = array(); 116 117 /** 118 * Array to which elements belong (if any) 119 * @var string 120 */ 121 protected $_elementsBelongTo; 122 123 /** 124 * Custom form-level error messages 125 * @var array 126 */ 127 protected $_errorMessages = array(); 128 129 /** 130 * Are there errors in the form? 131 * @var bool 132 */ 133 protected $_errorsExist = false; 134 135 /** 136 * Has the form been manually flagged as an error? 137 * @var bool 138 */ 139 protected $_errorsForced = false; 140 141 /** 142 * Form order 143 * @var int|null 144 */ 145 protected $_formOrder; 146 147 /** 148 * Whether or not form elements are members of an array 149 * @var bool 150 */ 151 protected $_isArray = false; 152 153 /** 154 * Form legend 155 * @var string 156 */ 157 protected $_legend; 158 159 /** 160 * Plugin loaders 161 * @var array 162 */ 163 protected $_loaders = array(); 164 165 /** 166 * Allowed form methods 167 * @var array 168 */ 169 protected $_methods = array('delete', 'get', 'post', 'put'); 170 171 /** 172 * Order in which to display and iterate elements 173 * @var array 174 */ 175 protected $_order = array(); 176 177 /** 178 * Whether internal order has been updated or not 179 * @var bool 180 */ 181 protected $_orderUpdated = false; 182 183 /** 184 * Sub form prefix paths 185 * @var array 186 */ 187 protected $_subFormPrefixPaths = array(); 188 189 /** 190 * Sub forms 191 * @var array 192 */ 193 protected $_subForms = array(); 194 195 /** 196 * @var Zend_Translate 197 */ 198 protected $_translator; 199 200 /** 201 * Global default translation adapter 202 * @var Zend_Translate 203 */ 204 protected static $_translatorDefault; 205 206 /** 207 * is the translator disabled? 208 * @var bool 209 */ 210 protected $_translatorDisabled = false; 211 212 /** 213 * @var Zend_View_Interface 214 */ 215 protected $_view; 216 217 /** 218 * @var bool 219 */ 220 protected $_isRendered = false; 221 222 /** 223 * Constructor 224 * 225 * Registers form view helper as decorator 226 * 227 * @param mixed $options 228 */ 229 public function __construct($options = null) 230 { 231 if (is_array($options)) { 232 $this->setOptions($options); 233 } elseif ($options instanceof Zend_Config) { 234 $this->setConfig($options); 235 } 236 237 // Extensions... 238 $this->init(); 239 240 $this->loadDefaultDecorators(); 241 } 242 243 /** 244 * Clone form object and all children 245 * 246 * @return void 247 */ 248 public function __clone() 249 { 250 $elements = array(); 251 foreach ($this->getElements() as $name => $element) { 252 $elements[] = clone $element; 253 } 254 $this->setElements($elements); 255 256 $subForms = array(); 257 foreach ($this->getSubForms() as $name => $subForm) { 258 $subForms[$name] = clone $subForm; 259 } 260 $this->setSubForms($subForms); 261 262 $displayGroups = array(); 263 foreach ($this->_displayGroups as $group) { 264 /** @var Zend_Form_DisplayGroup $clone */ 265 $clone = clone $group; 266 $elements = array(); 267 foreach ($clone->getElements() as $name => $e) { 268 $elements[] = $this->getElement($name); 269 } 270 $clone->setElements($elements); 271 $displayGroups[] = $clone; 272 } 273 $this->setDisplayGroups($displayGroups); 274 } 275 276 /** 277 * Reset values of form 278 * 279 * @return Zend_Form 280 */ 281 public function reset() 282 { 283 /** @var Zend_Form_Element $element */ 284 foreach ($this->getElements() as $element) { 285 $element->setValue(null); 286 } 287 /** @var Zend_Form_SubForm $subForm */ 288 foreach ($this->getSubForms() as $subForm) { 289 $subForm->reset(); 290 } 291 292 return $this; 293 } 294 295 /** 296 * Initialize form (used by extending classes) 297 * 298 * @return void 299 */ 300 public function init() 301 { 302 } 303 304 /** 305 * Set form state from options array 306 * 307 * @param array $options 308 * @return Zend_Form 309 */ 310 public function setOptions(array $options) 311 { 312 if (isset($options['prefixPath'])) { 313 $this->addPrefixPaths($options['prefixPath']); 314 unset($options['prefixPath']); 315 } 316 317 if (isset($options['elementPrefixPath'])) { 318 $this->addElementPrefixPaths($options['elementPrefixPath']); 319 unset($options['elementPrefixPath']); 320 } 321 322 if (isset($options['displayGroupPrefixPath'])) { 323 $this->addDisplayGroupPrefixPaths($options['displayGroupPrefixPath']); 324 unset($options['displayGroupPrefixPath']); 325 } 326 327 if (isset($options['elementDecorators'])) { 328 $this->_elementDecorators = $options['elementDecorators']; 329 unset($options['elementDecorators']); 330 } 331 332 if (isset($options['elements'])) { 333 $this->setElements($options['elements']); 334 unset($options['elements']); 335 } 336 337 if (isset($options['defaultDisplayGroupClass'])) { 338 $this->setDefaultDisplayGroupClass($options['defaultDisplayGroupClass']); 339 unset($options['defaultDisplayGroupClass']); 340 } 341 342 if (isset($options['displayGroupDecorators'])) { 343 $displayGroupDecorators = $options['displayGroupDecorators']; 344 unset($options['displayGroupDecorators']); 345 } 346 347 if (isset($options['elementsBelongTo'])) { 348 $elementsBelongTo = $options['elementsBelongTo']; 349 unset($options['elementsBelongTo']); 350 } 351 352 if (isset($options['attribs'])) { 353 $this->addAttribs($options['attribs']); 354 unset($options['attribs']); 355 } 356 357 if (isset($options['subForms'])) { 358 $this->addSubForms($options['subForms']); 359 unset($options['subForms']); 360 } 361 362 $forbidden = array( 363 'Options', 'Config', 'PluginLoader', 'SubForms', 'Translator', 364 'Attrib', 'Default', 365 ); 366 367 foreach ($options as $key => $value) { 368 $normalized = ucfirst($key); 369 if (in_array($normalized, $forbidden)) { 370 continue; 371 } 372 373 $method = 'set' . $normalized; 374 if (method_exists($this, $method)) { 375 if($normalized == 'View' && !($value instanceof Zend_View_Interface)) { 376 continue; 377 } 378 $this->$method($value); 379 } else { 380 $this->setAttrib($key, $value); 381 } 382 } 383 384 if (isset($displayGroupDecorators)) { 385 $this->setDisplayGroupDecorators($displayGroupDecorators); 386 } 387 388 if (isset($elementsBelongTo)) { 389 $this->setElementsBelongTo($elementsBelongTo); 390 } 391 392 return $this; 393 } 394 395 /** 396 * Set form state from config object 397 * 398 * @param Zend_Config $config 399 * @return Zend_Form 400 */ 401 public function setConfig(Zend_Config $config) 402 { 403 return $this->setOptions($config->toArray()); 404 } 405 406 407 // Loaders 408 409 /** 410 * Set plugin loaders for use with decorators and elements 411 * 412 * @param Zend_Loader_PluginLoader_Interface $loader 413 * @param string $type 'decorator' or 'element' 414 * @return Zend_Form 415 * @throws Zend_Form_Exception on invalid type 416 */ 417 public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader, $type = null) 418 { 419 $type = strtoupper($type); 420 switch ($type) { 421 case self::DECORATOR: 422 case self::ELEMENT: 423 $this->_loaders[$type] = $loader; 424 return $this; 425 default: 426 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to setPluginLoader()', $type)); 427 } 428 } 429 430 /** 431 * Retrieve plugin loader for given type 432 * 433 * $type may be one of: 434 * - decorator 435 * - element 436 * 437 * If a plugin loader does not exist for the given type, defaults are 438 * created. 439 * 440 * @param string $type 441 * @return Zend_Loader_PluginLoader_Interface 442 * @throws Zend_Form_Exception 443 */ 444 public function getPluginLoader($type = null) 445 { 446 $type = strtoupper($type); 447 if (!isset($this->_loaders[$type])) { 448 switch ($type) { 449 case self::DECORATOR: 450 $prefixSegment = 'Form_Decorator'; 451 $pathSegment = 'Form/Decorator'; 452 break; 453 case self::ELEMENT: 454 $prefixSegment = 'Form_Element'; 455 $pathSegment = 'Form/Element'; 456 break; 457 default: 458 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type)); 459 } 460 461 $this->_loaders[$type] = new Zend_Loader_PluginLoader( 462 array('Zend_' . $prefixSegment . '_' => 'Zend/' . $pathSegment . '/') 463 ); 464 } 465 466 return $this->_loaders[$type]; 467 } 468 469 /** 470 * Add prefix path for plugin loader 471 * 472 * If no $type specified, assumes it is a base path for both filters and 473 * validators, and sets each according to the following rules: 474 * - decorators: $prefix = $prefix . '_Decorator' 475 * - elements: $prefix = $prefix . '_Element' 476 * 477 * Otherwise, the path prefix is set on the appropriate plugin loader. 478 * 479 * If $type is 'decorator', sets the path in the decorator plugin loader 480 * for all elements. Additionally, if no $type is provided, 481 * the prefix and path is added to both decorator and element 482 * plugin loader with following settings: 483 * $prefix . '_Decorator', $path . '/Decorator/' 484 * $prefix . '_Element', $path . '/Element/' 485 * 486 * @param string $prefix 487 * @param string $path 488 * @param string $type 489 * @return Zend_Form 490 * @throws Zend_Form_Exception for invalid type 491 */ 492 public function addPrefixPath($prefix, $path, $type = null) 493 { 494 $type = strtoupper($type); 495 switch ($type) { 496 case self::DECORATOR: 497 case self::ELEMENT: 498 $loader = $this->getPluginLoader($type); 499 $loader->addPrefixPath($prefix, $path); 500 return $this; 501 case null: 502 $nsSeparator = (false !== strpos($prefix, '\\'))?'\\':'_'; 503 $prefix = rtrim($prefix, $nsSeparator); 504 $path = rtrim($path, DIRECTORY_SEPARATOR); 505 foreach (array(self::DECORATOR, self::ELEMENT) as $type) { 506 $cType = ucfirst(strtolower($type)); 507 $pluginPath = $path . DIRECTORY_SEPARATOR . $cType . DIRECTORY_SEPARATOR; 508 $pluginPrefix = $prefix . $nsSeparator . $cType; 509 $loader = $this->getPluginLoader($type); 510 $loader->addPrefixPath($pluginPrefix, $pluginPath); 511 } 512 return $this; 513 default: 514 throw new Zend_Form_Exception(sprintf('Invalid type "%s" provided to getPluginLoader()', $type)); 515 } 516 } 517 518 /** 519 * Add many prefix paths at once 520 * 521 * @param array $spec 522 * @return Zend_Form 523 */ 524 public function addPrefixPaths(array $spec) 525 { 526 if (isset($spec['prefix']) && isset($spec['path'])) { 527 return $this->addPrefixPath($spec['prefix'], $spec['path']); 528 } 529 foreach ($spec as $type => $paths) { 530 if (is_numeric($type) && is_array($paths)) { 531 $type = null; 532 if (isset($paths['prefix']) && isset($paths['path'])) { 533 if (isset($paths['type'])) { 534 $type = $paths['type']; 535 } 536 $this->addPrefixPath($paths['prefix'], $paths['path'], $type); 537 } 538 } elseif (!is_numeric($type)) { 539 if (!isset($paths['prefix']) || !isset($paths['path'])) { 540 continue; 541 } 542 $this->addPrefixPath($paths['prefix'], $paths['path'], $type); 543 } 544 } 545 return $this; 546 } 547 548 /** 549 * Add prefix path for all elements 550 * 551 * @param string $prefix 552 * @param string $path 553 * @param string $type 554 * @return Zend_Form 555 */ 556 public function addElementPrefixPath($prefix, $path, $type = null) 557 { 558 $this->_elementPrefixPaths[] = array( 559 'prefix' => $prefix, 560 'path' => $path, 561 'type' => $type, 562 ); 563 564 /** @var Zend_Form_Element $element */ 565 foreach ($this->getElements() as $element) { 566 $element->addPrefixPath($prefix, $path, $type); 567 } 568 569 /** @var Zend_Form_SubForm $subForm */ 570 foreach ($this->getSubForms() as $subForm) { 571 $subForm->addElementPrefixPath($prefix, $path, $type); 572 } 573 574 return $this; 575 } 576 577 /** 578 * Add prefix paths for all elements 579 * 580 * @param array $spec 581 * @return Zend_Form 582 */ 583 public function addElementPrefixPaths(array $spec) 584 { 585 $this->_elementPrefixPaths = $this->_elementPrefixPaths + $spec; 586 587 /** @var Zend_Form_Element $element */ 588 foreach ($this->getElements() as $element) { 589 $element->addPrefixPaths($spec); 590 } 591 592 return $this; 593 } 594 595 /** 596 * Add prefix path for all display groups 597 * 598 * @param string $prefix 599 * @param string $path 600 * @return Zend_Form 601 */ 602 public function addDisplayGroupPrefixPath($prefix, $path) 603 { 604 $this->_displayGroupPrefixPaths[] = array( 605 'prefix' => $prefix, 606 'path' => $path, 607 ); 608 609 /** @var Zend_Form_DisplayGroup $group */ 610 foreach ($this->getDisplayGroups() as $group) { 611 $group->addPrefixPath($prefix, $path); 612 } 613 614 return $this; 615 } 616 617 /** 618 * Add multiple display group prefix paths at once 619 * 620 * @param array $spec 621 * @return Zend_Form 622 */ 623 public function addDisplayGroupPrefixPaths(array $spec) 624 { 625 foreach ($spec as $key => $value) { 626 if (is_string($value) && !is_numeric($key)) { 627 $this->addDisplayGroupPrefixPath($key, $value); 628 continue; 629 } 630 631 if (is_string($value) && is_numeric($key)) { 632 continue; 633 } 634 635 if (is_array($value)) { 636 $count = count($value); 637 if (array_keys($value) === range(0, $count - 1)) { 638 if ($count < 2) { 639 continue; 640 } 641 $prefix = array_shift($value); 642 $path = array_shift($value); 643 $this->addDisplayGroupPrefixPath($prefix, $path); 644 continue; 645 } 646 if (array_key_exists('prefix', $value) && array_key_exists('path', $value)) { 647 $this->addDisplayGroupPrefixPath($value['prefix'], $value['path']); 648 } 649 } 650 } 651 return $this; 652 } 653 654 // Form metadata: 655 656 /** 657 * Set form attribute 658 * 659 * @param string $key 660 * @param mixed $value 661 * @return Zend_Form 662 */ 663 public function setAttrib($key, $value) 664 { 665 $key = (string) $key; 666 $this->_attribs[$key] = $value; 667 return $this; 668 } 669 670 /** 671 * Add multiple form attributes at once 672 * 673 * @param array $attribs 674 * @return Zend_Form 675 */ 676 public function addAttribs(array $attribs) 677 { 678 foreach ($attribs as $key => $value) { 679 $this->setAttrib($key, $value); 680 } 681 return $this; 682 } 683 684 /** 685 * Set multiple form attributes at once 686 * 687 * Overwrites any previously set attributes. 688 * 689 * @param array $attribs 690 * @return Zend_Form 691 */ 692 public function setAttribs(array $attribs) 693 { 694 $this->clearAttribs(); 695 return $this->addAttribs($attribs); 696 } 697 698 /** 699 * Retrieve a single form attribute 700 * 701 * @param string $key 702 * @return mixed 703 */ 704 public function getAttrib($key) 705 { 706 $key = (string) $key; 707 if (!isset($this->_attribs[$key])) { 708 return null; 709 } 710 711 return $this->_attribs[$key]; 712 } 713 714 /** 715 * Retrieve all form attributes/metadata 716 * 717 * @return array 718 */ 719 public function getAttribs() 720 { 721 return $this->_attribs; 722 } 723 724 /** 725 * Remove attribute 726 * 727 * @param string $key 728 * @return bool 729 */ 730 public function removeAttrib($key) 731 { 732 if (isset($this->_attribs[$key])) { 733 unset($this->_attribs[$key]); 734 return true; 735 } 736 737 return false; 738 } 739 740 /** 741 * Clear all form attributes 742 * 743 * @return Zend_Form 744 */ 745 public function clearAttribs() 746 { 747 $this->_attribs = array(); 748 return $this; 749 } 750 751 /** 752 * Set form action 753 * 754 * @param string $action 755 * @return Zend_Form 756 */ 757 public function setAction($action) 758 { 759 return $this->setAttrib('action', (string) $action); 760 } 761 762 /** 763 * Get form action 764 * 765 * Sets default to '' if not set. 766 * 767 * @return string 768 */ 769 public function getAction() 770 { 771 $action = $this->getAttrib('action'); 772 if (null === $action) { 773 $action = ''; 774 $this->setAction($action); 775 } 776 return $action; 777 } 778 779 /** 780 * Set form method 781 * 782 * Only values in {@link $_methods()} allowed 783 * 784 * @param string $method 785 * @return Zend_Form 786 * @throws Zend_Form_Exception 787 */ 788 public function setMethod($method) 789 { 790 $method = strtolower($method); 791 if (!in_array($method, $this->_methods)) { 792 throw new Zend_Form_Exception(sprintf('"%s" is an invalid form method', $method)); 793 } 794 $this->setAttrib('method', $method); 795 return $this; 796 } 797 798 /** 799 * Retrieve form method 800 * 801 * @return string 802 */ 803 public function getMethod() 804 { 805 if (null === ($method = $this->getAttrib('method'))) { 806 $method = self::METHOD_POST; 807 $this->setAttrib('method', $method); 808 } 809 return strtolower($method); 810 } 811 812 /** 813 * Set encoding type 814 * 815 * @param string $value 816 * @return Zend_Form 817 */ 818 public function setEnctype($value) 819 { 820 $this->setAttrib('enctype', $value); 821 return $this; 822 } 823 824 /** 825 * Get encoding type 826 * 827 * @return string 828 */ 829 public function getEnctype() 830 { 831 if (null === ($enctype = $this->getAttrib('enctype'))) { 832 $enctype = self::ENCTYPE_URLENCODED; 833 $this->setAttrib('enctype', $enctype); 834 } 835 return $this->getAttrib('enctype'); 836 } 837 838 /** 839 * Filter a name to only allow valid variable characters 840 * 841 * @param string $value 842 * @param bool $allowBrackets 843 * @return string 844 */ 845 public function filterName($value, $allowBrackets = false) 846 { 847 $charset = '^a-zA-Z0-9_\x7f-\xff'; 848 if ($allowBrackets) { 849 $charset .= '\[\]'; 850 } 851 return preg_replace('/[' . $charset . ']/', '', (string) $value); 852 } 853 854 /** 855 * Set form name 856 * 857 * @param string $name 858 * @return Zend_Form 859 * @throws Zend_Form_Exception 860 */ 861 public function setName($name) 862 { 863 $name = $this->filterName($name); 864 if ('' === (string)$name) { 865 throw new Zend_Form_Exception('Invalid name provided; must contain only valid variable characters and be non-empty'); 866 } 867 868 return $this->setAttrib('name', $name); 869 } 870 871 /** 872 * Get name attribute 873 * 874 * @return null|string 875 */ 876 public function getName() 877 { 878 return $this->getAttrib('name'); 879 } 880 881 /** 882 * Get fully qualified name 883 * 884 * Places name as subitem of array and/or appends brackets. 885 * 886 * @return string 887 */ 888 public function getFullyQualifiedName() 889 { 890 return $this->getName(); 891 } 892 893 /** 894 * Get element id 895 * 896 * @return string 897 */ 898 public function getId() 899 { 900 if (null !== ($id = $this->getAttrib('id'))) { 901 return $id; 902 } 903 904 $id = $this->getFullyQualifiedName(); 905 906 // Bail early if no array notation detected 907 if (!strstr($id, '[')) { 908 return $id; 909 } 910 911 // Strip array notation 912 if ('[]' == substr($id, -2)) { 913 $id = substr($id, 0, strlen($id) - 2); 914 } 915 $id = str_replace('][', '-', $id); 916 $id = str_replace(array(']', '['), '-', $id); 917 $id = trim($id, '-'); 918 919 return $id; 920 } 921 922 /** 923 * Set form legend 924 * 925 * @param string $value 926 * @return Zend_Form 927 */ 928 public function setLegend($value) 929 { 930 $this->_legend = (string) $value; 931 return $this; 932 } 933 934 /** 935 * Get form legend 936 * 937 * @return string 938 */ 939 public function getLegend() 940 { 941 return $this->_legend; 942 } 943 944 /** 945 * Set form description 946 * 947 * @param string $value 948 * @return Zend_Form 949 */ 950 public function setDescription($value) 951 { 952 $this->_description = (string) $value; 953 return $this; 954 } 955 956 /** 957 * Retrieve form description 958 * 959 * @return string 960 */ 961 public function getDescription() 962 { 963 return $this->_description; 964 } 965 966 /** 967 * Set form order 968 * 969 * @param int $index 970 * @return Zend_Form 971 */ 972 public function setOrder($index) 973 { 974 $this->_formOrder = (int) $index; 975 return $this; 976 } 977 978 /** 979 * Get form order 980 * 981 * @return int|null 982 */ 983 public function getOrder() 984 { 985 return $this->_formOrder; 986 } 987 988 /** 989 * When calling renderFormElements or render this method 990 * is used to set $_isRendered member to prevent repeatedly 991 * merging belongsTo setting 992 */ 993 protected function _setIsRendered() 994 { 995 $this->_isRendered = true; 996 return $this; 997 } 998 999 /** 1000 * Get the value of $_isRendered member 1001 */ 1002 protected function _getIsRendered() 1003 { 1004 return (bool)$this->_isRendered; 1005 } 1006 1007 // Element interaction: 1008 1009 /** 1010 * Add a new element 1011 * 1012 * $element may be either a string element type, or an object of type 1013 * Zend_Form_Element. If a string element type is provided, $name must be 1014 * provided, and $options may be optionally provided for configuring the 1015 * element. 1016 * 1017 * If a Zend_Form_Element is provided, $name may be optionally provided, 1018 * and any provided $options will be ignored. 1019 * 1020 * @param string|Zend_Form_Element $element 1021 * @param string $name 1022 * @param array|Zend_Config $options 1023 * @throws Zend_Form_Exception on invalid element 1024 * @return Zend_Form 1025 */ 1026 public function addElement($element, $name = null, $options = null) 1027 { 1028 if (is_string($element)) { 1029 if (null === $name) { 1030 throw new Zend_Form_Exception( 1031 'Elements specified by string must have an accompanying name' 1032 ); 1033 } 1034 1035 $this->_elements[$name] = $this->createElement($element, $name, $options); 1036 } elseif ($element instanceof Zend_Form_Element) { 1037 $prefixPaths = array(); 1038 $prefixPaths['decorator'] = $this->getPluginLoader('decorator')->getPaths(); 1039 if (!empty($this->_elementPrefixPaths)) { 1040 $prefixPaths = array_merge($prefixPaths, $this->_elementPrefixPaths); 1041 } 1042 1043 if (is_array($this->_elementDecorators) 1044 && 0 == count($element->getDecorators()) 1045 ) { 1046 $element->setDecorators($this->_elementDecorators); 1047 } 1048 1049 if (null === $name) { 1050 $name = $element->getName(); 1051 } 1052 1053 $this->_elements[$name] = $element; 1054 $this->_elements[$name]->addPrefixPaths($prefixPaths); 1055 } else { 1056 throw new Zend_Form_Exception( 1057 'Element must be specified by string or Zend_Form_Element instance' 1058 ); 1059 } 1060 1061 $this->_order[$name] = $this->_elements[$name]->getOrder(); 1062 $this->_orderUpdated = true; 1063 $this->_setElementsBelongTo($name); 1064 1065 return $this; 1066 } 1067 1068 /** 1069 * Create an element 1070 * 1071 * Acts as a factory for creating elements. Elements created with this 1072 * method will not be attached to the form, but will contain element 1073 * settings as specified in the form object (including plugin loader 1074 * prefix paths, default decorators, etc.). 1075 * 1076 * @param string $type 1077 * @param string $name 1078 * @param array|Zend_Config $options 1079 * @throws Zend_Form_Exception 1080 * @return Zend_Form_Element 1081 */ 1082 public function createElement($type, $name, $options = null) 1083 { 1084 if (!is_string($type)) { 1085 throw new Zend_Form_Exception('Element type must be a string indicating type'); 1086 } 1087 1088 if (!is_string($name)) { 1089 throw new Zend_Form_Exception('Element name must be a string'); 1090 } 1091 1092 $prefixPaths = array(); 1093 $prefixPaths['decorator'] = $this->getPluginLoader('decorator')->getPaths(); 1094 if (!empty($this->_elementPrefixPaths)) { 1095 $prefixPaths = array_merge($prefixPaths, $this->_elementPrefixPaths); 1096 } 1097 1098 if ($options instanceof Zend_Config) { 1099 $options = $options->toArray(); 1100 } 1101 1102 if ((null === $options) || !is_array($options)) { 1103 $options = array('prefixPath' => $prefixPaths); 1104 1105 if (is_array($this->_elementDecorators)) { 1106 $options['decorators'] = $this->_elementDecorators; 1107 } 1108 } elseif (is_array($options)) { 1109 if (array_key_exists('prefixPath', $options)) { 1110 $options['prefixPath'] = array_merge($prefixPaths, $options['prefixPath']); 1111 } else { 1112 $options['prefixPath'] = $prefixPaths; 1113 } 1114 1115 if (is_array($this->_elementDecorators) 1116 && !array_key_exists('decorators', $options) 1117 ) { 1118 $options['decorators'] = $this->_elementDecorators; 1119 } 1120 } 1121 1122 $class = $this->getPluginLoader(self::ELEMENT)->load($type); 1123 $element = new $class($name, $options); 1124 1125 return $element; 1126 } 1127 1128 /** 1129 * Add multiple elements at once 1130 * 1131 * @param array $elements 1132 * @return Zend_Form 1133 */ 1134 public function addElements(array $elements) 1135 { 1136 foreach ($elements as $key => $spec) { 1137 $name = null; 1138 if (!is_numeric($key)) { 1139 $name = $key; 1140 } 1141 1142 if (is_string($spec) || ($spec instanceof Zend_Form_Element)) { 1143 $this->addElement($spec, $name); 1144 continue; 1145 } 1146 1147 if (is_array($spec)) { 1148 $argc = count($spec); 1149 $options = array(); 1150 if (isset($spec['type'])) { 1151 $type = $spec['type']; 1152 if (isset($spec['name'])) { 1153 $name = $spec['name']; 1154 } 1155 if (isset($spec['options'])) { 1156 $options = $spec['options']; 1157 } 1158 $this->addElement($type, $name, $options); 1159 } else { 1160 switch ($argc) { 1161 case 0: 1162 continue 2; 1163 case (1 <= $argc): 1164 $type = array_shift($spec); 1165 case (2 <= $argc): 1166 if (null === $name) { 1167 $name = array_shift($spec); 1168 } else { 1169 $options = array_shift($spec); 1170 } 1171 case (3 <= $argc): 1172 if (empty($options)) { 1173 $options = array_shift($spec); 1174 } 1175 default: 1176 $this->addElement($type, $name, $options); 1177 } 1178 } 1179 } 1180 } 1181 return $this; 1182 } 1183 1184 /** 1185 * Set form elements (overwrites existing elements) 1186 * 1187 * @param array $elements 1188 * @return Zend_Form 1189 */ 1190 public function setElements(array $elements) 1191 { 1192 $this->clearElements(); 1193 return $this->addElements($elements); 1194 } 1195 1196 /** 1197 * Retrieve a single element 1198 * 1199 * @param string $name 1200 * @return Zend_Form_Element|null 1201 */ 1202 public function getElement($name) 1203 { 1204 if (array_key_exists($name, $this->_elements)) { 1205 return $this->_elements[$name]; 1206 } 1207 return null; 1208 } 1209 1210 /** 1211 * Retrieve all elements 1212 * 1213 * @return array 1214 */ 1215 public function getElements() 1216 { 1217 return $this->_elements; 1218 } 1219 1220 /** 1221 * Remove element 1222 * 1223 * @param string $name 1224 * @return boolean 1225 */ 1226 public function removeElement($name) 1227 { 1228 $name = (string) $name; 1229 if (isset($this->_elements[$name])) { 1230 unset($this->_elements[$name]); 1231 if (array_key_exists($name, $this->_order)) { 1232 unset($this->_order[$name]); 1233 $this->_orderUpdated = true; 1234 } else { 1235 /** @var Zend_Form_DisplayGroup $group */ 1236 foreach ($this->_displayGroups as $group) { 1237 if (null !== $group->getElement($name)) { 1238 $group->removeElement($name); 1239 } 1240 } 1241 } 1242 return true; 1243 } 1244 1245 return false; 1246 } 1247 1248 /** 1249 * Remove all form elements 1250 * 1251 * @return Zend_Form 1252 */ 1253 public function clearElements() 1254 { 1255 foreach (array_keys($this->_elements) as $key) { 1256 if (array_key_exists($key, $this->_order)) { 1257 unset($this->_order[$key]); 1258 } 1259 } 1260 $this->_elements = array(); 1261 $this->_orderUpdated = true; 1262 return $this; 1263 } 1264 1265 /** 1266 * Set default values for elements 1267 * 1268 * Sets values for all elements specified in the array of $defaults. 1269 * 1270 * @param array $defaults 1271 * @return Zend_Form 1272 */ 1273 public function setDefaults(array $defaults) 1274 { 1275 $eBelongTo = null; 1276 1277 if ($this->isArray()) { 1278 $eBelongTo = $this->getElementsBelongTo(); 1279 $defaults = $this->_dissolveArrayValue($defaults, $eBelongTo); 1280 } 1281 /** @var Zend_Form_Element $element */ 1282 foreach ($this->getElements() as $name => $element) { 1283 $check = $defaults; 1284 if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) { 1285 $check = $this->_dissolveArrayValue($defaults, $belongsTo); 1286 } 1287 if (array_key_exists($name, (array)$check)) { 1288 $this->setDefault($name, $check[$name]); 1289 $defaults = $this->_dissolveArrayUnsetKey($defaults, $belongsTo, $name); 1290 } 1291 } 1292 /** @var Zend_Form_SubForm $form */ 1293 foreach ($this->getSubForms() as $name => $form) { 1294 if (!$form->isArray() && array_key_exists($name, $defaults)) { 1295 $form->setDefaults($defaults[$name]); 1296 } else { 1297 $form->setDefaults($defaults); 1298 } 1299 } 1300 return $this; 1301 } 1302 1303 /** 1304 * Set default value for an element 1305 * 1306 * @param string $name 1307 * @param mixed $value 1308 * @return Zend_Form 1309 */ 1310 public function setDefault($name, $value) 1311 { 1312 $name = (string) $name; 1313 if ($element = $this->getElement($name)) { 1314 $element->setValue($value); 1315 } else { 1316 if (is_scalar($value)) { 1317 /** @var Zend_Form_SubForm $subForm */ 1318 foreach ($this->getSubForms() as $subForm) { 1319 $subForm->setDefault($name, $value); 1320 } 1321 } elseif (is_array($value) && ($subForm = $this->getSubForm($name))) { 1322 $subForm->setDefaults($value); 1323 } 1324 } 1325 return $this; 1326 } 1327 1328 /** 1329 * Retrieve value for single element 1330 * 1331 * @param string $name 1332 * @return mixed 1333 */ 1334 public function getValue($name) 1335 { 1336 if ($element = $this->getElement($name)) { 1337 return $element->getValue(); 1338 } 1339 1340 if ($subForm = $this->getSubForm($name)) { 1341 return $subForm->getValues(true); 1342 } 1343 1344 /** @var Zend_Form_SubForm $subForm */ 1345 foreach ($this->getSubForms() as $subForm) { 1346 if ($name == $subForm->getElementsBelongTo()) { 1347 return $subForm->getValues(true); 1348 } 1349 } 1350 return null; 1351 } 1352 1353 /** 1354 * Retrieve all form element values 1355 * 1356 * @param bool $suppressArrayNotation 1357 * @return array 1358 */ 1359 public function getValues($suppressArrayNotation = false) 1360 { 1361 $values = array(); 1362 $eBelongTo = null; 1363 1364 if ($this->isArray()) { 1365 $eBelongTo = $this->getElementsBelongTo(); 1366 } 1367 /** @var Zend_Form_Element $element */ 1368 foreach ($this->getElements() as $key => $element) { 1369 if (!$element->getIgnore()) { 1370 $merge = array(); 1371 if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) { 1372 if ('' !== (string)$belongsTo) { 1373 $key = $belongsTo . '[' . $key . ']'; 1374 } 1375 } 1376 $merge = $this->_attachToArray($element->getValue(), $key); 1377 $values = $this->_array_replace_recursive($values, $merge); 1378 } 1379 } 1380 /** @var Zend_Form_SubForm $subForm */ 1381 foreach ($this->getSubForms() as $key => $subForm) { 1382 $merge = array(); 1383 if (!$subForm->isArray()) { 1384 $merge[$key] = $subForm->getValues(); 1385 } else { 1386 $merge = $this->_attachToArray($subForm->getValues(true), 1387 $subForm->getElementsBelongTo()); 1388 } 1389 $values = $this->_array_replace_recursive($values, $merge); 1390 } 1391 1392 if (!$suppressArrayNotation && 1393 $this->isArray() && 1394 !$this->_getIsRendered()) { 1395 $values = $this->_attachToArray($values, $this->getElementsBelongTo()); 1396 } 1397 1398 return $values; 1399 } 1400 1401 /** 1402 * Returns only the valid values from the given form input. 1403 * 1404 * For models that can be saved in a partially valid state, for example when following the builder, 1405 * prototype or state patterns it is particularly interessting to retrieve all the current valid 1406 * values to persist them. 1407 * 1408 * @param array $data 1409 * @param bool $suppressArrayNotation 1410 * @return array 1411 */ 1412 public function getValidValues($data, $suppressArrayNotation = false) 1413 { 1414 $values = array(); 1415 $eBelongTo = null; 1416 1417 if ($this->isArray()) { 1418 $eBelongTo = $this->getElementsBelongTo(); 1419 $data = $this->_dissolveArrayValue($data, $eBelongTo); 1420 } 1421 $context = $data; 1422 /** @var Zend_Form_Element $element */ 1423 foreach ($this->getElements() as $key => $element) { 1424 if (!$element->getIgnore()) { 1425 $check = $data; 1426 if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) { 1427 $check = $this->_dissolveArrayValue($data, $belongsTo); 1428 } 1429 if (isset($check[$key])) { 1430 if($element->isValid($check[$key], $context)) { 1431 $merge = array(); 1432 if ($belongsTo !== $eBelongTo && '' !== (string)$belongsTo) { 1433 $key = $belongsTo . '[' . $key . ']'; 1434 } 1435 $merge = $this->_attachToArray($element->getValue(), $key); 1436 $values = $this->_array_replace_recursive($values, $merge); 1437 } 1438 $data = $this->_dissolveArrayUnsetKey($data, $belongsTo, $key); 1439 } 1440 } 1441 } 1442 /** @var Zend_Form_SubForm $form */ 1443 foreach ($this->getSubForms() as $key => $form) { 1444 $merge = array(); 1445 if (isset($data[$key]) && !$form->isArray()) { 1446 $tmp = $form->getValidValues($data[$key]); 1447 if (!empty($tmp)) { 1448 $merge[$key] = $tmp; 1449 } 1450 } else { 1451 $tmp = $form->getValidValues($data, true); 1452 if (!empty($tmp)) { 1453 $merge = $this->_attachToArray($tmp, $form->getElementsBelongTo()); 1454 } 1455 } 1456 $values = $this->_array_replace_recursive($values, $merge); 1457 } 1458 if (!$suppressArrayNotation && 1459 $this->isArray() && 1460 !empty($values) && 1461 !$this->_getIsRendered()) { 1462 $values = $this->_attachToArray($values, $this->getElementsBelongTo()); 1463 } 1464 1465 return $values; 1466 } 1467 1468 /** 1469 * Get unfiltered element value 1470 * 1471 * @param string $name 1472 * @return mixed 1473 */ 1474 public function getUnfilteredValue($name) 1475 { 1476 if ($element = $this->getElement($name)) { 1477 return $element->getUnfilteredValue(); 1478 } 1479 return null; 1480 } 1481 1482 /** 1483 * Retrieve all unfiltered element values 1484 * 1485 * @return array 1486 */ 1487 public function getUnfilteredValues() 1488 { 1489 $values = array(); 1490 /** @var Zend_Form_Element $element */ 1491 foreach ($this->getElements() as $key => $element) { 1492 $values[$key] = $element->getUnfilteredValue(); 1493 } 1494 1495 return $values; 1496 } 1497 1498 /** 1499 * Set all elements' filters 1500 * 1501 * @param array $filters 1502 * @return Zend_Form 1503 */ 1504 public function setElementFilters(array $filters) 1505 { 1506 /** @var Zend_Form_Element $element */ 1507 foreach ($this->getElements() as $element) { 1508 $element->setFilters($filters); 1509 } 1510 return $this; 1511 } 1512 1513 /** 1514 * Set name of array elements belong to 1515 * 1516 * @param string $array 1517 * @return Zend_Form 1518 */ 1519 public function setElementsBelongTo($array) 1520 { 1521 $origName = $this->getElementsBelongTo(); 1522 $name = $this->filterName($array, true); 1523 if ('' === $name) { 1524 $name = null; 1525 } 1526 $this->_elementsBelongTo = $name; 1527 1528 if (null === $name) { 1529 $this->setIsArray(false); 1530 if (null !== $origName) { 1531 $this->_setElementsBelongTo(); 1532 } 1533 } else { 1534 $this->setIsArray(true); 1535 $this->_setElementsBelongTo(); 1536 } 1537 1538 return $this; 1539 } 1540 1541 /** 1542 * Set array to which elements belong 1543 * 1544 * @param string $name Element name 1545 * @return void 1546 */ 1547 protected function _setElementsBelongTo($name = null) 1548 { 1549 $array = $this->getElementsBelongTo(); 1550 1551 if (null === $array) { 1552 return; 1553 } 1554 1555 if (null === $name) { 1556 /** @var Zend_Form_Element $element */ 1557 foreach ($this->getElements() as $element) { 1558 $element->setBelongsTo($array); 1559 } 1560 } else { 1561 if (null !== ($element = $this->getElement($name))) { 1562 $element->setBelongsTo($array); 1563 } 1564 } 1565 } 1566 1567 /** 1568 * Get name of array elements belong to 1569 * 1570 * @return string|null 1571 */ 1572 public function getElementsBelongTo() 1573 { 1574 if ((null === $this->_elementsBelongTo) && $this->isArray()) { 1575 $name = $this->getName(); 1576 if ('' !== (string)$name) { 1577 return $name; 1578 } 1579 } 1580 return $this->_elementsBelongTo; 1581 } 1582 1583 /** 1584 * Set flag indicating elements belong to array 1585 * 1586 * @param bool $flag Value of flag 1587 * @return Zend_Form 1588 */ 1589 public function setIsArray($flag) 1590 { 1591 $this->_isArray = (bool) $flag; 1592 return $this; 1593 } 1594 1595 /** 1596 * Get flag indicating if elements belong to an array 1597 * 1598 * @return bool 1599 */ 1600 public function isArray() 1601 { 1602 return $this->_isArray; 1603 } 1604 1605 // Element groups: 1606 1607 /** 1608 * Add a form group/subform 1609 * 1610 * @param Zend_Form $form 1611 * @param string $name 1612 * @param int $order 1613 * @return Zend_Form 1614 */ 1615 public function addSubForm(Zend_Form $form, $name, $order = null) 1616 { 1617 $name = (string) $name; 1618 /** @var Zend_Loader_PluginLoader $loader */ 1619 foreach ($this->_loaders as $type => $loader) { 1620 $loaderPaths = $loader->getPaths(); 1621 foreach ($loaderPaths as $prefix => $paths) { 1622 foreach ($paths as $path) { 1623 $form->addPrefixPath($prefix, $path, $type); 1624 } 1625 } 1626 } 1627 1628 if (!empty($this->_elementPrefixPaths)) { 1629 foreach ($this->_elementPrefixPaths as $spec) { 1630 list($prefix, $path, $type) = array_values($spec); 1631 $form->addElementPrefixPath($prefix, $path, $type); 1632 } 1633 } 1634 1635 if (!empty($this->_displayGroupPrefixPaths)) { 1636 foreach ($this->_displayGroupPrefixPaths as $spec) { 1637 list($prefix, $path) = array_values($spec); 1638 $form->addDisplayGroupPrefixPath($prefix, $path); 1639 } 1640 } 1641 1642 if (null !== $order) { 1643 $form->setOrder($order); 1644 } 1645 1646 if (($oldName = $form->getName()) && 1647 $oldName !== $name && 1648 $oldName === $form->getElementsBelongTo()) { 1649 $form->setElementsBelongTo($name); 1650 } 1651 1652 $form->setName($name); 1653 $this->_subForms[$name] = $form; 1654 $this->_order[$name] = $order; 1655 $this->_orderUpdated = true; 1656 return $this; 1657 } 1658 1659 /** 1660 * Add multiple form subForms/subforms at once 1661 * 1662 * @param array $subForms 1663 * @return Zend_Form 1664 */ 1665 public function addSubForms(array $subForms) 1666 { 1667 foreach ($subForms as $key => $spec) { 1668 $name = (string) $key; 1669 if ($spec instanceof Zend_Form) { 1670 $this->addSubForm($spec, $name); 1671 continue; 1672 } 1673 1674 if (is_array($spec)) { 1675 $argc = count($spec); 1676 $order = null; 1677 switch ($argc) { 1678 case 0: 1679 continue 2; 1680 case (1 <= $argc): 1681 $subForm = array_shift($spec); 1682 1683 if (!$subForm instanceof Zend_Form) { 1684 $subForm = new Zend_Form_SubForm($subForm); 1685 } 1686 case (2 <= $argc): 1687 $name = array_shift($spec); 1688 case (3 <= $argc): 1689 $order = array_shift($spec); 1690 default: 1691 $this->addSubForm($subForm, $name, $order); 1692 } 1693 } 1694 } 1695 return $this; 1696 } 1697 1698 /** 1699 * Set multiple form subForms/subforms (overwrites) 1700 * 1701 * @param array $subForms 1702 * @return Zend_Form 1703 */ 1704 public function setSubForms(array $subForms) 1705 { 1706 $this->clearSubForms(); 1707 return $this->addSubForms($subForms); 1708 } 1709 1710 /** 1711 * Retrieve a form subForm/subform 1712 * 1713 * @param string $name 1714 * @return Zend_Form|null 1715 */ 1716 public function getSubForm($name) 1717 { 1718 $name = (string) $name; 1719 if (isset($this->_subForms[$name])) { 1720 return $this->_subForms[$name]; 1721 } 1722 return null; 1723 } 1724 1725 /** 1726 * Retrieve all form subForms/subforms 1727 * 1728 * @return array 1729 */ 1730 public function getSubForms() 1731 { 1732 return $this->_subForms; 1733 } 1734 1735 /** 1736 * Remove form subForm/subform 1737 * 1738 * @param string $name 1739 * @return boolean 1740 */ 1741 public function removeSubForm($name) 1742 { 1743 $name = (string) $name; 1744 if (array_key_exists($name, $this->_subForms)) { 1745 unset($this->_subForms[$name]); 1746 if (array_key_exists($name, $this->_order)) { 1747 unset($this->_order[$name]); 1748 $this->_orderUpdated = true; 1749 } 1750 return true; 1751 } 1752 1753 return false; 1754 } 1755 1756 /** 1757 * Remove all form subForms/subforms 1758 * 1759 * @return Zend_Form 1760 */ 1761 public function clearSubForms() 1762 { 1763 foreach (array_keys($this->_subForms) as $key) { 1764 if (array_key_exists($key, $this->_order)) { 1765 unset($this->_order[$key]); 1766 } 1767 } 1768 $this->_subForms = array(); 1769 $this->_orderUpdated = true; 1770 return $this; 1771 } 1772 1773 1774 // Display groups: 1775 1776 /** 1777 * Set default display group class 1778 * 1779 * @param string $class 1780 * @return Zend_Form 1781 */ 1782 public function setDefaultDisplayGroupClass($class) 1783 { 1784 $this->_defaultDisplayGroupClass = (string) $class; 1785 return $this; 1786 } 1787 1788 /** 1789 * Retrieve default display group class 1790 * 1791 * @return string 1792 */ 1793 public function getDefaultDisplayGroupClass() 1794 { 1795 return $this->_defaultDisplayGroupClass; 1796 } 1797 1798 /** 1799 * Add a display group 1800 * 1801 * Groups named elements for display purposes. 1802 * 1803 * If a referenced element does not yet exist in the form, it is omitted. 1804 * 1805 * @param array $elements 1806 * @param string $name 1807 * @param array|Zend_Config $options 1808 * @return Zend_Form 1809 * @throws Zend_Form_Exception if no valid elements provided 1810 */ 1811 public function addDisplayGroup(array $elements, $name, $options = null) 1812 { 1813 $group = array(); 1814 foreach ($elements as $element) { 1815 if($element instanceof Zend_Form_Element) { 1816 $elementName = $element->getName(); 1817 if (!isset($this->_elements[$elementName])) { 1818 $this->addElement($element); 1819 } 1820 $element = $elementName; 1821 } 1822 1823 if (isset($this->_elements[$element])) { 1824 $add = $this->getElement($element); 1825 if (null !== $add) { 1826 $group[] = $add; 1827 } 1828 } 1829 } 1830 if (empty($group)) { 1831 throw new Zend_Form_Exception('No valid elements specified for display group'); 1832 } 1833 1834 $name = (string) $name; 1835 1836 if (is_array($options)) { 1837 $options['form'] = $this; 1838 $options['elements'] = $group; 1839 } elseif ($options instanceof Zend_Config) { 1840 $options = $options->toArray(); 1841 $options['form'] = $this; 1842 $options['elements'] = $group; 1843 } else { 1844 $options = array( 1845 'form' => $this, 1846 'elements' => $group, 1847 ); 1848 } 1849 1850 if (isset($options['displayGroupClass'])) { 1851 $class = $options['displayGroupClass']; 1852 unset($options['displayGroupClass']); 1853 } else { 1854 $class = $this->getDefaultDisplayGroupClass(); 1855 } 1856 1857 if (!class_exists($class)) { 1858 Zend_Loader::loadClass($class); 1859 } 1860 $this->_displayGroups[$name] = new $class( 1861 $name, 1862 $this->getPluginLoader(self::DECORATOR), 1863 $options 1864 ); 1865 1866 if (!empty($this->_displayGroupPrefixPaths)) { 1867 $this->_displayGroups[$name]->addPrefixPaths($this->_displayGroupPrefixPaths); 1868 } 1869 1870 $this->_order[$name] = $this->_displayGroups[$name]->getOrder(); 1871 $this->_orderUpdated = true; 1872 return $this; 1873 } 1874 1875 /** 1876 * Add a display group object (used with cloning) 1877 * 1878 * @param Zend_Form_DisplayGroup $group 1879 * @param string|null $name 1880 * @throws Zend_Form_Exception 1881 * @return Zend_Form 1882 */ 1883 protected function _addDisplayGroupObject(Zend_Form_DisplayGroup $group, $name = null) 1884 { 1885 if (null === $name) { 1886 $name = $group->getName(); 1887 if ('' === (string)$name) { 1888 throw new Zend_Form_Exception('Invalid display group added; requires name'); 1889 } 1890 } 1891 1892 $this->_displayGroups[$name] = $group; 1893 $group->setForm($this); 1894 1895 if (!empty($this->_displayGroupPrefixPaths)) { 1896 $this->_displayGroups[$name]->addPrefixPaths($this->_displayGroupPrefixPaths); 1897 } 1898 1899 $this->_order[$name] = $this->_displayGroups[$name]->getOrder(); 1900 $this->_orderUpdated = true; 1901 return $this; 1902 } 1903 1904 /** 1905 * Add multiple display groups at once 1906 * 1907 * @param array $groups 1908 * @return Zend_Form 1909 */ 1910 public function addDisplayGroups(array $groups) 1911 { 1912 foreach ($groups as $key => $spec) { 1913 $name = null; 1914 if (!is_numeric($key)) { 1915 $name = $key; 1916 } 1917 1918 if ($spec instanceof Zend_Form_DisplayGroup) { 1919 $this->_addDisplayGroupObject($spec); 1920 } 1921 1922 if (!is_array($spec) || empty($spec)) { 1923 continue; 1924 } 1925 1926 $argc = count($spec); 1927 $options = array(); 1928 1929 if (isset($spec['elements'])) { 1930 $elements = $spec['elements']; 1931 if (isset($spec['name'])) { 1932 $name = $spec['name']; 1933 } 1934 if (isset($spec['options'])) { 1935 $options = $spec['options']; 1936 } 1937 $this->addDisplayGroup($elements, $name, $options); 1938 } else { 1939 switch ($argc) { 1940 case (1 <= $argc): 1941 $elements = array_shift($spec); 1942 if (!is_array($elements) && (null !== $name)) { 1943 $elements = array_merge((array) $elements, $spec); 1944 $this->addDisplayGroup($elements, $name); 1945 break; 1946 } 1947 case (2 <= $argc): 1948 if (null !== $name) { 1949 $options = array_shift($spec); 1950 $this->addDisplayGroup($elements, $name, $options); 1951 break; 1952 } 1953 $name = array_shift($spec); 1954 case (3 <= $argc): 1955 $options = array_shift($spec); 1956 default: 1957 $this->addDisplayGroup($elements, $name, $options); 1958 } 1959 } 1960 } 1961 return $this; 1962 } 1963 1964 /** 1965 * Add multiple display groups (overwrites) 1966 * 1967 * @param array $groups 1968 * @return Zend_Form 1969 */ 1970 public function setDisplayGroups(array $groups) 1971 { 1972 return $this->clearDisplayGroups() 1973 ->addDisplayGroups($groups); 1974 } 1975 1976 /** 1977 * Return a display group 1978 * 1979 * @param string $name 1980 * @return Zend_Form_DisplayGroup|null 1981 */ 1982 public function getDisplayGroup($name) 1983 { 1984 $name = (string) $name; 1985 if (isset($this->_displayGroups[$name])) { 1986 return $this->_displayGroups[$name]; 1987 } 1988 1989 return null; 1990 } 1991 1992 /** 1993 * Return all display groups 1994 * 1995 * @return array 1996 */ 1997 public function getDisplayGroups() 1998 { 1999 return $this->_displayGroups; 2000 } 2001 2002 /** 2003 * Remove a display group by name 2004 * 2005 * @param string $name 2006 * @return boolean 2007 */ 2008 public function removeDisplayGroup($name) 2009 { 2010 $name = (string) $name; 2011 if (array_key_exists($name, $this->_displayGroups)) { 2012 /** @var Zend_Form_Element $element */ 2013 foreach ($this->_displayGroups[$name] as $key => $element) { 2014 if (array_key_exists($key, $this->_elements)) { 2015 $this->_order[$key] = $element->getOrder(); 2016 $this->_orderUpdated = true; 2017 } 2018 } 2019 unset($this->_displayGroups[$name]); 2020 2021 if (array_key_exists($name, $this->_order)) { 2022 unset($this->_order[$name]); 2023 $this->_orderUpdated = true; 2024 } 2025 return true; 2026 } 2027 2028 return false; 2029 } 2030 2031 /** 2032 * Remove all display groups 2033 * 2034 * @return Zend_Form 2035 */ 2036 public function clearDisplayGroups() 2037 { 2038 foreach ($this->_displayGroups as $key => $group) { 2039 if (array_key_exists($key, $this->_order)) { 2040 unset($this->_order[$key]); 2041 } 2042 /** @var Zend_Form_Element $element */ 2043 foreach ($group as $name => $element) { 2044 if (isset($this->_elements[$name])) { 2045 $this->_order[$name] = $element->getOrder(); 2046 } 2047 $this->_order[$name] = $element->getOrder(); 2048 } 2049 } 2050 $this->_displayGroups = array(); 2051 $this->_orderUpdated = true; 2052 return $this; 2053 } 2054 2055 2056 // Processing 2057 2058 /** 2059 * Populate form 2060 * 2061 * Proxies to {@link setDefaults()} 2062 * 2063 * @param array $values 2064 * @return Zend_Form 2065 */ 2066 public function populate(array $values) 2067 { 2068 return $this->setDefaults($values); 2069 } 2070 2071 /** 2072 * Determine array key name from given value 2073 * 2074 * Given a value such as foo[bar][baz], returns the last element (in this case, 'baz'). 2075 * 2076 * @param string $value 2077 * @return string 2078 */ 2079 protected function _getArrayName($value) 2080 { 2081 if (!is_string($value) || '' === $value) { 2082 return $value; 2083 } 2084 2085 if (!strstr($value, '[')) { 2086 return $value; 2087 } 2088 2089 $endPos = strlen($value) - 1; 2090 if (']' != $value[$endPos]) { 2091 return $value; 2092 } 2093 2094 $start = strrpos($value, '[') + 1; 2095 $name = substr($value, $start, $endPos - $start); 2096 return $name; 2097 } 2098 2099 /** 2100 * Extract the value by walking the array using given array path. 2101 * 2102 * Given an array path such as foo[bar][baz], returns the value of the last 2103 * element (in this case, 'baz'). 2104 * 2105 * @param array $value Array to walk 2106 * @param string $arrayPath Array notation path of the part to extract 2107 * @return string 2108 */ 2109 protected function _dissolveArrayValue($value, $arrayPath) 2110 { 2111 // As long as we have more levels 2112 while ($arrayPos = strpos($arrayPath, '[')) { 2113 // Get the next key in the path 2114 $arrayKey = trim(substr($arrayPath, 0, $arrayPos), ']'); 2115 2116 // Set the potentially final value or the next search point in the array 2117 if (isset($value[$arrayKey])) { 2118 $value = $value[$arrayKey]; 2119 } 2120 2121 // Set the next search point in the path 2122 $arrayPath = trim(substr($arrayPath, $arrayPos + 1), ']'); 2123 } 2124 2125 if (isset($value[$arrayPath])) { 2126 $value = $value[$arrayPath]; 2127 } 2128 2129 return $value; 2130 } 2131 2132 /** 2133 * Given an array, an optional arrayPath and a key this method 2134 * dissolves the arrayPath and unsets the key within the array 2135 * if it exists. 2136 * 2137 * @param array $array 2138 * @param string|null $arrayPath 2139 * @param string $key 2140 * @return array 2141 */ 2142 protected function _dissolveArrayUnsetKey($array, $arrayPath, $key) 2143 { 2144 $unset =& $array; 2145 $path = trim(strtr((string)$arrayPath, array('[' => '/', ']' => '')), '/'); 2146 $segs = ('' !== $path) ? explode('/', $path) : array(); 2147 2148 foreach ($segs as $seg) { 2149 if (!array_key_exists($seg, (array)$unset)) { 2150 return $array; 2151 } 2152 $unset =& $unset[$seg]; 2153 } 2154 if (array_key_exists($key, (array)$unset)) { 2155 unset($unset[$key]); 2156 } 2157 return $array; 2158 } 2159 2160 /** 2161 * Converts given arrayPath to an array and attaches given value at the end of it. 2162 * 2163 * @param mixed $value The value to attach 2164 * @param string $arrayPath Given array path to convert and attach to. 2165 * @return array 2166 */ 2167 protected function _attachToArray($value, $arrayPath) 2168 { 2169 // As long as we have more levels 2170 while ($arrayPos = strrpos($arrayPath, '[')) { 2171 // Get the next key in the path 2172 $arrayKey = trim(substr($arrayPath, $arrayPos + 1), ']'); 2173 2174 // Attach 2175 $value = array($arrayKey => $value); 2176 2177 // Set the next search point in the path 2178 $arrayPath = trim(substr($arrayPath, 0, $arrayPos), ']'); 2179 } 2180 2181 $value = array($arrayPath => $value); 2182 2183 return $value; 2184 } 2185 2186 /** 2187 * Returns a one dimensional numerical indexed array with the 2188 * Elements, SubForms and Elements from DisplayGroups as Values. 2189 * 2190 * Subitems are inserted based on their order Setting if set, 2191 * otherwise they are appended, the resulting numerical index 2192 * may differ from the order value. 2193 * 2194 * @access protected 2195 * @return array 2196 */ 2197 public function getElementsAndSubFormsOrdered() 2198 { 2199 $ordered = array(); 2200 foreach ($this->_order as $name => $order) { 2201 $order = isset($order) ? $order : count($ordered); 2202 if ($this->$name instanceof Zend_Form_Element || 2203 $this->$name instanceof Zend_Form) { 2204 array_splice($ordered, $order, 0, array($this->$name)); 2205 } else if ($this->$name instanceof Zend_Form_DisplayGroup) { 2206 $subordered = array(); 2207 /** @var Zend_Form_Element $element */ 2208 foreach ($this->$name->getElements() as $element) { 2209 $suborder = $element->getOrder(); 2210 $suborder = (null !== $suborder) ? $suborder : count($subordered); 2211 array_splice($subordered, $suborder, 0, array($element)); 2212 } 2213 if (!empty($subordered)) { 2214 array_splice($ordered, $order, 0, $subordered); 2215 } 2216 } 2217 } 2218 return $ordered; 2219 } 2220 2221 /** 2222 * This is a helper function until php 5.3 is widespreaded 2223 * 2224 * @param array $into 2225 * @return array 2226 */ 2227 protected function _array_replace_recursive(array $into) 2228 { 2229 $fromArrays = array_slice(func_get_args(),1); 2230 2231 foreach ($fromArrays as $from) { 2232 foreach ($from as $key => $value) { 2233 if (is_array($value)) { 2234 if (!isset($into[$key])) { 2235 $into[$key] = array(); 2236 } 2237 $into[$key] = $this->_array_replace_recursive($into[$key], $from[$key]); 2238 } else { 2239 $into[$key] = $value; 2240 } 2241 } 2242 } 2243 return $into; 2244 } 2245 2246 /** 2247 * Validate the form 2248 * 2249 * @param array $data 2250 * @throws Zend_Form_Exception 2251 * @return bool 2252 */ 2253 public function isValid($data) 2254 { 2255 if (!is_array($data)) { 2256 throw new Zend_Form_Exception(__METHOD__ . ' expects an array'); 2257 } 2258 $translator = $this->getTranslator(); 2259 $valid = true; 2260 $eBelongTo = null; 2261 2262 if ($this->isArray()) { 2263 $eBelongTo = $this->getElementsBelongTo(); 2264 $data = $this->_dissolveArrayValue($data, $eBelongTo); 2265 } 2266 $context = $data; 2267 /** @var Zend_Form_Element $element */ 2268 foreach ($this->getElements() as $key => $element) { 2269 if (null !== $translator && $this->hasTranslator() 2270 && !$element->hasTranslator()) { 2271 $element->setTranslator($translator); 2272 } 2273 $check = $data; 2274 if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) { 2275 $check = $this->_dissolveArrayValue($data, $belongsTo); 2276 } 2277 if (!isset($check[$key])) { 2278 $valid = $element->isValid(null, $context) && $valid; 2279 } else { 2280 $valid = $element->isValid($check[$key], $context) && $valid; 2281 $data = $this->_dissolveArrayUnsetKey($data, $belongsTo, $key); 2282 } 2283 } 2284 /** @var Zend_Form_SubForm $form */ 2285 foreach ($this->getSubForms() as $key => $form) { 2286 if (null !== $translator && $this->hasTranslator() 2287 && !$form->hasTranslator()) { 2288 $form->setTranslator($translator); 2289 } 2290 if (isset($data[$key]) && !$form->isArray()) { 2291 $valid = $form->isValid($data[$key]) && $valid; 2292 } else { 2293 $valid = $form->isValid($data) && $valid; 2294 } 2295 } 2296 2297 $this->_errorsExist = !$valid; 2298 2299 // If manually flagged as an error, return invalid status 2300 if ($this->_errorsForced) { 2301 return false; 2302 } 2303 2304 return $valid; 2305 } 2306 2307 /** 2308 * Validate a partial form 2309 * 2310 * Does not check for required flags. 2311 * 2312 * @param array $data 2313 * @return boolean 2314 */ 2315 public function isValidPartial(array $data) 2316 { 2317 $eBelongTo = null; 2318 2319 if ($this->isArray()) { 2320 $eBelongTo = $this->getElementsBelongTo(); 2321 $data = $this->_dissolveArrayValue($data, $eBelongTo); 2322 } 2323 2324 $translator = $this->getTranslator(); 2325 $valid = true; 2326 $context = $data; 2327 2328 /** @var Zend_Form_Element $element */ 2329 foreach ($this->getElements() as $key => $element) { 2330 $check = $data; 2331 if (($belongsTo = $element->getBelongsTo()) !== $eBelongTo) { 2332 $check = $this->_dissolveArrayValue($data, $belongsTo); 2333 } 2334 if (isset($check[$key])) { 2335 if (null !== $translator && !$element->hasTranslator()) { 2336 $element->setTranslator($translator); 2337 } 2338 $valid = $element->isValid($check[$key], $context) && $valid; 2339 $data = $this->_dissolveArrayUnsetKey($data, $belongsTo, $key); 2340 } 2341 } 2342 /** @var Zend_Form_SubForm $form */ 2343 foreach ($this->getSubForms() as $key => $form) { 2344 if (null !== $translator && !$form->hasTranslator()) { 2345 $form->setTranslator($translator); 2346 } 2347 if (isset($data[$key]) && !$form->isArray()) { 2348 $valid = $form->isValidPartial($data[$key]) && $valid; 2349 } else { 2350 $valid = $form->isValidPartial($data) && $valid; 2351 } 2352 } 2353 2354 $this->_errorsExist = !$valid; 2355 return $valid; 2356 } 2357 2358 /** 2359 * Process submitted AJAX data 2360 * 2361 * Checks if provided $data is valid, via {@link isValidPartial()}. If so, 2362 * it returns JSON-encoded boolean true. If not, it returns JSON-encoded 2363 * error messages (as returned by {@link getMessages()}). 2364 * 2365 * @param array $data 2366 * @return string JSON-encoded boolean true or error messages 2367 */ 2368 public function processAjax(array $data) 2369 { 2370 if ($this->isValidPartial($data)) { 2371 return Zend_Json::encode(true); 2372 } 2373 $messages = $this->getMessages(); 2374 return Zend_Json::encode($messages); 2375 } 2376 2377 /** 2378 * Add a custom error message to return in the event of failed validation 2379 * 2380 * @param string $message 2381 * @return Zend_Form 2382 */ 2383 public function addErrorMessage($message) 2384 { 2385 $this->_errorMessages[] = (string) $message; 2386 return $this; 2387 } 2388 2389 /** 2390 * Add multiple custom error messages to return in the event of failed validation 2391 * 2392 * @param array $messages 2393 * @return Zend_Form 2394 */ 2395 public function addErrorMessages(array $messages) 2396 { 2397 foreach ($messages as $message) { 2398 $this->addErrorMessage($message); 2399 } 2400 return $this; 2401 } 2402 2403 /** 2404 * Same as addErrorMessages(), but clears custom error message stack first 2405 * 2406 * @param array $messages 2407 * @return Zend_Form 2408 */ 2409 public function setErrorMessages(array $messages) 2410 { 2411 $this->clearErrorMessages(); 2412 return $this->addErrorMessages($messages); 2413 } 2414 2415 /** 2416 * Retrieve custom error messages 2417 * 2418 * @return array 2419 */ 2420 public function getErrorMessages() 2421 { 2422 return $this->_errorMessages; 2423 } 2424 2425 /** 2426 * Clear custom error messages stack 2427 * 2428 * @return Zend_Form 2429 */ 2430 public function clearErrorMessages() 2431 { 2432 $this->_errorMessages = array(); 2433 return $this; 2434 } 2435 2436 /** 2437 * Mark the element as being in a failed validation state 2438 * 2439 * @return Zend_Form 2440 */ 2441 public function markAsError() 2442 { 2443 $this->_errorsExist = true; 2444 $this->_errorsForced = true; 2445 return $this; 2446 } 2447 2448 /** 2449 * Add an error message and mark element as failed validation 2450 * 2451 * @param string $message 2452 * @return Zend_Form 2453 */ 2454 public function addError($message) 2455 { 2456 $this->addErrorMessage($message); 2457 $this->markAsError(); 2458 return $this; 2459 } 2460 2461 /** 2462 * Add multiple error messages and flag element as failed validation 2463 * 2464 * @param array $messages 2465 * @return Zend_Form 2466 */ 2467 public function addErrors(array $messages) 2468 { 2469 foreach ($messages as $message) { 2470 $this->addError($message); 2471 } 2472 return $this; 2473 } 2474 2475 /** 2476 * Overwrite any previously set error messages and flag as failed validation 2477 * 2478 * @param array $messages 2479 * @return Zend_Form 2480 */ 2481 public function setErrors(array $messages) 2482 { 2483 $this->clearErrorMessages(); 2484 return $this->addErrors($messages); 2485 } 2486 2487 2488 public function persistData() 2489 { 2490 } 2491 2492 /** 2493 * Are there errors in the form? 2494 * 2495 * @deprecated since 1.11.1 - use hasErrors() instead 2496 * @return bool 2497 */ 2498 public function isErrors() 2499 { 2500 return $this->hasErrors(); 2501 } 2502 2503 /** 2504 * Are there errors in the form? 2505 * 2506 * @return bool 2507 */ 2508 public function hasErrors() 2509 { 2510 $errors = $this->_errorsExist; 2511 2512 if (!$errors) { 2513 /** @var Zend_Form_Element $element */ 2514 foreach ($this->getElements() as $element) { 2515 if ($element->hasErrors()) { 2516 $errors = true; 2517 break; 2518 } 2519 } 2520 2521 /** @var Zend_Form_SubForm $subForm */ 2522 foreach ($this->getSubForms() as $subForm) { 2523 if ($subForm->hasErrors()) { 2524 $errors = true; 2525 break; 2526 } 2527 } 2528 } 2529 2530 return $errors; 2531 } 2532 2533 /** 2534 * Get error codes for all elements failing validation 2535 * 2536 * @param string $name 2537 * @param bool $suppressArrayNotation 2538 * @return array 2539 */ 2540 public function getErrors($name = null, $suppressArrayNotation = false) 2541 { 2542 $errors = array(); 2543 if (null !== $name) { 2544 if (isset($this->_elements[$name])) { 2545 return $this->getElement($name)->getErrors(); 2546 } else if (isset($this->_subForms[$name])) { 2547 return $this->getSubForm($name)->getErrors(null, true); 2548 } 2549 } 2550 2551 /** @var Zend_Form_Element $element */ 2552 foreach ($this->_elements as $key => $element) { 2553 $errors[$key] = $element->getErrors(); 2554 } 2555 /** @var Zend_Form_SubForm $subForm */ 2556 foreach ($this->getSubForms() as $key => $subForm) { 2557 $merge = array(); 2558 if (!$subForm->isArray()) { 2559 $merge[$key] = $subForm->getErrors(); 2560 } else { 2561 $merge = $this->_attachToArray($subForm->getErrors(null, true), 2562 $subForm->getElementsBelongTo()); 2563 } 2564 $errors = $this->_array_replace_recursive($errors, $merge); 2565 } 2566 2567 if (!$suppressArrayNotation && 2568 $this->isArray() && 2569 !$this->_getIsRendered()) { 2570 $errors = $this->_attachToArray($errors, $this->getElementsBelongTo()); 2571 } 2572 2573 return $errors; 2574 } 2575 2576 /** 2577 * Retrieve error messages from elements failing validations 2578 * 2579 * @param string $name 2580 * @param bool $suppressArrayNotation 2581 * @return array 2582 */ 2583 public function getMessages($name = null, $suppressArrayNotation = false) 2584 { 2585 if (null !== $name) { 2586 if (isset($this->_elements[$name])) { 2587 return $this->getElement($name)->getMessages(); 2588 } else if (isset($this->_subForms[$name])) { 2589 return $this->getSubForm($name)->getMessages(null, true); 2590 } 2591 /** @var Zend_Form_SubForm $subForm */ 2592 foreach ($this->getSubForms() as $key => $subForm) { 2593 if ($subForm->isArray()) { 2594 $belongTo = $subForm->getElementsBelongTo(); 2595 if ($name == $this->_getArrayName($belongTo)) { 2596 return $subForm->getMessages(null, true); 2597 } 2598 } 2599 } 2600 } 2601 2602 $customMessages = $this->_getErrorMessages(); 2603 if ($this->isErrors() && !empty($customMessages)) { 2604 return $customMessages; 2605 } 2606 2607 $messages = array(); 2608 2609 /** @var Zend_Form_Element $element */ 2610 foreach ($this->getElements() as $name => $element) { 2611 $eMessages = $element->getMessages(); 2612 if (!empty($eMessages)) { 2613 $messages[$name] = $eMessages; 2614 } 2615 } 2616 2617 /** @var Zend_Form_SubForm $subForm */ 2618 foreach ($this->getSubForms() as $key => $subForm) { 2619 $merge = $subForm->getMessages(null, true); 2620 if (!empty($merge)) { 2621 if (!$subForm->isArray()) { 2622 $merge = array($key => $merge); 2623 } else { 2624 $merge = $this->_attachToArray($merge, 2625 $subForm->getElementsBelongTo()); 2626 } 2627 $messages = $this->_array_replace_recursive($messages, $merge); 2628 } 2629 } 2630 2631 if (!$suppressArrayNotation && 2632 $this->isArray() && 2633 !$this->_getIsRendered()) { 2634 $messages = $this->_attachToArray($messages, $this->getElementsBelongTo()); 2635 } 2636 2637 return $messages; 2638 } 2639 2640 /** 2641 * Retrieve translated custom error messages 2642 * Proxies to {@link _getErrorMessages()}. 2643 * 2644 * @return array 2645 */ 2646 public function getCustomMessages() 2647 { 2648 return $this->_getErrorMessages(); 2649 } 2650 2651 2652 // Rendering 2653 2654 /** 2655 * Set view object 2656 * 2657 * @param Zend_View_Interface $view 2658 * @return Zend_Form 2659 */ 2660 public function setView(Zend_View_Interface $view = null) 2661 { 2662 $this->_view = $view; 2663 return $this; 2664 } 2665 2666 /** 2667 * Retrieve view object 2668 * 2669 * If none registered, attempts to pull from ViewRenderer. 2670 * 2671 * @return Zend_View_Interface|null 2672 */ 2673 public function getView() 2674 { 2675 if (null === $this->_view) { 2676 $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer'); 2677 $this->setView($viewRenderer->view); 2678 } 2679 2680 return $this->_view; 2681 } 2682 2683 /** 2684 * Instantiate a decorator based on class name or class name fragment 2685 * 2686 * @param string $name 2687 * @param null|array $options 2688 * @return Zend_Form_Decorator_Interface 2689 */ 2690 protected function _getDecorator($name, $options) 2691 { 2692 $class = $this->getPluginLoader(self::DECORATOR)->load($name); 2693 if (null === $options) { 2694 $decorator = new $class; 2695 } else { 2696 $decorator = new $class($options); 2697 } 2698 2699 return $decorator; 2700 } 2701 2702 /** 2703 * Add a decorator for rendering the element 2704 * 2705 * @param string|Zend_Form_Decorator_Interface $decorator 2706 * @param array|Zend_Config $options Options with which to initialize decorator 2707 * @throws Zend_Form_Exception 2708 * @return Zend_Form 2709 */ 2710 public function addDecorator($decorator, $options = null) 2711 { 2712 if ($decorator instanceof Zend_Form_Decorator_Interface) { 2713 $name = get_class($decorator); 2714 } elseif (is_string($decorator)) { 2715 $name = $decorator; 2716 $decorator = array( 2717 'decorator' => $name, 2718 'options' => $options, 2719 ); 2720 } elseif (is_array($decorator)) { 2721 foreach ($decorator as $name => $spec) { 2722 break; 2723 } 2724 if (is_numeric($name)) { 2725 throw new Zend_Form_Exception('Invalid alias provided to addDecorator; must be alphanumeric string'); 2726 } 2727 if (is_string($spec)) { 2728 $decorator = array( 2729 'decorator' => $spec, 2730 'options' => $options, 2731 ); 2732 } elseif ($spec instanceof Zend_Form_Decorator_Interface) { 2733 $decorator = $spec; 2734 } 2735 } else { 2736 throw new Zend_Form_Exception('Invalid decorator provided to addDecorator; must be string or Zend_Form_Decorator_Interface'); 2737 } 2738 2739 $this->_decorators[$name] = $decorator; 2740 2741 return $this; 2742 } 2743 2744 /** 2745 * Add many decorators at once 2746 * 2747 * @param array $decorators 2748 * @throws Zend_Form_Exception 2749 * @return Zend_Form 2750 */ 2751 public function addDecorators(array $decorators) 2752 { 2753 foreach ($decorators as $decoratorName => $decoratorInfo) { 2754 if (is_string($decoratorInfo) || 2755 $decoratorInfo instanceof Zend_Form_Decorator_Interface) { 2756 if (!is_numeric($decoratorName)) { 2757 $this->addDecorator(array($decoratorName => $decoratorInfo)); 2758 } else { 2759 $this->addDecorator($decoratorInfo); 2760 } 2761 } elseif (is_array($decoratorInfo)) { 2762 $argc = count($decoratorInfo); 2763 $options = array(); 2764 if (isset($decoratorInfo['decorator'])) { 2765 $decorator = $decoratorInfo['decorator']; 2766 if (isset($decoratorInfo['options'])) { 2767 $options = $decoratorInfo['options']; 2768 } 2769 $this->addDecorator($decorator, $options); 2770 } else { 2771 switch (true) { 2772 case (0 == $argc): 2773 break; 2774 case (1 <= $argc): 2775 $decorator = array_shift($decoratorInfo); 2776 case (2 <= $argc): 2777 $options = array_shift($decoratorInfo); 2778 default: 2779 $this->addDecorator($decorator, $options); 2780 break; 2781 } 2782 } 2783 } else { 2784 throw new Zend_Form_Exception('Invalid decorator passed to addDecorators()'); 2785 } 2786 } 2787 2788 return $this; 2789 } 2790 2791 /** 2792 * Overwrite all decorators 2793 * 2794 * @param array $decorators 2795 * @return Zend_Form 2796 */ 2797 public function setDecorators(array $decorators) 2798 { 2799 $this->clearDecorators(); 2800 return $this->addDecorators($decorators); 2801 } 2802 2803 /** 2804 * Retrieve a registered decorator 2805 * 2806 * @param string $name 2807 * @return false|Zend_Form_Decorator_Abstract 2808 */ 2809 public function getDecorator($name) 2810 { 2811 if (!isset($this->_decorators[$name])) { 2812 $len = strlen($name); 2813 foreach ($this->_decorators as $localName => $decorator) { 2814 if ($len > strlen($localName)) { 2815 continue; 2816 } 2817 2818 if (0 === substr_compare($localName, $name, -$len, $len, true)) { 2819 if (is_array($decorator)) { 2820 return $this->_loadDecorator($decorator, $localName); 2821 } 2822 return $decorator; 2823 } 2824 } 2825 return false; 2826 } 2827 2828 if (is_array($this->_decorators[$name])) { 2829 return $this->_loadDecorator($this->_decorators[$name], $name); 2830 } 2831 2832 return $this->_decorators[$name]; 2833 } 2834 2835 /** 2836 * Retrieve all decorators 2837 * 2838 * @return array 2839 */ 2840 public function getDecorators() 2841 { 2842 foreach ($this->_decorators as $key => $value) { 2843 if (is_array($value)) { 2844 $this->_loadDecorator($value, $key); 2845 } 2846 } 2847 return $this->_decorators; 2848 } 2849 2850 /** 2851 * Remove a single decorator 2852 * 2853 * @param string $name 2854 * @return bool 2855 */ 2856 public function removeDecorator($name) 2857 { 2858 $decorator = $this->getDecorator($name); 2859 if ($decorator) { 2860 if (array_key_exists($name, $this->_decorators)) { 2861 unset($this->_decorators[$name]); 2862 } else { 2863 $class = get_class($decorator); 2864 if (!array_key_exists($class, $this->_decorators)) { 2865 return false; 2866 } 2867 unset($this->_decorators[$class]); 2868 } 2869 return true; 2870 } 2871 2872 return false; 2873 } 2874 2875 /** 2876 * Clear all decorators 2877 * 2878 * @return Zend_Form 2879 */ 2880 public function clearDecorators() 2881 { 2882 $this->_decorators = array(); 2883 return $this; 2884 } 2885 2886 /** 2887 * Set all element decorators as specified 2888 * 2889 * @param array $decorators 2890 * @param array|null $elements Specific elements to decorate or exclude from decoration 2891 * @param bool $include Whether $elements is an inclusion or exclusion list 2892 * @return Zend_Form 2893 */ 2894 public function setElementDecorators(array $decorators, array $elements = null, $include = true) 2895 { 2896 if (is_array($elements)) { 2897 if ($include) { 2898 $elementObjs = array(); 2899 foreach ($elements as $name) { 2900 if (null !== ($element = $this->getElement($name))) { 2901 $elementObjs[] = $element; 2902 } 2903 } 2904 } else { 2905 $elementObjs = $this->getElements(); 2906 foreach ($elements as $name) { 2907 if (array_key_exists($name, $elementObjs)) { 2908 unset($elementObjs[$name]); 2909 } 2910 } 2911 } 2912 } else { 2913 $elementObjs = $this->getElements(); 2914 } 2915 2916 /** @var Zend_Form_Element $element */ 2917 foreach ($elementObjs as $element) { 2918 $element->setDecorators($decorators); 2919 } 2920 2921 $this->_elementDecorators = $decorators; 2922 2923 return $this; 2924 } 2925 2926 /** 2927 * Set all display group decorators as specified 2928 * 2929 * @param array $decorators 2930 * @return Zend_Form 2931 */ 2932 public function setDisplayGroupDecorators(array $decorators) 2933 { 2934 /** @var Zend_Form_DisplayGroup $group */ 2935 foreach ($this->getDisplayGroups() as $group) { 2936 $group->setDecorators($decorators); 2937 } 2938 2939 return $this; 2940 } 2941 2942 /** 2943 * Set all subform decorators as specified 2944 * 2945 * @param array $decorators 2946 * @return Zend_Form 2947 */ 2948 public function setSubFormDecorators(array $decorators) 2949 { 2950 /** @var Zend_Form_SubForm $form */ 2951 foreach ($this->getSubForms() as $form) { 2952 $form->setDecorators($decorators); 2953 } 2954 2955 return $this; 2956 } 2957 2958 /** 2959 * Render form 2960 * 2961 * @param Zend_View_Interface $view 2962 * @return string 2963 */ 2964 public function render(Zend_View_Interface $view = null) 2965 { 2966 if (null !== $view) { 2967 $this->setView($view); 2968 } 2969 2970 $content = ''; 2971 /** @var Zend_Form_Decorator_Abstract $decorator */ 2972 foreach ($this->getDecorators() as $decorator) { 2973 $decorator->setElement($this); 2974 $content = $decorator->render($content); 2975 } 2976 $this->_setIsRendered(); 2977 return $content; 2978 } 2979 2980 /** 2981 * Serialize as string 2982 * 2983 * Proxies to {@link render()}. 2984 * 2985 * @return string 2986 */ 2987 public function __toString() 2988 { 2989 try { 2990 $return = $this->render(); 2991 return $return; 2992 } catch (Exception $e) { 2993 $message = "Exception caught by form: " . $e->getMessage() 2994 . "\nStack Trace:\n" . $e->getTraceAsString(); 2995 trigger_error($message, E_USER_WARNING); 2996 return ''; 2997 } 2998 } 2999 3000 3001 // Localization: 3002 3003 /** 3004 * Set translator object 3005 * 3006 * @param Zend_Translate|Zend_Translate_Adapter|null $translator 3007 * @throws Zend_Form_Exception 3008 * @return Zend_Form 3009 */ 3010 public function setTranslator($translator = null) 3011 { 3012 if (null === $translator) { 3013 $this->_translator = null; 3014 } elseif ($translator instanceof Zend_Translate_Adapter) { 3015 $this->_translator = $translator; 3016 } elseif ($translator instanceof Zend_Translate) { 3017 $this->_translator = $translator->getAdapter(); 3018 } else { 3019 throw new Zend_Form_Exception('Invalid translator specified'); 3020 } 3021 3022 return $this; 3023 } 3024 3025 /** 3026 * Set global default translator object 3027 * 3028 * @param Zend_Translate|Zend_Translate_Adapter|null $translator 3029 * @throws Zend_Form_Exception 3030 * @return void 3031 */ 3032 public static function setDefaultTranslator($translator = null) 3033 { 3034 if (null === $translator) { 3035 self::$_translatorDefault = null; 3036 } elseif ($translator instanceof Zend_Translate_Adapter) { 3037 self::$_translatorDefault = $translator; 3038 } elseif ($translator instanceof Zend_Translate) { 3039 self::$_translatorDefault = $translator->getAdapter(); 3040 } else { 3041 throw new Zend_Form_Exception('Invalid translator specified'); 3042 } 3043 } 3044 3045 /** 3046 * Retrieve translator object 3047 * 3048 * @return Zend_Translate|null 3049 */ 3050 public function getTranslator() 3051 { 3052 if ($this->translatorIsDisabled()) { 3053 return null; 3054 } 3055 3056 if (null === $this->_translator) { 3057 return self::getDefaultTranslator(); 3058 } 3059 3060 return $this->_translator; 3061 } 3062 3063 /** 3064 * Does this form have its own specific translator? 3065 * 3066 * @return bool 3067 */ 3068 public function hasTranslator() 3069 { 3070 return (bool)$this->_translator; 3071 } 3072 3073 /** 3074 * Get global default translator object 3075 * 3076 * @return null|Zend_Translate 3077 */ 3078 public static function getDefaultTranslator() 3079 { 3080 if (null === self::$_translatorDefault) { 3081 if (Zend_Registry::isRegistered('Zend_Translate')) { 3082 $translator = Zend_Registry::get('Zend_Translate'); 3083 if ($translator instanceof Zend_Translate_Adapter) { 3084 return $translator; 3085 } elseif ($translator instanceof Zend_Translate) { 3086 return $translator->getAdapter(); 3087 } 3088 } 3089 } 3090 return self::$_translatorDefault; 3091 } 3092 3093 /** 3094 * Is there a default translation object set? 3095 * 3096 * @return boolean 3097 */ 3098 public static function hasDefaultTranslator() 3099 { 3100 return (bool)self::$_translatorDefault; 3101 } 3102 3103 /** 3104 * Indicate whether or not translation should be disabled 3105 * 3106 * @param bool $flag 3107 * @return Zend_Form 3108 */ 3109 public function setDisableTranslator($flag) 3110 { 3111 $this->_translatorDisabled = (bool) $flag; 3112 return $this; 3113 } 3114 3115 /** 3116 * Is translation disabled? 3117 * 3118 * @return bool 3119 */ 3120 public function translatorIsDisabled() 3121 { 3122 return $this->_translatorDisabled; 3123 } 3124 3125 /** 3126 * Overloading: access to elements, form groups, and display groups 3127 * 3128 * @param string $name 3129 * @return Zend_Form_Element|Zend_Form|null 3130 */ 3131 public function __get($name) 3132 { 3133 if (isset($this->_elements[$name])) { 3134 return $this->_elements[$name]; 3135 } elseif (isset($this->_subForms[$name])) { 3136 return $this->_subForms[$name]; 3137 } elseif (isset($this->_displayGroups[$name])) { 3138 return $this->_displayGroups[$name]; 3139 } 3140 3141 return null; 3142 } 3143 3144 /** 3145 * Overloading: access to elements, form groups, and display groups 3146 * 3147 * @param string $name 3148 * @param Zend_Form_Element|Zend_Form $value 3149 * @return void 3150 * @throws Zend_Form_Exception for invalid $value 3151 */ 3152 public function __set($name, $value) 3153 { 3154 if ($value instanceof Zend_Form_Element) { 3155 $this->addElement($value, $name); 3156 return; 3157 } elseif ($value instanceof Zend_Form) { 3158 $this->addSubForm($value, $name); 3159 return; 3160 } elseif (is_array($value)) { 3161 $this->addDisplayGroup($value, $name); 3162 return; 3163 } 3164 3165 if (is_object($value)) { 3166 $type = get_class($value); 3167 } else { 3168 $type = gettype($value); 3169 } 3170 throw new Zend_Form_Exception('Only form elements and groups may be overloaded; variable of type "' . $type . '" provided'); 3171 } 3172 3173 /** 3174 * Overloading: access to elements, form groups, and display groups 3175 * 3176 * @param string $name 3177 * @return boolean 3178 */ 3179 public function __isset($name) 3180 { 3181 if (isset($this->_elements[$name]) 3182 || isset($this->_subForms[$name]) 3183 || isset($this->_displayGroups[$name])) 3184 { 3185 return true; 3186 } 3187 3188 return false; 3189 } 3190 3191 /** 3192 * Overloading: access to elements, form groups, and display groups 3193 * 3194 * @param string $name 3195 * @return void 3196 */ 3197 public function __unset($name) 3198 { 3199 if (isset($this->_elements[$name])) { 3200 unset($this->_elements[$name]); 3201 } elseif (isset($this->_subForms[$name])) { 3202 unset($this->_subForms[$name]); 3203 } elseif (isset($this->_displayGroups[$name])) { 3204 unset($this->_displayGroups[$name]); 3205 } 3206 } 3207 3208 /** 3209 * Overloading: allow rendering specific decorators 3210 * 3211 * Call renderDecoratorName() to render a specific decorator. 3212 * 3213 * @param string $method 3214 * @param array $args 3215 * @return string 3216 * @throws Zend_Form_Exception for invalid decorator or invalid method call 3217 */ 3218 public function __call($method, $args) 3219 { 3220 if ('render' == substr($method, 0, 6)) { 3221 $decoratorName = substr($method, 6); 3222 if (false !== ($decorator = $this->getDecorator($decoratorName))) { 3223 $decorator->setElement($this); 3224 $seed = ''; 3225 if (0 < count($args)) { 3226 $seed = array_shift($args); 3227 } 3228 if ($decoratorName === 'FormElements' || 3229 $decoratorName === 'PrepareElements') { 3230 $this->_setIsRendered(); 3231 } 3232 return $decorator->render($seed); 3233 } 3234 3235 throw new Zend_Form_Exception(sprintf('Decorator by name %s does not exist', $decoratorName)); 3236 } 3237 3238 throw new Zend_Form_Exception(sprintf('Method %s does not exist', $method)); 3239 } 3240 3241 // Interfaces: Iterator, Countable 3242 3243 /** 3244 * Current element/subform/display group 3245 * 3246 * @throws Zend_Form_Exception 3247 * @return Zend_Form_Element|Zend_Form_DisplayGroup|Zend_Form 3248 */ 3249 public function current() 3250 { 3251 $this->_sort(); 3252 current($this->_order); 3253 $key = key($this->_order); 3254 3255 if (isset($this->_elements[$key])) { 3256 return $this->getElement($key); 3257 } elseif (isset($this->_subForms[$key])) { 3258 return $this->getSubForm($key); 3259 } elseif (isset($this->_displayGroups[$key])) { 3260 return $this->getDisplayGroup($key); 3261 } else { 3262 throw new Zend_Form_Exception(sprintf('Corruption detected in form; invalid key ("%s") found in internal iterator', (string) $key)); 3263 } 3264 } 3265 3266 /** 3267 * Current element/subform/display group name 3268 * 3269 * @return string 3270 */ 3271 public function key() 3272 { 3273 $this->_sort(); 3274 return key($this->_order); 3275 } 3276 3277 /** 3278 * Move pointer to next element/subform/display group 3279 * 3280 * @return void 3281 */ 3282 public function next() 3283 { 3284 $this->_sort(); 3285 next($this->_order); 3286 } 3287 3288 /** 3289 * Move pointer to beginning of element/subform/display group loop 3290 * 3291 * @return void 3292 */ 3293 public function rewind() 3294 { 3295 $this->_sort(); 3296 reset($this->_order); 3297 } 3298 3299 /** 3300 * Determine if current element/subform/display group is valid 3301 * 3302 * @return bool 3303 */ 3304 public function valid() 3305 { 3306 $this->_sort(); 3307 return (current($this->_order) !== false); 3308 } 3309 3310 /** 3311 * Count of elements/subforms that are iterable 3312 * 3313 * @return int 3314 */ 3315 public function count() 3316 { 3317 return count($this->_order); 3318 } 3319 3320 /** 3321 * Set flag to disable loading default decorators 3322 * 3323 * @param bool $flag 3324 * @return Zend_Form 3325 */ 3326 public function setDisableLoadDefaultDecorators($flag) 3327 { 3328 $this->_disableLoadDefaultDecorators = (bool) $flag; 3329 return $this; 3330 } 3331 3332 /** 3333 * Should we load the default decorators? 3334 * 3335 * @return bool 3336 */ 3337 public function loadDefaultDecoratorsIsDisabled() 3338 { 3339 return $this->_disableLoadDefaultDecorators; 3340 } 3341 3342 /** 3343 * Load the default decorators 3344 * 3345 * @return Zend_Form 3346 */ 3347 public function loadDefaultDecorators() 3348 { 3349 if ($this->loadDefaultDecoratorsIsDisabled()) { 3350 return $this; 3351 } 3352 3353 $decorators = $this->getDecorators(); 3354 if (empty($decorators)) { 3355 $this->addDecorator('FormElements') 3356 ->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')) 3357 ->addDecorator('Form'); 3358 } 3359 return $this; 3360 } 3361 3362 /** 3363 * Remove an element from iteration 3364 * 3365 * @param string $name Element/group/form name 3366 * @return void 3367 */ 3368 public function removeFromIteration($name) 3369 { 3370 if (array_key_exists($name, $this->_order)) { 3371 unset($this->_order[$name]); 3372 $this->_orderUpdated = true; 3373 } 3374 } 3375 3376 /** 3377 * Sort items according to their order 3378 * 3379 * @throws Zend_Form_Exception 3380 * @return void 3381 */ 3382 protected function _sort() 3383 { 3384 if ($this->_orderUpdated) { 3385 $items = array(); 3386 $index = 0; 3387 foreach ($this->_order as $key => $order) { 3388 if (null === $order) { 3389 if (null === ($order = $this->{$key}->getOrder())) { 3390 while (array_search($index, $this->_order, true)) { 3391 ++$index; 3392 } 3393 $items[$index] = $key; 3394 ++$index; 3395 } else { 3396 $items[$order] = $key; 3397 } 3398 } elseif (isset($items[$order]) && $items[$order] !== $key) { 3399 throw new Zend_Form_Exception('Form elements ' . 3400 $items[$order] . ' and ' . $key . 3401 ' have the same order (' . 3402 $order . ') - ' . 3403 'this would result in only the last added element to be rendered' 3404 ); 3405 } else { 3406 $items[$order] = $key; 3407 } 3408 } 3409 3410 $items = array_flip($items); 3411 asort($items); 3412 $this->_order = $items; 3413 $this->_orderUpdated = false; 3414 } 3415 } 3416 3417 /** 3418 * Lazy-load a decorator 3419 * 3420 * @param array $decorator Decorator type and options 3421 * @param mixed $name Decorator name or alias 3422 * @return Zend_Form_Decorator_Interface 3423 */ 3424 protected function _loadDecorator(array $decorator, $name) 3425 { 3426 $sameName = false; 3427 if ($name == $decorator['decorator']) { 3428 $sameName = true; 3429 } 3430 3431 $instance = $this->_getDecorator($decorator['decorator'], $decorator['options']); 3432 if ($sameName) { 3433 $newName = get_class($instance); 3434 $decoratorNames = array_keys($this->_decorators); 3435 $order = array_flip($decoratorNames); 3436 $order[$newName] = $order[$name]; 3437 $decoratorsExchange = array(); 3438 unset($order[$name]); 3439 asort($order); 3440 foreach ($order as $key => $index) { 3441 if ($key == $newName) { 3442 $decoratorsExchange[$key] = $instance; 3443 continue; 3444 } 3445 $decoratorsExchange[$key] = $this->_decorators[$key]; 3446 } 3447 $this->_decorators = $decoratorsExchange; 3448 } else { 3449 $this->_decorators[$name] = $instance; 3450 } 3451 3452 return $instance; 3453 } 3454 3455 /** 3456 * Retrieve optionally translated custom error messages 3457 * 3458 * @return array 3459 */ 3460 protected function _getErrorMessages() 3461 { 3462 $messages = $this->getErrorMessages(); 3463 $translator = $this->getTranslator(); 3464 if (null !== $translator) { 3465 foreach ($messages as $key => $message) { 3466 $messages[$key] = $translator->translate($message); 3467 } 3468 } 3469 return $messages; 3470 } 3471} 3472