Issues (164)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Bootstrap/Bootstrap3Form.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace BootPress\Bootstrap;
4
5
use BootPress\Form\Component as Form;
6
7
class Bootstrap3Form extends Form
8
{
9
    use Base;
10
    
11
    private $bp;
12
    private $prompt = array(
13
        'info' => 'glyphicon glyphicon-question-sign',
14
    );
15
    private $input = '';
16
    private $align = 'form-horizontal';
17
    private $collapse = 'sm';
18
    private $indent = 2;
19
    
20
    /**
21
     * {@inheritdoc}
22
     */
23 1
    public function __construct($name = 'form', $method = 'post', Bootstrap3 $bp)
24
    {
25 1
        parent::__construct($name, $method = 'post');
26 1
        $this->bp = $bp;
27 1
    }
28
    
29
    /**
30
     * A private property getter.
31
     * 
32
     * @param string $name 
33
     * 
34
     * @return null|string
35
     */
36 1
    public function __get($name)
37
    {
38
        switch ($name) {
39 1
            case 'prompt':
40 1
            case 'input':
41 1
            case 'align':
42 1
            case 'collapse':
43 1
            case 'indent':
44 1
                return $this->$name;
45
                break;
0 ignored issues
show
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...
46
        }
47 1
    }
48
    /**
49
     * This is to add html tags, or semicolons, or asterisks, or whatever you would like to all of the form's prompts.
50
     * 
51
     * @param string      $place    Either '**info**', '**append**', or '**prepend**' to the prompt.  You only have one shot at each.
52
     * @param string      $html     Whatever you would like to add.  For 'info', this will be the icon class you want to use.
53
     * @param false|mixed $required If this is anything but (bool) false and $place == 'prepend', the the $html will only be prepended if the field is required per the ``$form->validator``.
54
     * 
55
     * ```php
56
$form->prompt('prepend', '<font color="red">*</font> ', 'required'); // If the field is required it will add a red asterisk to the front.
57
58
$form->prompt('append', ':'); // Adds a semicolon to all of the prompts.
59
     * ```
60
     */
61 1
    public function prompt($place, $html, $required = false)
62
    {
63
        switch ($place) {
64 1
            case 'info':
65 1
            case 'append':
66 1
                $this->prompt[$place] = $html;
67 1
                break;
68 1
            case 'prepend':
69 1
                $this->prompt['prepend'] = array('html'=>$html, 'required'=>(bool) $required);
70 1
                break;
71
        }
72 1
    }
73
    
74
    /**
75
     * Supersize or undersize your input fields.
76
     * 
77
     * @param string $input Either '**lg**' (large), '**md**' (medium - the default), or '**sm**' (small).
78
     * 
79
     * ```php
80
     * $form->size('lg');
81
     * ```
82
     */
83 1
    public function size($input)
84
    {
85 1
        $this->input = (in_array($input, array('lg', 'sm'))) ? 'input-'.$input : '';
86 1
    }
87
88
    /**
89
     * Utilize any Bootstrap form style
90
     * 
91
     * @param string  $direction The options are:
92
     *
93
     * - '**collapse**' - This will display the form prompt immediately above the field.
94
     * - '**inline**' - All of the fields will be inline with each other, and the form prompts will be removed.
95
     * - '**horizontal**' - Vertically aligns all of the fields with the prompt immediately preceding, and right aligned.
96
     * 
97
     * @param string  $collapse  Either '**xs**', '**sm**', '**md**', or '**lg**'.  This is the breaking point so to speak for a '**horizontal**' form.  It is the device size on which the form will '**collapse**'.
98
     * @param integer $indent    The number of columns (up to 12) that you would like to indent the field in a '**horizontal**' form.
99
     * 
100
     * ```php
101
     * $form->align('collapse');
102
     * ```
103
     */
104 1
    public function align($direction = 'horizontal', $collapse = 'sm', $indent = 2)
105
    {
106 1
        if ($direction == 'collapse') {
107 1
            $this->align = '';
108 1
        } elseif ($direction == 'inline') {
109 1
            $this->align = 'form-inline';
110 1
        } else {
111 1
            $this->align = 'form-horizontal';
112 1
            $this->collapse = (in_array($collapse, array('xs', 'sm', 'md', 'lg'))) ? $collapse : 'sm';
113 1
            $this->indent = (is_numeric($indent) && $indent > 0 && $indent < 12) ? $indent : 2;
0 ignored issues
show
Documentation Bug introduced by
It seems like is_numeric($indent) && $...dent < 12 ? $indent : 2 can also be of type double or string. However, the property $indent is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
114
        }
115 1
    }
116
    
117
    /**
118
     * Use to display a message on the retrun trip after you have ``$page->eject()``ed them.  The Bootstrap alert status message will be displayed at the top of the form when you return ``$form->header()``.
119
     * 
120
     * @param string $status   Either '**success**', '**info**', '**warning**', or '**danger**'.  If this is '**html**', then the $message will be delivered as is.
121
     * @param string $message  The message you would like to get across to your user.  ``<h1-6>`` headers and ``<a>`` links may be used.
122
     * 
123
     * ```php
124
     * if ($vars = $form->validator->certified()) {
125
     *     $form->message('success', 'Good job, you are doing great!');
126
     *     $page->eject($form->eject);
127
     * }
128
     * ```
129
     */
130 1
    public function message($status, $message)
131
    {
132 1
        $this->page->session->getFlashBag()->add($this->header['name'], array(
0 ignored issues
show
The property $session is declared private in BootPress\Page\Component. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
133 1
            'status' => $status,
134 1
            'msg' => $message,
135 1
        ));
136 1
    }
137
    
138
    /**
139
     * {@inheritdoc}
140
     */
141 1
    public function header(array $validate = array())
142
    {
143 1
        $this->validator->jquery('form[name='.$this->header['name'].']', array_merge(array(
144 1
            'ignore' => '[]',
145 1
            'errorClass' => '"has-error"',
146 1
            'validClass' => '""',
147 1
            'errorElement' => '"span"',
148 1
            'highlight' => 'function(element, errorClass, validClass){ $(element).closest("div.form-group").addClass(errorClass).removeClass(validClass).find("p.validation").show(); }',
149 1
            'unhighlight' => 'function(element, errorClass, validClass){ $(element).closest("div.form-group").removeClass(errorClass).addClass(validClass).find("p.validation").text("").hide(); }',
150 1
            'errorPlacement' => 'function(error, element){ $(element).closest("div.form-group").find("p.validation").html(error); }',
151 1
            'submitHandler' => 'function(form, event){ event.preventDefault(); $(form).find("button[type=submit]").button("loading"); form.submit(); }',
152 1
            'onkeyup' => 'false',
153 1
        ), $validate));
154 1
        $html = "\n";
155 1
        if ($this->align == 'form-horizontal') {
156 1
            $html .= '<div class="row"><div class="col-'.$this->collapse.'-12">';
157 1
        }
158 1
        if ($flash = $this->page->session->getFlashBag()->get($this->header['name'])) {
0 ignored issues
show
The property $session is declared private in BootPress\Page\Component. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
159 1
            $html .= ($flash[0]['status'] == 'html') ? $flash[0]['msg'] : $this->bp->alert($flash[0]['status'], $flash[0]['msg']);
160 1
        }
161 1
        $html .= trim(parent::header());
162 1
        if (!empty($this->align)) {
163 1
            $html = $this->addClass($html, array('form' => $this->align));
164 1
        }
165
166 1
        return $html;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     * 
172
     * @param false|mixed $inline This tells us if you want the checkboxes to be inline (any value but false), or not (false).
173
     */
174 1
    public function checkbox($field, array $attributes = array(), $inline = false)
175
    {
176 1
        $disabled = in_array('disabled', $attributes) ? 'disabled' : '';
177 1
        if ($inline !== false) {
178 1
            $wrap = $this->page->tag('label', array('class' => array('checkbox-inline', $this->input, $disabled)), '%s');
179 1
        } else {
180 1
            $wrap = $this->page->tag('div', array('class' => array('checkbox', $this->input, $disabled)), '<label>%s</label>');
181
        }
182
183 1
        return parent::checkbox($field, $attributes, $wrap);
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     * 
189
     * @param false|mixed $inline This tells us if you want the radio buttons to be inline (any value but false), or not (false).
190
     */
191 1
    public function radio($field, array $attributes = array(), $inline = false)
192
    {
193 1
        $disabled = in_array('disabled', $attributes) ? 'disabled' : '';
194 1
        if ($inline !== false) {
195 1
            $wrap = $this->page->tag('label', array('class' => array('radio-inline', $this->input, $disabled)), '%s');
196 1
        } else {
197 1
            $wrap = $this->page->tag('div', array('class' => array('radio', $this->input, $disabled)), '<label>%s</label>');
198
        }
199
200 1
        return parent::radio($field, $attributes, $wrap);
201
    }
202
203
    /**
204
     * Group an input field with addons.  You can prepend and/or append a ``$bp->button(...)``, ``$bp->icon('...')``, or just a string of text.  To prepend or append multiple elements, then make it an ``array($html, ...)`` of addons.
205
     * 
206
     * @param string|array $prepend An element to place before the $input.
207
     * @param string|array $append  An element to place after the $input.
208
     * @param string $input   The form field to wrap.    
209
     * 
210
     * @return string A ``<div class="input-group">...</div>`` html string.
211
     * 
212
     * ```php
213
     * echo $form->group('$', '.00', $form->text('amount'));
214
     * ```
215
     */
216 1
    public function group($prepend, $append, $input)
217
    {
218 1
        if (!empty($prepend)) {
219 1
            foreach ((array) $prepend as $html) {
220 1
                $class = (strpos($html, 'btn') !== false) ? 'input-group-btn' : 'input-group-addon';
221 1
                $input = $this->page->tag('div', array('class' => $class), $html).$input;
222 1
            }
223 1
        }
224 1
        if (!empty($append)) {
225 1
            foreach ((array) $append as $html) {
226 1
                $class = (strpos($html, 'btn') !== false) ? 'input-group-btn' : 'input-group-addon';
227 1
                $input = $input.$this->page->tag('div', array('class' => $class), $html);
228 1
            }
229 1
        }
230 1
        $group = array('input-group');
231 1
        if (!empty($this->input)) {
232 1
            $group[] = str_replace('-', '-group-', $this->input);
233 1
        }
234
235 1
        return $this->page->tag('div', array('class' => $group), $input);
236
    }
237
    
238
    /**
239
     * This is to add html tags, or semicolons, or asterisks, or whatever you would like include with all of the form's prompts.
240
     * 
241
     * @param string|array $prompt  The form label reference.  If you want to include additional information relative to the field, then you can make this an ``array($prompt => $info)``, or an ``array($prompt, $info)`` that will appear when cliked or hovered over.  To customize the icon set ``$form->prompt('info', 'fa fa-info-circle')``.
242
     * @param string       $name    The name of the associated input field.
243
     * @param string       $id      The id of the associated input field.
244
     * 
245
     * @return string  The generated HTML ``<label>``.
246
     */
247 1
    public function label($prompt, $name, $id)
248
    {
249 1
        if (empty($prompt)) {
250 1
            return '';
251
        }
252 1
        if (is_array($prompt)) {
253 1
            list($prompt, $info) = (count($prompt) > 1) ? array_values($prompt) : each($prompt);
254 1
        }
255 1
        if (empty($prompt) || strpos($prompt, '<label') !== false) {
256 1
            return $prompt;
257
        }
258 1
        if (isset($this->prompt['prepend'])) {
259 1
            if (!$this->prompt['prepend']['required'] || $this->validator->required($name)) {
260 1
                $prompt = $this->prompt['prepend']['html'] . $prompt;
261 1
            }
262 1
        }
263 1
        if (isset($this->prompt['append'])) {
264 1
            $prompt .= $this->prompt['append'];
265 1
        }
266 1
        if (isset($info)) {
267 1
            $prompt .= ' '.$this->page->tag('i', array(
268 1
                'title' => htmlspecialchars($info),
269 1
                'class' => $this->prompt['info'],
270 1
                'style' => 'cursor:pointer;',
271 1
                'data-html' => 'true',
272 1
                'data-toggle' => 'tooltip',
273 1
                'data-placement' => 'bottom',
274 1
                'data-container' => 'form[name='.$this->header['name'].']',
275 1
            ), '');
276 1
            $this->page->jquery('$(\'[data-toggle="tooltip"]\').tooltip();');
277 1
        }
278 1
        switch ($this->align) {
279 1
            case 'form-inline':
280 1
                $class = 'sr-only';
281 1
                break;
282 1
            case 'form-horizontal':
283
                $class = array(
284 1
                    "col-{$this->collapse}-{$this->indent}",
285 1
                    'control-label',
286 1
                    $this->input,
287 1
                );
288 1
                break;
289 1
            default:
290 1
                $class = $this->input;
291 1
                break;
292 1
        }
293 1
        return $this->page->tag('label', array(
294 1
            'class' => $class,
295 1
            'for' => $id,
296 1
        ), $prompt);
297
    }
298
299
    /**
300
     * Adds a (properly formatted) $prompt to your $input field, and manages any error messages.
301
     * 
302
     * @param string $prompt The $input's name.
303
     * @param string $input  A form field.
304
     * @param string $error  An optional error to override, or include with the field.
305
     * 
306
     * @return string A ``<div class="form-group">...</div>`` html string.
307
     * 
308
     * ```php
309
     * echo $form->field('Amount', $form->group('$', '.00', $form->text('amount')));
310
     * ```
311
     */
312 1
    public function field($prompt, $input, $error = null)
313
    {
314 1
        foreach (array('input', 'select', 'textarea', 'button', 'p') as $tag) {
315 1
            if ($this->firstTagAttributes($input, $matches, '<'.$tag)) {
316 1
                break;
317
            }
318 1
        }
319 1
        list($first, $tag, $attributes) = $matches;
0 ignored issues
show
The variable $matches does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
The assignment to $first is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
320 1
        $type = (isset($attributes['type'])) ? $attributes['type'] : '';
321 1
        $name = (isset($attributes['name'])) ? $attributes['name'] : '';
322 1
        $id = (isset($attributes['id'])) ? $attributes['id'] : '';
323 1
        $prompt = $this->label($prompt, $name, $id);
324
        switch ($tag) {
325 1
            case 'input':
326 1
            case 'select':
327 1
            case 'textarea':
328 1
                $input = $this->addClass($input, array('p'=>'help-block'));
329 1
                if ($tag != 'input' || !in_array($type, array('checkbox', 'radio', 'file', 'submit', 'reset', 'button'))) {
330 1
                    $input = $this->addClass($input, array($tag=>'form-control '.$this->input));
331 1
                }
332 1
                break;
333 1
            case 'p':
334 1
                $input = $this->addClass($input, array('p'=>'form-control-static'));
335 1
                break;
336
        }
337 1
        $group = array('form-group');
338 1
        $msg = (empty($name)) ? null : $this->validator->error($name);
339 1
        if (!is_null($error)) {
340 1
            $msg = $error; // override all
341 1
        }
342 1
        if (!empty($msg)) {
343 1
            $group[] = 'has-error';
344 1
            $error = '<p class="validation help-block">'.$msg.'</p>';
345 1
        } elseif (!empty($name)) { // only include this when needed for validation
346 1
            $error = '<p class="validation help-block" style="display:none;"></p>';
347 1
        }
348 1
        if ($this->align == 'form-horizontal') {
349 1
            $class = array('col-'.$this->collapse.'-'.(12 - $this->indent));
350 1
            if (empty($prompt)) {
351 1
                $class[] = 'col-'.$this->collapse.'-offset-'.$this->indent;
352 1
            }
353 1
            $html = $prompt.$this->page->tag('div', array('class'=>$class), $error.$input);
354 1
        } else {
355 1
            $html = $prompt.$error.$input;
356
        }
357
358 1
        return "\n\t".$this->page->tag('div', array('class'=>$group), $html);
359
    }
360
361
    /**
362
     * Quickly adds a submit button to your form.
363
     * 
364
     * @param string $submit What you would like the submit button to say.  If it starts with a '**<**', then we assume you have spelled it all out for us.
365
     * @param string $reset  This will add a reset button if you give it a value, and if it starts with a '**<**' then it can be whatever you want it to be.  You can keep adding args until you run out of ideas for buttons to include.
366
     * 
367
     * @return string A ``<div class="form-group">...</div>`` html string with buttons.
368
     * 
369
     * ```php
370
     * echo $form->submit();
371
     * ```
372
     */
373 1
    public function submit($submit = 'Submit', $reset = '')
374
    {
375
        // never use name="submit" per: http://jqueryvalidation.org/reference/#developing-and-debugging-a-form
376 1
        $buttons = func_get_args();
377 1
        if (substr($submit, 0, 1) != '<') {
378 1
            $buttons[0] = $this->page->tag('button', array(
379 1
                'type' => 'submit',
380 1
                'class' => array('btn', 'btn-primary', str_replace('input', 'btn', $this->input)),
381 1
                'data-loading-text' => 'Submitting...',
382 1
            ), $submit);
383 1
        }
384 1
        if (isset($buttons[1]) && substr($reset, 0, 1) != '<') {
385 1
            $buttons[1] = $this->page->tag('button', array(
386 1
                'type' => 'reset',
387 1
                'class' => array('btn', 'btn-default', str_replace('input', 'btn', $this->input)),
388 1
            ), $reset);
389 1
        }
390
391 1
        return $this->field('', implode(' ', $buttons));
392
    }
393
394
    /**
395
     * {@inheritdoc}
396
     */
397 1
    public function close()
398
    {
399 1
        $html = parent::close();
400 1
        if ($this->align == 'form-horizontal') {
401 1
            $html .= '</div></div>';
402 1
        }
403 1
        return $html;
404
    }
405
}
406