Issues (19)

Security Analysis    no request data  

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/Forms/Fields/Field.php (2 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
namespace Rocket\UI\Forms\Fields;
3
4
use Rocket\UI\Forms\Forms;
5
6
/**
7
 * Creates a form field
8
 *
9
 * @author Stéphane Goetz
10
 *
11
 * @method $this value(string $value) the value in the field
12
 * @method $this inline(bool $enabled)
13
 * @method $this id(string $id)
14
 * @method $this width(string $width)
15
 * @method $this height(string $height)
16
 * @method $this tip(string $tip)
17
 * @method $this label_style(string $label)
18
 * @method $this writable(boolean $writable)
19
 * @method $this validate(boolean $validate)
20
 * @method $this maxlength(int $max)
21
 */
22
class Field
23
{
24
    /**
25
     * The field name
26
     *
27
     * @var string
28
     */
29
    protected $name;
30
31
    /**
32
     * Options for the field
33
     *
34
     * @var array
35
     */
36
    protected $params;
37
38
    /**
39
     * Unique ID for the field
40
     * @var string
41
     */
42
    protected $id;
43
44
    /**
45
     * Show the label or not ?
46
     * @var bool
47
     */
48
    protected $show_label = true;
49
50
    /**
51
     * The attributes that will be applied to the <input>
52
     * @var array
53
     */
54
    protected $input_attributes = [
55
        'class' => ['elm', 'form-control'],
56
    ];
57
58
    /**
59
     * The attributes that will be applied to the <label>
60
     * @var array
61
     */
62
    protected $label_attributes = [
63
        'class' => ['elm'],
64
    ];
65
66
    /**
67
     * The attributes that will be applied to the <span> with the label
68
     * @var array
69
     */
70
    protected $span_attributes = [
71
        'class' => ['field-label', 'control-label'],
72
    ];
73
74
    /**
75
     * The final content of the field
76
     * @var string
77
     */
78
    protected $result;
79
80
    /**
81
     * Is the field required or not ?
82
     *
83
     * @var bool
84
     */
85
    protected $required = '';
86
87
    /**
88
     * The fields type, will be added in <input type="" ...
89
     *
90
     * @var string
91
     */
92
    protected $type = 'text';
93
94
    /**
95
     * The javascript events bound to the field
96
     *
97
     * @var array
98
     */
99
    protected $events = [];
100
101
    /**
102
     * The javascript events bound to the label
103
     * @var array
104
     */
105
    protected $events_label = [];
106
107
    protected static $templates;
108
109
    protected static $config;
110
111
    /**
112
     * @var \Rocket\UI\Script\JS
113
     */
114
    protected static $js;
115
116
    /**
117
     * @var callable
118
     */
119
    protected static $js_resolver;
120
121
    /**
122
     * Initializes the field
123
     * @param string $name
124
     * @param array $data
125
     */
126
    public function __construct($name, $data = [])
127
    {
128
        $this->name = $name;
129
130
        $required = false;
131
        $value = '';
132
        if ($validator = $this->getValidator()) {
133
            $required = $validator->isRequired($name);
134
            $value = $validator->getValue($name, array_key_exists('default', $data) ? $data['default'] : '');
135
        }
136
137
        // Default configuration
138
        $default = $this->getDefaults() + ['value' => $value, 'required' => $required];
139
140
        // Final configuration
141
        $this->params = array_replace_recursive($default, $data);
142
    }
143
144
    /**
145
     * Set the javascript queueing instance callback
146
     *
147
     * @param $callable callable
148
     */
149
    public static function setJSResolver($callable)
150
    {
151
        self::$js_resolver = $callable;
152
    }
153
154
    /**
155
     * Get the Javascript queueing instance
156
     *
157
     * @throws \Exception
158
     * @return \Rocket\UI\Script\JS
159
     */
160
    protected function getJS()
161
    {
162
        if (null === self::$js) {
163
            if (!self::$js = call_user_func(self::$js_resolver)) {
164
                throw new \Exception('No javascript queueing instance can be found');
165
            }
166
        }
167
168
        return self::$js;
169
    }
170
171
    /**
172
     * Get the Javascript queueing instance
173
     *
174
     * @throws \Exception
175
     * @return \Rocket\UI\Forms\ValidatorAdapters\ValidatorInterface
176
     */
177
    protected function getValidator()
178
    {
179
        return Forms::getFormValidator();
180
    }
181
182
    /**
183
     * Get the default values array
184
     *
185
     * @return array
186
     */
187
    protected function getDefaults()
188
    {
189
        $defaults = [
190
            'title' => '',
191
            'live' => 'blur',
192
            'validate' => true,
193
            'label_position' => 'before',
194
            'inline' => false, //afficher le label sur la même ligne que le champ
195
            'type' => $this->type,
196
            'width' => 0,
197
            'height' => 12,
198
            'margins' => 12, //width in px
199
            'data_attributes' => [],
200
            'class' => '',
201
            'multifield' => false,
202
            'maxlength' => [
203
                'slider' => true,
204
            ],
205
        ];
206
207
        return $defaults;
208
    }
209
210
    /**
211
     * Proper destructor
212
     */
213
    public function __destruct()
214
    {
215
        unset($this->name);
216
        unset($this->input_attributes);
217
        unset($this->label_attributes);
218
        unset($this->params);
219
        unset($this->required);
220
        unset($this->result);
221
        unset($this->show_label);
222
        unset($this->span_attributes);
223
    }
224
225
    /**
226
     * Adds a jquery event to the field
227
     * @param string $name
228
     * @param string $action
229
     * @param bool $bind_to_attributes
230
     * @return $this
231
     */
232
    public function event($name, $action, $bind_to_attributes = false)
233
    {
234
        if ($bind_to_attributes) {
235
            $this->input_attributes[$name] = $action;
236
        } else {
237
            $this->events[$name] = $action;
238
        }
239
240
        return $this;
241
    }
242
243
    /**
244
     * Adds a jquery event to the field
245
     * @param string $name
246
     * @param string $action
247
     * @return $this
248
     */
249
    public function eventLabel($name, $action)
250
    {
251
        $this->events_label[$name] = $action;
252
253
        return $this;
254
    }
255
256
    /**
257
     * Change parameters
258
     *
259
     * @param $method
260
     * @param $arguments
261
     * @throws \Exception
262
     * @return $this
263
     */
264
    public function __call($method, $arguments)
265
    {
266
        if (strlen(ltrim($method, '_')) != strlen($method)) {
267
            throw new \Exception('Invalid method');
268
        } else {
269
            if (is_array($arguments[0])) {
270
                if (array_key_exists($method, $this->params)) {
271
                    $this->params[$method] =
272
                        array_merge($this->params[$method], $arguments[0]);
273
                } else {
274
                    $this->params[$method] = $arguments[0];
275
                }
276
            } else {
277
                $this->params[$method] = $arguments[0];
278
            }
279
280
            return $this;
281
        }
282
    }
283
284
    /**
285
     * Set data attributes to the field
286
     *
287
     * @param $key string
288
     * @param $value string|array
289
     * @return $this
290
     */
291
    public function data($key, $value)
292
    {
293
        $this->params['data_attributes'][$key] = $value;
294
295
        return $this;
296
    }
297
298
    /**
299
     * Checks if it's an error
300
     */
301
    protected function hasError()
302
    {
303
        $validator = $this->getValidator();
304
305
        if ($validator && $validator->hasError($this->name)) {
306
            $this->label_attributes['class'][] = 'has-error';
307
        }
308
    }
309
310
    /**
311
     * Is required ?
312
     */
313
    protected function isRequired()
314
    {
315
        if ($this->params['required']
316
            && $this->show_label
317
            && $this->params['title'] != ''
318
        ) {
319
            $this->required = ' *';
0 ignored issues
show
Documentation Bug introduced by
The property $required was declared of type boolean, but ' *' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
320
        }
321
    }
322
323
    /**
324
     * Adds default css classes
325
     */
326
    protected function classes()
327
    {
328
        //Label
329
        if (array_key_exists('label_style', $this->params)) {
330
            $this->label_attributes['style'] = $this->params['label_style'];
331
        }
332
333
        if (array_key_exists('label_id', $this->params)) {
334
            $this->label_attributes['id'] = $this->params['label_id'];
335
        }
336
337
        $this->label_attributes['class']['form_type'] = 'form_' . $this->params['type'];
338
339
        //Input
340
        if (array_key_exists('input_style', $this->params)) {
341
            $this->input_attributes['style'] = $this->params['input_style'];
342
        }
343
344
        if (array_key_exists('class', $this->params)) {
345
            $this->input_attributes['class'][] = $this->params['class'];
346
        }
347
348
        $this->input_attributes['class']['type'] = 'form_' . $this->params['type'];
349
    }
350
351
    /**
352
     * Adds the width to the field
353
     */
354
    protected function applyWidth()
355
    {
356
        if (strpos($this->params['width'], '%') or
357
            strpos($this->params['width'], 'em') or
358
            strpos($this->params['width'], 'px')
359
        ) {
360
            $this->label_attributes['style']['width'] = $this->params['width'];
361
362
            if (strpos($this->params['width'], 'px')) {
363
                $w = str_replace('px', '', $this->params['width']);
364
                $w -= $this->params['margins'];
365
                $this->params['width'] = $w . 'px';
366
            }
367
            $this->input_attributes['style']['width'] = $this->params['width'];
368
369
            return;
370
        }
371
372
        if ($this->params['width'] != 0) {
373
            $this->label_attributes['class'][] = 'col-xs-' . $this->params['width'];
374
375
            return;
376
        }
377
378
        if ($this->params['width'] == 0) {
379
            $this->label_attributes['class'][] = 'col-full';
380
381
            return;
382
        }
383
    }
384
385
    /**
386
     * Extracts the validation rules to make a javascript validation
387
     */
388
    protected function inputValidation()
389
    {
390
        if (!$this->params['validate']) {
391
            return;
392
        }
393
394
        //TODO :: finish integration with validation libraries
395
396
        /*$validator = $this->getValidator();
397
        if ($validator) {
398
            $field = $validator->getClientRules($this->name);
399
            if (!empty($field)) {
400
                $this->input_attributes['data-rules'] = $field;
401
                $this->input_attributes['data-display'] = strip_tags($this->params['title']);
402
                $this->input_attributes['data-live'] = $this->params['live'];
403
            }
404
        }*/
405
    }
406
407
    /**
408
     * generate an unique id to use in the field
409
     */
410
    protected function generateId()
411
    {
412
        $s = strtoupper(md5(uniqid(rand(), true)));
413
414
        return substr($s, 0, 8) . '_' . substr($s, 8, 4) . '_' . substr($s, 12, 4) . '_' . substr($s, 16, 4);
415
    }
416
417
    public function template($string, array $args = [])
418
    {
419
        $class = \Rocket\UI\Forms\Templates\Bootstrap::class;
420
        if (null == self::$templates) {
421
            self::$templates[$class] = get_class_vars($class);
422
        }
423
424
        //if there are some arguments
425
        if (empty($args)) {
426
            return self::$templates[$class][$string];
427
        }
428
429
        return strtr(self::$templates[$class][$string], $args);
430
    }
431
432
    /**
433
     * Renders the field
434
     * @return string
435
     */
436
    public function render()
437
    {
438
        if (!array_key_exists('id', $this->params) || empty($this->params['id'])) {
439
            $this->params['id'] = $this->generateId();
440
        }
441
442
        $this->id = $this->params['id'];
443
444
        //Run the scripts and events before (they may add some parameters)
445
        $this->renderEvents();
446
        $this->renderScript();
447
448
        $this->input_attributes['value'] = $this->params['value'];
449
450
        $this->classes();
451
        $this->applyWidth();
452
453
        $this->isRequired();
454
        $this->hasError();
455
456
        $this->inputAttributes();
457
        $this->inputDataAttributes();
458
        $this->inputValidation();
0 ignored issues
show
The call to the method Rocket\UI\Forms\Fields\Field::inputValidation() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
459
        $this->inputTip();
460
461
        if ($this->show_label) {
462
            $tag = $this->params['multifield'] ? 'div' : 'label';
463
            $this->result .= "<$tag " . $this->renderAttributes($this->label_attributes) . '>';
464
        }
465
466
        if ($this->params['label_position'] == 'before') {
467
            $this->renderTitle();
468
        }
469
470
        $this->renderInner();
471
472
        if ($this->params['label_position'] != 'before') {
473
            $this->renderTitle();
474
        }
475
476
        $validator = $this->getValidator();
477
        if ($validator && $validator->hasError($this->name)) {
478
            $this->result .= $this->template('HELP_BLOCK', ['!help' => $this->formatErrors($validator->getErrors($this->name))]);
479
        }
480
481
        if ($this->show_label) {
482
            $this->result .= "</$tag>";
483
        }
484
485
        return $this->result;
486
    }
487
488
    protected function formatErrors($errors)
489
    {
490
        if (is_string($errors)) {
491
            return $errors;
492
        }
493
494
        if (count($errors) == 1) {
495
            return $errors[0];
496
        }
497
498
        return '<ul><li>' . implode('</li><li>', $errors) . '</li></ul>';
499
    }
500
501
    /**
502
     * Auto render the field
503
     * @return string
504
     */
505
    public function __toString()
506
    {
507
        return $this->render();
508
    }
509
510
    /**
511
     * Renders the script
512
     */
513
    protected function renderScript()
514
    {
515
        if (!empty($this->params['maxlength']['maxCharacters'])) {
516
            $this->getJS()->ready('$("#' . $this->id . '").maxlength(' . json_encode($this->params['maxlength']) . ');');
517
        }
518
    }
519
520
    /**
521
     * Renders the javascript events
522
     */
523
    protected function renderEvents()
524
    {
525
        if (count($this->events) or count($this->events_label)) {
526
            if (count($this->events)) {
527
                foreach ($this->events as $event => $action) {
528
                    $this->getJS()->ready('$("#' . $this->id . '").' . $event . '(function(){ ' . $action . ' });');
529
                }
530
            }
531
532
            if (count($this->events_label)) {
533
                foreach ($this->events_label as $event => $action) {
534
                    $this->getJS()->ready('$("#' . $this->id . '").' . $event . '(function(){ ' . $action . ' });');
535
                }
536
            }
537
        }
538
    }
539
540
    /**
541
     * Renders the title
542
     */
543
    protected function renderTitle()
544
    {
545
        if ($this->show_label) {
546
            $this->result .= '<span' . $this->renderAttributes($this->span_attributes) . '>';
547
            if (array_key_exists('label_style', $this->params) && $this->params['label_style'] == 'small') {
548
                $this->result .= '<small>';
549
            }
550
            $this->result .= $this->params['title'] . $this->required;
551
            if (array_key_exists('label_style', $this->params) && $this->params['label_style'] == 'small') {
552
                $this->result .= '</small>';
553
            }
554
            $this->result .= '</span>';
555
556
            if ($this->params['inline'] == false && $this->params['title'] != ''
557
                && $this->params['label_position'] == 'before'
558
            ) {
559
                $this->result .= '<br />';
560
            }
561
        }
562
    }
563
564
    /**
565
     * Render the inner field
566
     */
567
    protected function renderInner()
568
    {
569
        $this->result .= '<input' . $this->renderAttributes($this->input_attributes) . ' />';
570
    }
571
572
    /**
573
     * Renders the input attributes
574
     */
575
    protected function inputAttributes()
576
    {
577
        $this->input_attributes['name'] = $this->name;
578
        $this->input_attributes['id'] = $this->id;
579
        $this->input_attributes['type'] = $this->params['type'];
580
581
        if (array_key_exists('enabled', $this->params)
582
            && $this->params['enabled'] == false
583
        ) {
584
            $this->input_attributes['disabled'] = 'disabled';
585
            $this->input_attributes['class'][] = 'disabled';
586
        }
587
588
        if (array_key_exists('writable', $this->params)
589
            && $this->params['writable'] == false
590
        ) {
591
            $this->input_attributes['readonly'] = 'readonly';
592
            $this->input_attributes['class'][] = 'readonly';
593
        }
594
595
        if (array_key_exists('onchange', $this->params)) {
596
            $this->input_attributes['onchange'] = $this->params['onchange'];
597
        }
598
599
        if (array_key_exists('placeholder', $this->params)) {
600
            $this->input_attributes['placeholder'] = $this->params['placeholder'];
601
        }
602
    }
603
604
    protected function labelAttributes()
605
    {
606
        $this->label_attributes['for'] = $this->id;
607
    }
608
609
    /**
610
     * Take all data attributes and render them to the input item
611
     */
612
    protected function inputDataAttributes()
613
    {
614
        foreach ($this->params['data_attributes'] as $key => $value) {
615
            $this->input_attributes['data-' . $key] = $value;
616
        }
617
    }
618
619
    /**
620
     * Renders the input tip
621
     */
622
    protected function inputTip()
623
    {
624
        if (isset($this->params['tip']) && $this->params['tip'] != '') {
625
            $this->input_attributes['title'] = json_encode($this->params['tip']);
626
        }
627
    }
628
629
    /**
630
     * Renders the special attributes
631
     *
632
     * @param  array $attr
633
     * @return string
634
     */
635
    protected function renderAttributes($attr)
636
    {
637
        if (array_key_exists('class', $attr) && is_array($attr['class'])) {
638
            $attr['class'] = implode(' ', $attr['class']);
639
        }
640
641
        if (array_key_exists('style', $attr) && is_array($attr['style'])) {
642
            $out = '';
643
            foreach ($attr['style'] as $key => $value) {
644
                $out .= $key . ':' . $value . '; ';
645
            }
646
            $attr['style'] = $out;
647
        }
648
649
        $out = ' ';
650
        foreach ($attr as $key => $value) {
651
            $out .= $key . '="' . $value . '" ';
652
        }
653
654
        return $out;
655
    }
656
}
657