[Pieforms-commit] SF.net SVN: pieforms: [254] pieforms-php5/trunk/src/pieform.php
Status: Alpha
Brought to you by:
oracleshinoda
From: <ora...@us...> - 2007-12-31 02:00:10
|
Revision: 254 http://pieforms.svn.sourceforge.net/pieforms/?rev=254&view=rev Author: oracleshinoda Date: 2007-12-30 18:00:15 -0800 (Sun, 30 Dec 2007) Log Message: ----------- A new datastructure to speed up and simplify looping through elements. Added 'elementrefs' structure, containing references to all elements in the form, minus fieldsets. Changed calls to get_elements to use elementrefs. Simplified a bunch of code using this. For example, a whole bunch of duplicated checking in the constructor has been consolidated now that it's easier to loop through all elements without having to take fieldsets into account. Modified Paths: -------------- pieforms-php5/trunk/src/pieform.php Modified: pieforms-php5/trunk/src/pieform.php =================================================================== --- pieforms-php5/trunk/src/pieform.php 2007-12-31 01:59:52 UTC (rev 253) +++ pieforms-php5/trunk/src/pieform.php 2007-12-31 02:00:15 UTC (rev 254) @@ -123,6 +123,15 @@ private $data = array(); /** + * A hash of references to the elements of the form, not including + * fieldsets (although including all elements inside any fieldsets. Used internally + * for simplifying looping over elements + * + * @var array + */ + private $elementrefs = array(); + + /** * Whether this form includes a file element. If so, the enctype attribute * for the form will be specified as "multipart/mixed" as required. This * is auto-detected by the Pieform class. @@ -328,165 +337,123 @@ throw new PieformException('Forms must have a list of elements'); } - // Rename all the keys to have nice compliant names - // @todo: - // - This isn't done for elements inside fieldsets - // - There's no easy way for other things do do all this preprocessing if they - // need. It should be a method so that other things (like multirecord) - // can use it. - $elements = array(); - foreach ($this->data['elements'] as $name => $element) { - $newname = preg_replace('/[^a-zA-Z0-9_]/', '_', $name); - if (isset($elements[$name])) { - throw new PieformException('Element "' . $name . '" has a dangerous name that interferes with another element'); + // Get references to all the elements in the form, excluding fieldsets + foreach ($this->data['elements'] as $name => &$_element) { + if (isset($_element['type']) && $_element['type'] == 'fieldset') { + // Load the fieldset plugin as we know this form has one now + $this->include_plugin('element', 'fieldset'); + if ($this->get_property('template')) { + self::info("Your form '$this->name' has a fieldset, but is using a template. Fieldsets make no sense when using templates"); + } + + foreach ($_element['elements'] as $subname => &$_subelement) { + $this->elementrefs[$subname] = &$_subelement; + } } - $elements[$newname] = $element; + else { + $this->elementrefs[$name] = &$_element; + } } - $this->data['elements'] = $elements; + // Check that all elements have names compliant to PHP's variable naming policy + // (otherwise things get messy later) + foreach (array_keys($this->elementrefs) as $name) { + if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $name)) { + throw new PieformException('Element "' . $name . '" is badly named (validity test: could you give a PHP variable the name?)'); + } + } + // Remove elements to ignore + // This can't be done using $this->elementrefs, because you can't unset + // an entry in there and have it unset the entry in $this->data['elements'] foreach ($this->data['elements'] as $name => $element) { if (isset($element['type']) && $element['type'] == 'fieldset') { foreach ($element['elements'] as $subname => $subelement) { if (!empty($subelement['ignore'])) { unset ($this->data['elements'][$name]['elements'][$subname]); + unset($this->elementrefs[$subname]); } } } else { if (!empty($element['ignore'])) { unset($this->data['elements'][$name]); + unset($this->elementrefs[$name]); } } } // Set some attributes for all elements $autofocusadded = false; - foreach ($this->data['elements'] as $name => &$element) { + foreach ($this->elementrefs as $name => &$element) { // @todo re-check ordering of this section - // The name can be in the element itself. This is compatibility for the perl version - if (isset($element['name'])) { - $name = $element['name']; - } if (count($element) == 0) { throw new PieformException('An element in form "' . $this->name . '" has no data (' . $name . ')'); } - if (!isset($element['type'])) { + + if (!isset($element['type']) || $element['type'] == 'markup') { $element['type'] = 'markup'; if (!isset($element['value'])) { throw new PieformException('The markup element "' . $name . '" has no value'); } } - if (!isset($element['title'])) { - $element['title'] = ''; - } - if ($element['type'] == 'file') { - $this->fileupload = true; - if ($this->data['method'] == 'get') { - $this->data['method'] = 'post'; - self::info("Your form '$this->name' had the method 'get' and also a file element - it has been converted to 'post'"); + else { + // Now we know what type the element is, we can load the plugin for it + $this->include_plugin('element', $element['type']); + + // The name can be in the element itself. This is compatibility for the perl version + if (isset($element['name'])) { + $name = $element['name']; } - } - if ($element['type'] == 'fieldset') { - if ($this->get_property('template')) { - self::info("Your form '$this->name' has a fieldset, but is using a template. Fieldsets make no sense when using templates"); + + // All elements should have at least the title key set + if (!isset($element['title'])) { + $element['title'] = ''; } - $this->include_plugin('element', 'fieldset'); - foreach ($element['elements'] as $subname => &$subelement) { - // The name can be in the element itself. This is compatibility for the perl version - if (isset($subelement['name'])) { - $subname = $subelement['name']; - } - if (count($subelement) == 0) { - throw new PieformException('An element in form "' . $this->name . '" has no data (' . $subname . ')'); - } - if (!isset($subelement['type'])) { - $subelement['type'] = 'markup'; - if (!isset($subelement['value'])) { - throw new PieformException('The markup element "' - . $name . '" has no value'); - } - } - // Configure some basics for real elements - if ($subelement['type'] != 'markup') { - // This function can be defined by the application using Pieforms, - // and applies to all elements of this type - $function = 'pieform_element_' . $subelement['type'] . '_configure'; - if (function_exists($function)) { - $subelement = $function($subelement); - } - - // This function is defined by the plugin itself, to set fields on - // the element that need to be set but should not be set by the - // application - $function = 'pieform_element_' . $subelement['type'] . '_set_attributes'; - $this->include_plugin('element', $subelement['type']); - if (function_exists($function)) { - $subelement = $function($subelement); - } - - // Add the autofocus flag to the element if required - if (!$autofocusadded && $this->data['autofocus'] === true && empty($element['nofocus'])) { - $subelement['autofocus'] = true; - $autofocusadded = true; - } - else if (!empty($this->data['autofocus']) && $this->data['autofocus'] !== true - && $subname == $this->data['autofocus']) { - $subelement['autofocus'] = true; - } - - // All elements should have some kind of title - if (!isset($subelement['title'])) { - $subelement['title'] = ''; - } - - // Force the form method to post if there is a file to upload. - if ($subelement['type'] == 'file') { - $this->fileupload = true; - if ($this->data['method'] == 'get') { - $this->data['method'] = 'post'; - self::info("Your form '$this->name' had the method 'get' and also a file element - it has been converted to 'post'"); - } - } - - // All elements inherit the form tabindex - $subelement['tabindex'] = $this->data['tabindex']; + // Force the form method to post if there is a file to upload + if ($element['type'] == 'file') { + $this->fileupload = true; + if ($this->data['method'] == 'get') { + $this->data['method'] = 'post'; + self::info("Your form '$this->name' had the method 'get' and also a file element - it has been converted to 'post'"); } - $subelement['name'] = $subname; + } + // This function can be defined by the application using Pieforms, + // and applies to all elements of this type + $function = 'pieform_element_' . $element['type'] . '_configure'; + if (function_exists($function)) { + $element = $function($element); } - } - else { - // Let each element set and override attributes if necessary - if ($element['type'] != 'markup') { - $function = 'pieform_element_' . $element['type'] . '_configure'; - if (function_exists($function)) { - $element = $function($element); - } - $function = 'pieform_element_' . $element['type'] . '_set_attributes'; - $this->include_plugin('element', $element['type']); - if (function_exists($function)) { - $element = $function($element); - } + // vvv --------------------------------------------------- vvv + // After this point Pieforms can set or override attributes + // without fear that the developer will be able to change them. - // Add the autofocus flag to the element if required - if (!$autofocusadded && $this->data['autofocus'] === true && empty($element['nofocus'])) { - $element['autofocus'] = true; - $autofocusadded = true; - } - elseif (!empty($this->data['autofocus']) && $this->data['autofocus'] !== true - && $name == $this->data['autofocus']) { - $element['autofocus'] = true; - } + // This function is defined by the plugin itself, to set + // fields on the element that need to be set but should not + // be set by the application + $function = 'pieform_element_' . $element['type'] . '_set_attributes'; + if (function_exists($function)) { + $element = $function($element); + } - $element['tabindex'] = $this->data['tabindex']; + // Add the autofocus flag to the element if required + if (!$autofocusadded && $this->data['autofocus'] === true && empty($element['nofocus'])) { + $element['autofocus'] = true; + $autofocusadded = true; } - $element['name'] = $name; - } + elseif (!empty($this->data['autofocus']) && $this->data['autofocus'] !== true + && $name == $this->data['autofocus']) { + $element['autofocus'] = true; + } + // All elements inherit the form tabindex + $element['tabindex'] = $this->data['tabindex']; + } + $element['name'] = $name; } // Check if the form was submitted, and if so, validate and process it @@ -528,7 +495,7 @@ // Submit the form if things went OK if ($this->data['submit'] && !$this->has_errors()) { $submitted = false; - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if (!empty($element['submitelement']) && isset($global[$element['name']])) { $function = "{$this->data['successcallback']}_{$element['name']}"; if (function_exists($function)) { @@ -543,10 +510,8 @@ // Call the user defined function for processing a submit // This function should really redirect/exit after it has // finished processing the form. - // @todo maybe it should do just that... call_user_func_array($function, array($this, $values)); if ($this->data['dieaftersubmit']) { - // This will only work if I can make the login_submit function stuff work in login_validate if ($this->data['jsform']) { $message = 'Your ' . $this->name . '_submit function should use $form->json_reply to send a response'; } @@ -565,6 +530,8 @@ } } + // If we get here, the form was submitted but failed validation + // Auto focus the first element with an error if required if ($this->data['autofocus'] !== false) { $this->auto_focus_first_error(); @@ -642,9 +609,10 @@ }/*}}}*/ /** - * Builds and returns the HTML for the form, respecting the chosen renderer. + * Builds and returns the HTML for the form, using the chosen renderer or + * template * - * Note that the "action" attribute for the form tag are NOT HTML escaped + * Note that the "action" attribute for the form tag is NOT HTML escaped * for you. This allows you to build your own URLs, should you require. On * the other hand, this means you must be careful about escaping the URL, * especially if it has data from an external source in it. @@ -675,7 +643,7 @@ // $elements is a convenience variable that contains all of the form elements (minus fieldsets and // hidden elements) $elements = array(); - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if ($element['type'] != 'hidden') { $elements[$element['name']] = $element; } @@ -684,7 +652,7 @@ // Hidden elements $this->include_plugin('element', 'hidden'); $hidden_elements = ''; - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if ($element['type'] == 'hidden') { $hidden_elements .= pieform_element_hidden($this, $element); } @@ -743,7 +711,7 @@ // Hidden elements $this->include_plugin('element', 'hidden'); - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if ($element['type'] == 'hidden') { $result .= pieform_element_hidden($this, $element); } @@ -828,7 +796,7 @@ } return $elements; }/*}}}*/ - + /** * Returns the element with the given name. Throws a PieformException if the * element cannot be found. @@ -841,11 +809,10 @@ * @throws PieformException If the element could not be found */ public function get_element($name) {/*{{{*/ - foreach ($this->get_elements() as $element) { - if ($element['name'] == $name) { - return $element; - } + if (isset($this->elementrefs[$name])) { + return $this->elementrefs[$name]; } + throw new PieformException('Element "' . $name . '" cannot be found'); }/*}}}*/ @@ -864,7 +831,7 @@ private function get_submitted_values() {/*{{{*/ $result = array(); $global = ($this->data['method'] == 'get') ? $_GET : $_POST; - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if ($element['type'] != 'markup') { if ( (empty($element['submitelement']) && empty($element['cancelelement'])) || @@ -900,7 +867,7 @@ } // Perform rule validation - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if (isset($element['rules']) && is_array($element['rules'])) { foreach ($element['rules'] as $rule => $data) { if (!$this->get_error($element['name'])) { @@ -936,7 +903,7 @@ $result = "var {$this->name}_btn = null;\n"; $connecteventadded = false; - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if (!empty($element['submitelement'])) { if (!$connecteventadded) { $result .= "addLoadEvent(function() {\n"; @@ -1244,7 +1211,7 @@ * @return bool Whether there are errors with the form */ public function has_errors() {/*{{{*/ - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if (isset($element['error'])) { return true; } @@ -1371,7 +1338,7 @@ */ public function get_errors() {/*{{{*/ $result = array(); - foreach ($this->get_elements() as $element) { + foreach ($this->elementrefs as $element) { if (isset($element['error'])) { $result[] = $element; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |