Completed
Pull Request — master (#203)
by
unknown
12:17
created

XoopsForm::XoopsForm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 9.4285
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 20.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * XOOPS Form Class
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
13
 * @license             GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             kernel
15
 * @subpackage          form
16
 * @since               2.0.0
17
 * @author              Kazumi Ono (AKA onokazu) http://www.myweb.ne.jp/, http://jp.xoops.org/
18
 * @author              Taiwen Jiang <[email protected]>
19
 */
20
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
21
22
/**
23
 * Abstract base class for forms
24
 *
25
 * @author         Kazumi Ono <[email protected]>
26
 * @author         Taiwen Jiang <[email protected]>
27
 * @package        kernel
28
 * @subpackage     form
29
 * @access         public
30
 */
31
class XoopsForm
32
{
33
    /**
34
     * *#@+
35
     *
36
     * @access private
37
     */
38
    /**
39
     * "action" attribute for the html form
40
     *
41
     * @var string
42
     */
43
    public $_action;
44
45
    /**
46
     * "method" attribute for the form.
47
     *
48
     * @var string
49
     */
50
    public $_method;
51
52
    /**
53
     * "name" attribute of the form
54
     *
55
     * @var string
56
     */
57
    public $_name;
58
59
    /**
60
     * title for the form
61
     *
62
     * @var string
63
     */
64
    public $_title;
65
66
    /**
67
     * summary for the form (WGAC2 Requirement)
68
     *
69
     * @var string
70
     */
71
    public $_summary = '';
72
73
    /**
74
     * array of {@link XoopsFormElement} objects
75
     *
76
     * @var array
77
     */
78
    public $_elements = array();
79
80
    /**
81
     * extra information for the <form> tag
82
     *
83
     * @var array
84
     */
85
    public $_extra = array();
86
87
    /**
88
     * required elements
89
     *
90
     * @var array
91
     */
92
    public $_required = array();
93
94
    /**
95
     * additional serialised object checksum (ERM Analysis - Requirement)
96
     * @deprecated
97
     * @access private
98
     */
99
    public $_objid = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
100
101
    /**
102
     * *#@-
103
     */
104
105
    /**
106
     * constructor
107
     *
108
     * @param string $title    title of the form
109
     * @param string $name     "name" attribute for the <form> tag
110
     * @param string $action   "action" attribute for the <form> tag
111
     * @param string $method   "method" attribute for the <form> tag
112
     * @param bool   $addtoken whether to add a security token to the form
113
     * @param string $summary
114
     */
115
    public function __construct($title, $name, $action, $method = 'post', $addtoken = false, $summary = '')
116
    {
117
        $this->_title   = $title;
118
        $this->_name    = $name;
119
        $this->_action  = $action;
120
        $this->_method  = $method;
121
        $this->_summary = $summary;
122
        if ($addtoken != false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison !== instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
123
            $this->addElement(new XoopsFormHiddenToken());
124
        }
125
    }
126
    /**
127
     * PHP 4 style constructor compatibility shim
128
     * @deprecated all callers should be using parent::__construct()
129
     */
130
    public function XoopsForm()
131
    {
132
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
133
        trigger_error("Should call parent::__construct in {$trace[0]['file']} line {$trace[0]['line']},");
134
        self::__construct();
0 ignored issues
show
Bug introduced by
The call to XoopsForm::__construct() misses some required arguments starting with $title.
Loading history...
135
    }    
136
    /**
137
     * *#@+
138
     * retrieves object serialisation/identification id (sha1 used)
139
     *
140
     * each object has serialisation<br>
141
     * - legal requirement of enterprise relational management (ERM)
142
     *
143
     * @deprecated
144
     * @access public
145
     * @param         $object
146
     * @param  string $hashinfo
147
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
148
     */
149
    public function getObjectID($object, $hashinfo = 'sha1')
150
    {
151
        if (!is_object($object)) {
152
            $object = $this;
153
        }
154
155
        switch ($hashinfo) {
156 View Code Duplication
            case 'md5':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
157
158
                @$var['name'] = md5(get_class($object));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$var was never initialized. Although not strictly required by PHP, it is generally a good practice to add $var = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
159
160
                foreach (get_object_vars($object) as $key => $value) {
161
                    if ($key !== '_objid') {
162
                        @$var['value'] = $this->getArrayID($value, $key, $var['value'], $hashinfo);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
163
                    }
164
                }
165
166
                foreach (get_class_methods($object) as $key => $value) {
167
                    @$var['func'] = $this->getArrayID($value, $key, $var['func'], $hashinfo);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
168
                }
169
170
                @$this->_objid = md5($var['name'] . ':' . $var['func'] . ':' . $var['value']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Deprecated Code introduced by
The property XoopsForm::$_objid has been deprecated.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
171
172
                return $this->_objid;
0 ignored issues
show
Deprecated Code introduced by
The property XoopsForm::$_objid has been deprecated.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
173
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
174
175 View Code Duplication
            default:
0 ignored issues
show
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
176
177
                @$var['name'] = sha1(get_class($object));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$var was never initialized. Although not strictly required by PHP, it is generally a good practice to add $var = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
178
179
                foreach (get_object_vars($object) as $key => $value) {
180
                    if ($key !== '_objid') {
181
                        @$var['value'] = $this->getArrayID($value, $key, $var['value'], $hashinfo);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
182
                    }
183
                }
184
185
                foreach (get_class_methods($object) as $key => $value) {
186
                    @$var['func'] = $this->getArrayID($value, $key, $var['func'], $hashinfo);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
187
                }
188
189
                @$this->_objid = sha1($var['name'] . ':' . $var['func'] . ':' . $var['value']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Deprecated Code introduced by
The property XoopsForm::$_objid has been deprecated.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
190
191
                return $this->_objid;
0 ignored issues
show
Deprecated Code introduced by
The property XoopsForm::$_objid has been deprecated.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
192
193
        }
194
    }
195
196
    /**
197
     * @param        $value
198
     * @param        $key
199
     * @param        $ret
200
     * @param string $hashinfo
201
     *
202
     * @return string
203
     */
204
    public function getArrayID($value, $key, $ret, $hashinfo = 'sha1')
205
    {
206
        switch ($hashinfo) {
207 View Code Duplication
            case 'md5':
208
                if (is_array($value)) {
209
                    foreach ($value as $keyb => $valueb) {
210
                        @$ret = md5($ret . ':' . $this->getArrayID($valueb, $keyb, $ret, $hashinfo));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
211
                    }
212
                } else {
213
                    @$ret = md5($ret . ':' . $key . ':' . $value);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
214
                }
215
216
                return $ret;
217
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
218 View Code Duplication
            default:
219
                if (is_array($value)) {
220
                    foreach ($value as $keyb => $valueb) {
221
                        @$ret = sha1($ret . ':' . $this->getArrayID($valueb, $keyb, $ret, $hashinfo));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
222
                    }
223
                } else {
224
                    @$ret = sha1($ret . ':' . $key . ':' . $value);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
225
                }
226
227
                return $ret;
228
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
229
        }
230
    }
231
232
    /**
233
     * return the summary of the form
234
     *
235
     * @param  bool $encode To sanitizer the text?
236
     * @return string
237
     */
238
    public function getSummary($encode = false)
239
    {
240
        return $encode ? htmlspecialchars($this->_summary, ENT_QUOTES) : $this->_summary;
241
    }
242
243
    /**
244
     * return the title of the form
245
     *
246
     * @param  bool $encode To sanitizer the text?
247
     * @return string
248
     */
249
    public function getTitle($encode = false)
250
    {
251
        return $encode ? htmlspecialchars($this->_title, ENT_QUOTES) : $this->_title;
252
    }
253
254
    /**
255
     * get the "name" attribute for the <form> tag
256
     *
257
     * Deprecated, to be refactored
258
     *
259
     * @param  bool $encode To sanitizer the text?
260
     * @return string
261
     */
262
    public function getName($encode = true)
263
    {
264
        return $encode ? htmlspecialchars($this->_name, ENT_QUOTES) : $this->_name;
265
    }
266
267
    /**
268
     * get the "action" attribute for the <form> tag
269
     *
270
     * @param  bool $encode To sanitizer the text?
271
     * @return string
272
     */
273
    public function getAction($encode = true)
274
    {
275
        // Convert &amp; to & for backward compatibility
276
        return $encode ? htmlspecialchars(str_replace('&amp;', '&', $this->_action), ENT_QUOTES) : $this->_action;
277
    }
278
279
    /**
280
     * get the "method" attribute for the <form> tag
281
     *
282
     * @return string
283
     */
284
    public function getMethod()
285
    {
286
        return (strtolower($this->_method) === 'get') ? 'get' : 'post';
287
    }
288
289
    /**
290
     * Add an element to the form
291
     *
292
     * @param string|XoopsFormElement $formElement reference to a {@link XoopsFormElement}
293
     * @param bool             $required    is this a "required" element?
294
     *
295
     */
296
    public function addElement($formElement, $required = false)
297
    {
298
        if (is_string($formElement)) {
299
            $this->_elements[] = $formElement;
300
        } elseif (is_subclass_of($formElement, 'xoopsformelement')) {
301
            $this->_elements[] = &$formElement;
302 View Code Duplication
            if (!$formElement->isContainer()) {
303
                if ($required) {
304
                    $formElement->_required = true;
305
                    $this->_required[]      = &$formElement;
306
                }
307
            } else {
308
                $required_elements = &$formElement->getRequired();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class XoopsFormElement as the method getRequired() does only exist in the following sub-classes of XoopsFormElement: XoopsFormDateTime, XoopsFormElementTray, XoopsFormSelectEditor, XoopsFormSelectUser. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
309
                $count             = count($required_elements);
310
                for ($i = 0; $i < $count; ++$i) {
311
                    $this->_required[] = &$required_elements[$i];
312
                }
313
            }
314
        }
315
    }
316
317
    /**
318
     * get an array of forms elements
319
     *
320
     * @param bool $recurse get elements recursively?
321
     *
322
     * @return array array of {@link XoopsFormElement}s
323
     */
324 View Code Duplication
    public function &getElements($recurse = false)
325
    {
326
        if (!$recurse) {
327
            return $this->_elements;
328
        } else {
329
            $ret   = array();
330
            $count = count($this->_elements);
331
            for ($i = 0; $i < $count; ++$i) {
332
                if (is_object($this->_elements[$i])) {
333
                    if (!$this->_elements[$i]->isContainer()) {
334
                        $ret[] = &$this->_elements[$i];
335
                    } else {
336
                        $elements = &$this->_elements[$i]->getElements(true);
337
                        $count2   = count($elements);
338
                        for ($j = 0; $j < $count2; ++$j) {
339
                            $ret[] = &$elements[$j];
340
                        }
341
                        unset($elements);
342
                    }
343
                }
344
            }
345
346
            return $ret;
347
        }
348
    }
349
350
    /**
351
     * get an array of "name" attributes of form elements
352
     *
353
     * @return array array of form element names
354
     */
355
    public function getElementNames()
356
    {
357
        $ret      = array();
358
        $elements = &$this->getElements(true);
359
        $count    = count($elements);
360
        for ($i = 0; $i < $count; ++$i) {
361
            $ret[] = $elements[$i]->getName();
362
        }
363
364
        return $ret;
365
    }
366
367
    /**
368
     * get a reference to a {@link XoopsFormElement} object by its "name"
369
     *
370
     * @param  string $name "name" attribute assigned to a {@link XoopsFormElement}
371
     * @return object reference to a {@link XoopsFormElement}, false if not found
372
     */
373
    public function &getElementByName($name)
374
    {
375
        $elements =& $this->getElements(true);
376
        $count    = count($elements);
377
        for ($i = 0; $i < $count; ++$i) {
378
            if ($name == $elements[$i]->getName(false)) {
379
                return $elements[$i];
380
            }
381
        }
382
        $elt = null;
383
384
        return $elt;
385
    }
386
387
    /**
388
     * Sets the "value" attribute of a form element
389
     *
390
     * @param string $name  the "name" attribute of a form element
391
     * @param string $value the "value" attribute of a form element
392
     */
393
    public function setElementValue($name, $value)
394
    {
395
        $ele = &$this->getElementByName($name);
396
        if (is_object($ele) && method_exists($ele, 'setValue')) {
397
            $ele->setValue($value);
398
        }
399
    }
400
401
    /**
402
     * Sets the "value" attribute of form elements in a batch
403
     *
404
     * @param array $values array of name/value pairs to be assigned to form elements
405
     */
406
    public function setElementValues($values)
407
    {
408
        if (is_array($values) && !empty($values)) {
409
            // will not use getElementByName() for performance..
410
            $elements = &$this->getElements(true);
411
            $count    = count($elements);
412 View Code Duplication
            for ($i = 0; $i < $count; ++$i) {
413
                $name = $elements[$i]->getName(false);
414
                if ($name && isset($values[$name]) && method_exists($elements[$i], 'setValue')) {
415
                    $elements[$i]->setValue($values[$name]);
416
                }
417
            }
418
        }
419
    }
420
421
    /**
422
     * Gets the "value" attribute of a form element
423
     *
424
     * @param  string $name   the "name" attribute of a form element
425
     * @param  bool   $encode To sanitizer the text?
426
     * @return string the "value" attribute assigned to a form element, null if not set
427
     */
428
    public function getElementValue($name, $encode = false)
429
    {
430
        $ele = &$this->getElementByName($name);
431
        if (is_object($ele) && method_exists($ele, 'getValue')) {
432
            return $ele->getValue($encode);
433
        }
434
435
        return null;
436
    }
437
438
    /**
439
     * gets the "value" attribute of all form elements
440
     *
441
     * @param  bool $encode To sanitizer the text?
442
     * @return array array of name/value pairs assigned to form elements
443
     */
444
    public function getElementValues($encode = false)
445
    {
446
        // will not use getElementByName() for performance..
447
        $elements = &$this->getElements(true);
448
        $count    = count($elements);
449
        $values   = array();
450 View Code Duplication
        for ($i = 0; $i < $count; ++$i) {
451
            $name = $elements[$i]->getName(false);
452
            if ($name && method_exists($elements[$i], 'getValue')) {
453
                $values[$name] = &$elements[$i]->getValue($encode);
454
            }
455
        }
456
457
        return $values;
458
    }
459
460
    /**
461
     * set the extra attributes for the <form> tag
462
     *
463
     * @param string $extra extra attributes for the <form> tag
464
     */
465
    public function setExtra($extra)
466
    {
467
        if (!empty($extra)) {
468
            $this->_extra[] = $extra;
469
        }
470
    }
471
472
    /**
473
     * set the summary tag for the <form> tag
474
     *
475
     * @param string $summary
476
     */
477
    public function setSummary($summary)
478
    {
479
        if (!empty($summary)) {
480
            $this->summary = strip_tags($summary);
0 ignored issues
show
Bug introduced by
The property summary does not seem to exist. Did you mean _summary?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
481
        }
482
    }
483
484
    /**
485
     * get the extra attributes for the <form> tag
486
     *
487
     * @return string
488
     */
489
    public function &getExtra()
490
    {
491
        $extra = empty($this->_extra) ? '' : ' ' . implode(' ', $this->_extra);
492
493
        return $extra;
494
    }
495
496
    /**
497
     * make an element "required"
498
     *
499
     * @param XoopsFormElement $formElement reference to a {@link XoopsFormElement}
500
     */
501
    public function setRequired(XoopsFormElement $formElement)
502
    {
503
        $this->_required[] = &$formElement;
504
    }
505
506
    /**
507
     * get an array of "required" form elements
508
     *
509
     * @return array array of {@link XoopsFormElement}s
510
     */
511
    public function &getRequired()
512
    {
513
        return $this->_required;
514
    }
515
516
    /**
517
     * insert a break in the form
518
     *
519
     * This method is abstract. It must be overwritten in the child classes.
520
     *
521
     * @param string $extra extra information for the break
522
     * @abstract
523
     */
524
    public function insertBreak($extra = null)
525
    {
526
    }
527
528
    /**
529
     * returns renderered form
530
     *
531
     * This method is abstract. It must be overwritten in the child classes.
532
     *
533
     * @abstract
534
     */
535
    public function render()
536
    {
537
    }
538
539
    /**
540
     * displays rendered form
541
     */
542
    public function display()
543
    {
544
        echo $this->render();
545
    }
546
547
    /**
548
     * Renders the Javascript function needed for client-side for validation
549
     *
550
     * Form elements that have been declared "required" and not set will prevent the form from being
551
     * submitted. Additionally, each element class may provide its own "renderValidationJS" method
552
     * that is supposed to return custom validation code for the element.
553
     *
554
     * The element validation code can assume that the JS "myform" variable points to the form, and must
555
     * execute <i>return false</i> if validation fails.
556
     *
557
     * A basic element validation method may contain something like this:
558
     * <code>
559
     * function renderValidationJS() {
560
     *            $name = $this->getName();
561
     *            return "if (myform.{$name}.value != 'valid') { " .
562
     *              "myform.{$name}.focus(); window.alert( '$name is invalid' ); return false;" .
563
     *              " }";
564
     * }
565
     * </code>
566
     *
567
     * @param boolean $withtags Include the < javascript > tags in the returned string
568
     *
569
     * @return string
570
     */
571
    public function renderValidationJS($withtags = true)
572
    {
573
        $js = '';
574
        if ($withtags) {
575
            $js .= "\n<!-- Start Form Validation JavaScript //-->\n<script type='text/javascript'>\n<!--//\n";
576
        }
577
        $formname = $this->getName();
578
        $js .= "function xoopsFormValidate_{$formname}() { var myform = window.document.{$formname}; ";
579
        $elements =& $this->getElements(true);
580
        foreach ($elements as $elt) {
581
            if (method_exists($elt, 'renderValidationJS')) {
582
                $js .= $elt->renderValidationJS();
583
            }
584
        }
585
        $js .= "return true;\n}\n";
586
        if ($withtags) {
587
            $js .= "//--></script>\n<!-- End Form Validation JavaScript //-->\n";
588
        }
589
590
        return $js;
591
    }
592
593
    /**
594
     * assign to smarty form template instead of displaying directly
595
     *
596
     * @param XoopsTpl $tpl reference to a {@link Smarty} object object
597
     * @see      Smarty
598
     */
599
    public function assign(XoopsTpl $tpl)
600
    {
601
        $i        = -1;
602
        $elements = array();
603
        if (count($this->getRequired()) > 0) {
604
            $this->_elements[] = "<tr class='foot'><td colspan='2'>* = " . _REQUIRED . '</td></tr>';
605
        }
606
        foreach ($this->getElements() as $ele) {
607
            ++$i;
608
            if (is_string($ele)) {
609
                $elements[$i]['body'] = $ele;
610
                continue;
611
            }
612
            $ele_name                 = $ele->getName();
613
            $ele_description          = $ele->getDescription();
614
            $n                        = $ele_name ?: $i;
615
            $elements[$n]['name']     = $ele_name;
616
            $elements[$n]['caption']  = $ele->getCaption();
617
            $elements[$n]['body']     = $ele->render();
618
            $elements[$n]['hidden']   = $ele->isHidden();
619
            $elements[$n]['required'] = $ele->isRequired();
620
            if ($ele_description != '') {
621
                $elements[$n]['description'] = $ele_description;
622
            }
623
        }
624
        $js = $this->renderValidationJS();
625
        $tpl->assign($this->getName(), array(
626
            'title'      => $this->getTitle(),
627
            'name'       => $this->getName(),
628
            'action'     => $this->getAction(),
629
            'method'     => $this->getMethod(),
630
            'extra'      => 'onsubmit="return xoopsFormValidate_' . $this->getName() . '();"' . $this->getExtra(),
631
            'javascript' => $js,
632
            'elements'   => $elements));
633
    }
634
}
635