Development

/branches/1.2/lib/plugins/sfCompat10Plugin/lib/util/sfFillInForm.class.php

You must first sign up to be able to contribute.

root/branches/1.2/lib/plugins/sfCompat10Plugin/lib/util/sfFillInForm.class.php

Revision 27415, 7.9 kB (checked in by FabianLange, 5 years ago)

[1.2, 1.3] fixed sfFillInForm so that it doesn't loose encoding information (fixes #3943)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  *
13  * @package    symfony
14  * @subpackage util
15  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
16  * @version    SVN: $Id$
17  */
18 class sfFillInForm
19 {
20   protected
21     $converters = array(),
22     $skipFields = array(),
23     $types      = array('text', 'checkbox', 'radio', 'hidden', 'password');
24
25   public function addConverter($callable, $fields)
26   {
27     foreach ((array) $fields as $field)
28     {
29       $this->converters[$field][] = $callable;
30     }
31   }
32
33   public function setSkipFields($fields)
34   {
35     $this->skipFields = $fields;
36   }
37
38   public function setTypes($types)
39   {
40     $this->types = $types;
41   }
42
43   /**
44    * fills in the values and returns HTML. This is a non validating tolerant mode.
45    *
46    * @return HTML with values filled in
47    */
48   public function fillInHtml($html, $formName, $formId, $values)
49   {
50     $dom = new DomDocument('1.0', sfConfig::get('sf_charset', 'UTF-8'));
51
52     $noHead = strpos($html,'<head') === false;
53     if ($noHead){
54       // loadHTML needs the conent-type meta for the charset
55       $html = '<meta http-equiv="Content-Type" content="text/html; charset='.sfConfig::get('sf_charset').'"/>'.$html;
56     }
57
58     @$dom->loadHTML($html);
59     $dom = $this->fillInDom($dom, $formName, $formId, $values);
60
61     if($noHead){
62       // remove the head element that was created by adding the meta tag.
63       $headElement = $dom->getElementsByTagName('head')->item(0);
64       if ($headElement)
65       {
66         $dom->getElementsByTagName('html')->item(0)->removeChild($headElement);
67       }
68     }
69     return $dom->saveHTML();
70   }
71
72   /**
73    * fills in the values and returns XHTML. This is same as XML but stripts the XML Prolog.
74    *
75    * @return XHTML without prolog with values filled in
76    */
77   public function fillInXhtml($xml, $formName, $formId, $values)
78   {
79     $xhtml = $this->fillInXml($xml, $formName, $formId, $values);
80     $prolog_regexp = '/^' . preg_quote('<?xml version="1.0" encoding="'.sfConfig::get('sf_charset', 'UTF-8').'"?>') . '\s*/';
81     return preg_replace($prolog_regexp, '', $xhtml);
82   }
83
84   /**
85    * fills in the values and returns XHTML. It can only work correctly on validating XHTML.
86    *
87    * @return XHTML including XML prolog with values filled in
88    */
89   public function fillInXml($xml, $formName, $formId, $values)
90   {
91     $dom = new DomDocument('1.0', sfConfig::get('sf_charset', 'UTF-8'));
92
93     // pages not having a doctype need it for loading
94     if (strpos($xml,'<!DOCTYPE') === false)
95     {
96       $xml = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '.
97              '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'.
98              $xml;
99     }
100
101     @$dom->loadXML($xml);
102     // loadXML will unset encoding if it was not defined in the source, no matter what the DomDocument constructor said
103     if($dom->encoding === null)
104     {
105       $dom->encoding = sfConfig::get('sf_charset', 'UTF-8');
106     }
107     $dom = $this->fillInDom($dom, $formName, $formId, $values);
108
109     return $dom->saveXML();
110   }
111
112   public function fillInDom($dom, $formName, $formId, $values)
113   {
114     $xpath = new DomXPath($dom);
115     if ($dom->documentElement && $dom->documentElement->namespaceURI)
116     {
117       $xpath->registerNamespace('xhtml', $dom->documentElement->namespaceURI);
118       $ns = 'xhtml:';
119     }
120     else
121     {
122       $ns = '';
123     }
124
125     // find our form
126     if ($formName)
127     {
128       $xpath_query = '//'.$ns.'form[@name="'.$formName.'"]';
129     }
130     elseif ($formId)
131     {
132       $xpath_query = '//'.$ns.'form[@id="'.$formId.'"]';
133     }
134     else
135     {
136       $xpath_query = '//'.$ns.'form';
137     }
138
139     $form = $xpath->query($xpath_query)->item(0);
140     if (!$form)
141     {
142       if (!$formName && !$formId)
143       {
144         throw new sfException('No form found in this page.');
145       }
146       else
147       {
148         throw new sfException(sprintf('The form "%s" cannot be found.', $formName ? $formName : $formId));
149       }
150     }
151
152     $query = 'descendant::'.$ns.'input[@name and (not(@type)';
153     foreach ($this->types as $type)
154     {
155       $query .= ' or @type="'.$type.'"';
156     }
157     $query .= ')] | descendant::'.$ns.'textarea[@name] | descendant::'.$ns.'select[@name]';
158
159     foreach ($xpath->query($query, $form) as $element)
160     {
161       $name  = (string) $element->getAttribute('name');
162       $value = (string) $element->getAttribute('value');
163       $type  = (string) $element->getAttribute('type');
164
165       // skip fields
166       if (!$this->hasValue($values, $name) || in_array($name, $this->skipFields))
167       {
168         continue;
169       }
170
171       if ($element->nodeName == 'input')
172       {
173         if ($type == 'checkbox' || $type == 'radio')
174         {
175           // checkbox and radio
176           $element->removeAttribute('checked');
177           if (is_array($this->getValue($values, $name)) && ($this->hasValue($values, $name) || !$element->hasAttribute('value')))
178           {
179             if (in_array($value, $this->getValue($values, $name)))
180             {
181               $element->setAttribute('checked', 'checked');
182             }
183           }
184           else if ($this->hasValue($values, $name) && ($this->getValue($values, $name) == $value || !$element->hasAttribute('value')))
185           {
186             $element->setAttribute('checked', 'checked');
187           }
188         }
189         else
190         {
191           // text input
192           $element->removeAttribute('value');
193           $element->setAttribute('value', $this->escapeValue($this->getValue($values, $name, true), $name));
194         }
195       }
196       else if ($element->nodeName == 'textarea')
197       {
198         $el = $element->cloneNode(false);
199         $el->appendChild($dom->createTextNode($this->escapeValue($this->getValue($values, $name, true), $name)));
200         $element->parentNode->replaceChild($el, $element);
201       }
202       else if ($element->nodeName == 'select')
203       {
204         // if the name contains [] it is part of an array that needs to be shifted
205         $value    = $this->getValue($values, $name, strpos($name,'[]') !== false);
206         $multiple = $element->hasAttribute('multiple');
207         foreach ($xpath->query('descendant::'.$ns.'option', $element) as $option)
208         {
209           $option->removeAttribute('selected');
210           if ($multiple && is_array($value))
211           {
212             if (in_array($option->getAttribute('value'), $value))
213             {
214               $option->setAttribute('selected', 'selected');
215             }
216           }
217           else if ($value == $option->getAttribute('value'))
218           {
219             $option->setAttribute('selected', 'selected');
220           }
221         }
222       }
223     }
224
225     return $dom;
226   }
227
228   protected function hasValue($values, $name)
229   {
230     if (array_key_exists($name, $values))
231     {
232       return true;
233     }
234
235     return sfToolkit::hasArrayValueForPath($values, $name);
236   }
237
238   // use reference to values so that arrays can be shifted.
239   protected function getValue(&$values, $name, $shiftArray = false)
240   {
241     if (array_key_exists($name, $values))
242     {
243       $return = &$values[$name];
244     } else {
245       $return = &sfToolkit::getArrayValueForPathByRef($values, $name);
246     }
247
248     if ($shiftArray && is_array($return))
249     {
250       // we need to remove the first element from the array. Therefore we need a reference
251       return array_shift($return);
252     }
253     return $return;
254   }
255
256   protected function escapeValue($value, $name)
257   {
258     if (function_exists('iconv') && strtolower(sfConfig::get('sf_charset')) != 'utf-8')
259     {
260       $new_value = iconv(sfConfig::get('sf_charset'), 'UTF-8', $value);
261       if (false !== $new_value)
262       {
263         $value = $new_value;
264       }
265     }
266
267     if (isset($this->converters[$name]))
268     {
269       foreach ($this->converters[$name] as $callable)
270       {
271         $value = call_user_func($callable, $value);
272       }
273     }
274
275     return $value;
276   }
277 }
278
Note: See TracBrowser for help on using the browser.