Completed
Push — master ( f2cd13...82e8ab )
by Mihail
03:03
created

Form   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 194
Duplicated Lines 1.55 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 32
Bugs 15 Features 1
Metric Value
wmc 30
c 32
b 15
f 1
lcom 1
cbo 13
dl 3
loc 194
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 30 8
A start() 0 8 2
C field() 3 66 11
A submitButton() 0 7 1
C finish() 0 27 8

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Ffcms\Core\Helper\HTML;
4
5
use Ffcms\Core\App;
6
use Ffcms\Core\Exception\NativeException;
7
use Ffcms\Core\Exception\SyntaxException;
8
use Ffcms\Core\Helper\HTML\System\NativeGenerator;
9
use Ffcms\Core\Helper\Security;
10
use Ffcms\Core\Helper\Type\Arr;
11
use Ffcms\Core\Helper\FileSystem\File;
12
use Ffcms\Core\Helper\Type\Obj;
13
use Ffcms\Core\Helper\Type\Str;
14
use Ffcms\Core\Arch\Model;
15
use Ffcms\Core\Helper\HTML\Form\Constructor;
16
17
/**
18
 * Class Form. Simple HTML model generator for fast form building.
19
 */
20
class Form extends NativeGenerator
21
{
22
    /** @var array */
23
    private static $structLayer = [
24
        'base' => 'native/form/base_layer',
25
        'checkbox' => 'native/form/checkbox_layer',
26
        'checkboxes' => 'native/form/checkboxes_layer',
27
        'radio' => 'native/form/radio_layer',
28
        'jsnotify' => 'native/form/jsnotify'
29
    ];
30
31
    /** @var string */
32
    private $name;
33
    private $formProperty = [];
34
    /** @var Model */
35
    private $model;
36
37
38
    /**
39
     * Form constructor. Build form based on model properties
40
     * @param Model $model
41
     * @param array|null $property
42
     * @param array|null $layerFiles
43
     * @throws SyntaxException
44
     */
45
    public function __construct($model, array $property = null, array $layerFiles = null)
46
    {
47
        // prevent white-screen locks when model is not passed or passed wrong
48
        if (!$model instanceof Model) {
49
            throw new SyntaxException('Bad model type passed in form builder. Check for init: new Form()');
50
        }
51
52
        $this->model = $model;
53
        $this->name = $model->getFormName();
54
        
55
        // check if passed custom layer file
56
        if (Obj::isArray($layerFiles) && count($layerFiles) > 0) {
57
            foreach (array_keys(static::$structLayer) as $type) {
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
58
                if (isset($layerFiles[$type]) && Obj::isString($layerFiles[$type])) {
59
                    static::$structLayer[$type] = $layerFiles[$type];
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
60
                }
61
            }
62
        }
63
        // set model submit method
64
        $property['method'] = $this->model->getSubmitMethod();
65
66
        $property['id'] = $this->name; // define form id
67
        // if action is not defined - define it
68
        if (Str::likeEmpty($property['action'])) {
69
            $property['action'] = App::$Request->getFullUrl();
70
        }
71
72
        // set property global for this form
73
        $this->formProperty = $property;
74
    }
75
76
    /**
77
     * Open form tag with prepared properties
78
     * @return string
79
     */
80
    public function start()
81
    {
82
        $form = self::buildSingleTag('form', $this->formProperty, false);
83
        if ($this->model->_tokenRequired) {
84
            $form .= PHP_EOL . $this->field('_csrf_token', 'hidden', ['value' => $this->model->_csrf_token]);
85
        }
86
        return $form;
87
    }
88
89
    /**
90
     * Display form field. Allowed type: text, password, textarea, checkbox, select, checkboxes, file, captcha, email, hidden
91
     * @param $object
92
     * @param $type
93
     * @param null|array $property
94
     * @param null|string $helper
95
     * @param null|string $layerFile
96
     * @return null|string
97
     * @throws NativeException
98
     * @throws SyntaxException
99
     */
100
    public function field($object, $type, $property = null, $helper = null, $layerFile = null)
101
    {
102
        if ($this->model === null) {
103
            if (App::$Debug !== null) {
104
                App::$Debug->addMessage('Form model is not defined for field name: [' . strip_tags($object) . ']');
105
            }
106
            return null;
107
        }
108
109
        // can be dots separated object
110
        $propertyName = $object;
111
        if (Str::contains('.', $propertyName)) {
112
            $propertyName = strstr($propertyName, '.', true);
113
        }
114
115
        // check if model contains current tag name as property
116
        if (!property_exists($this->model, $propertyName)) {
117 View Code Duplication
            if (App::$Debug !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
118
                App::$Debug->addMessage('Form field ["' . $object . '"] is not defined in model: [' . get_class($this->model) . ']', 'error');
119
            }
120
            return null;
121
        }
122
        
123
        // prepare layer template file path
124
        if ($layerFile === null) {
125
            switch ($type) {
126
                case 'checkbox':
127
                    $layerFile = static::$structLayer['checkbox'];
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
128
                    break;
129
                case 'radio':
130
                    $layerFile = static::$structLayer['radio'];
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
131
                    break;
132
                default:
133
                    $layerFile = static::$structLayer['base'];
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
134
                    break;
135
            }
136
        }
137
        
138
        // prepare labels text and label "for" attr 
139
        $labelFor = $this->name . '-' . $propertyName;
140
        $labelText = $this->model->getLabel($object);
141
        $itemValue = $this->model->{$propertyName};
142
        // sounds like a dot-separated $object
143
        if ($propertyName !== $object) {
144
            $nesting = trim(strstr($object, '.'), '.');
145
            $labelFor .= '-' . Str::replace('.', '-', $nesting);
146
            $itemValue = Arr::getByPath($nesting, $itemValue);
147
        }
148
149
        // initialize form fields constructor and build output dom html value
150
        $constructor = new Constructor($this->model, $this->name, $type);
0 ignored issues
show
Documentation introduced by
$this->name is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
151
        $elementDOM = $constructor->makeTag($object, $itemValue, $property);
152
        
153
        // if item is hidden - return tag without assign of global template
154
        if ($type === 'hidden') {
155
            return $elementDOM;
156
        }
157
        
158
        // render output viewer
159
        return App::$View->render($layerFile, [
160
            'name' => $labelFor,
161
            'label' => $labelText,
162
            'item' => $elementDOM,
163
            'help' => self::nohtml($helper)
164
        ]);
165
    }
166
167
    /**
168
     * Display submit button for current form
169
     * @param string $title
170
     * @param array $property
171
     * @return string
172
     */
173
    public function submitButton($title, array $property = [])
174
    {
175
        $property['type'] = 'submit';
176
        $property['name'] = $this->name . '[submit]';
177
        $property['value'] = $title;
178
        return self::buildSingleTag('input', $property);
179
    }
180
181
    /**
182
     * Finish current form.
183
     * @param bool $validate
184
     * @return string
185
     */
186
    public function finish($validate = true)
187
    {
188
        // pre-validate form fields based on model rules and jquery.validation
189
        if ($validate) {
190
            App::$Alias->addPlainCode('js', '$().ready(function() { $("#' . $this->name . '").validate(); });');
191
            App::$Alias->setCustomLibrary('js', '/vendor/bower/jquery-validation/dist/jquery.validate.min.js');
192
            if (App::$Request->getLanguage() !== 'en') {
193
                $localeFile = '/vendor/bower/jquery-validation/src/localization/messages_' . App::$Request->getLanguage() . '.js';
194
                if (File::exist($localeFile)) {
195
                    App::$Alias->setCustomLibrary('js', $localeFile);
196
                }
197
            }
198
            // if model is not empty - add js error color notification
199
            if ($this->model !== null) {
200
                $badAttr = $this->model->getBadAttribute();
201
                $formName = $this->model->getFormName();
202
                if (Obj::isArray($badAttr) && count($badAttr) > 0) {
203
                    foreach ($badAttr as $attr) {
0 ignored issues
show
Bug introduced by
The expression $badAttr of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
204
                        $itemId = $formName . '-' . $attr;
205
                        $render = App::$View->render(static::$structLayer['jsnotify'], ['itemId' => $itemId]);
0 ignored issues
show
Bug introduced by
Since $structLayer is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $structLayer to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
206
                        App::$Alias->addPlainCode('js', $render);
207
                    }
208
                }
209
            }
210
        }
211
        return '</form>';
212
    }
213
}