Issues (127)

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.

Form.php (13 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 Amarkal\UI;
4
5
/**
6
 * Implements a Form controller.
7
 * 
8
 * The form object is used to encapsulate UI components and the update/validation 
9
 * process into a single entity.
10
 */
11
class Form
12
{
13
    /**
14
     * The list of AbstractComponent type objects to be updated.
15
     * 
16
     * @var AbstractComponent[] objects array.
17
     */
18
    private $component_list;
19
    
20
    /**
21
     * The old component values array. These values are used if the new values
22
     * are invalid.
23
     * Structure: component_name => component_value
24
     * 
25
     * @var array Old values array.
26
     */
27
    private $old_instance;
28
    
29
    /**
30
     * The new component values array.
31
     * Structure: component_name => component_value
32
     * 
33
     * @var array New values array.
34
     */
35
    private $new_instance;
36
    
37
    /**
38
     * The final values array, after filtering and validation.
39
     * This is returned by the update function.
40
     * Structure: component_name => component_value
41
     * 
42
     * @var array Final values array. 
43
     */
44
    private $final_instance = array();
45
    
46
    /**
47
     * Array of names of components that were invalid, and the error message 
48
     * recieved.
49
     * Structure: component_name => error_message
50
     * 
51
     * @var string[] Array of error messages. 
52
     */
53
    private $errors = array();
54
    
55
    /**
56
     * When instantiating a form, a list of component arguments arrays must be 
57
     * provided. Each arguments array must have a 'type' argument, in addition 
58
     * to the component's original arguments.
59
     * 
60
     * @param ComponentList $component_list
61
     */
62
    public function __construct( ComponentList $component_list = null )
63
    {
64
        if(null === $component_list)
65
        {
66
            $component_list = new ComponentList();
67
        }
68
        $this->component_list = $component_list;
0 ignored issues
show
Documentation Bug introduced by
It seems like $component_list of type object<Amarkal\UI\ComponentList> is incompatible with the declared type array<integer,object<Ama...\UI\AbstractComponent>> of property $component_list.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
69
    }
70
    
71
    /**
72
     * Get the updated component values (validated, filtered or ignored).
73
     * 
74
     * Loops through each component and acts according to its type:
75
     * - Disableable components are ignored if they are disabled.
76
     * - Validatable components are validated using their validation function. 
77
     *   If the new value is invalid, the old value will be used.
78
     * - Filterable components are filtered using their filter function.
79
     * - Non-value components are skipped altogether.
80
     * 
81
     * Each component is also set with its new value.
82
     * 
83
     * @param array $new_instance The new component values array.
84
     * @param array $old_instance The old component values array.
85
     * 
86
     * @return array The updated values array.
87
     */
88
    public function update( array $new_instance = array(), array $old_instance = array() )
89
    {
90
        $this->old_instance   = array_merge($this->get_defaults(),$old_instance);
91
        $this->new_instance   = array_merge($this->old_instance,$new_instance);
92
        $this->final_instance = $this->new_instance;
93
        
94
        foreach ( $this->component_list->get_value_components() as $component ) 
0 ignored issues
show
The method get_value_components cannot be called on $this->component_list (of type array<integer,object<Ama...\UI\AbstractComponent>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
95
        {
96
            // Update individual fields, as well as the composite parent field.
97
            $this->update_component( $component );
98
        }
99
        
100
        return $this->final_instance;
101
    }
102
    
103
    /**
104
     * Reset all fields to their default values.
105
     * 
106
     * @return array The updated values array.
107
     */
108
    public function reset()
109
    {
110
        return $this->reset_components(
111
            $this->component_list->get_value_components()
0 ignored issues
show
The method get_value_components cannot be called on $this->component_list (of type array<integer,object<Ama...\UI\AbstractComponent>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
112
        );
113
    }
114
115
    /**
116
     * Reset the given list of components to their default values.
117
     *
118
     * @param array $components
119
     * @return void
120
     */
121
    public function reset_components( array $components )
122
    {
123
        foreach( $components as $component )
124
        {
125
            $this->reset_component($component);
126
        }
127
        return $this->final_instance;
128
    }
129
130
    /**
131
     * Reset the given component to its default value.
132
     *
133
     * @param AbstractComponent $component
134
     * @return void
135
     */
136
    public function reset_component( $component )
137
    {
138
        $component->value = $component->default;
0 ignored issues
show
The property value does not exist on object<Amarkal\UI\AbstractComponent>. Since you implemented __set, maybe consider adding a @property annotation.

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...
The property default does not exist on object<Amarkal\UI\AbstractComponent>. Since you implemented __set, maybe consider adding a @property annotation.

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...
139
        $this->final_instance[$component->name] = $component->default;
0 ignored issues
show
The property name does not exist on object<Amarkal\UI\AbstractComponent>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
The property default does not exist on object<Amarkal\UI\AbstractComponent>. Since you implemented __set, maybe consider adding a @property annotation.

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...
140
        return $this->final_instance;
141
    }
142
    
143
    /**
144
     * Get the list of error messages for components that could not be validated.
145
     * Structure: components_name => error_message
146
     * 
147
     * @return string[] Array of error messages. 
148
     */
149
    public function get_errors()
150
    {
151
        return $this->errors;
152
    }
153
    
154
    /**
155
     * Get all components.
156
     * 
157
     * @return array
158
     */
159
    public function get_component_list()
160
    {
161
        return $this->component_list;
162
    }
163
    
164
    /**
165
     * Update the component's value with the new value.
166
     * NOTE: this function also updates the $final_instance
167
     * array.
168
     * 
169
     * @param ValueComponentInterface $component The component to validate.
170
     */
171
    private function update_component( ValueComponentInterface $component )
172
    {
173
        $component->value = $this->final_instance[$component->name];
0 ignored issues
show
Accessing value on the interface Amarkal\UI\ValueComponentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Accessing name on the interface Amarkal\UI\ValueComponentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
174
        
175
        // Skip if this field is disabled
176
        if( $this->is_disabled($component) )
0 ignored issues
show
$component is of type object<Amarkal\UI\ValueComponentInterface>, but the function expects a object<Amarkal\UI\UI\AbstractComponent>.

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...
177
        {
178
            return;
179
        }
180
181
        // Apply user-defined filter
182
        $this->filter( $component );
0 ignored issues
show
$component is of type object<Amarkal\UI\ValueComponentInterface>, but the function expects a object<Amarkal\UI\UI\AbstractComponent>.

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...
183
        
184
        // Validate value
185
        $this->validate( $component );
0 ignored issues
show
$component is of type object<Amarkal\UI\ValueComponentInterface>, but the function expects a object<Amarkal\UI\UI\AbstractComponent>.

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...
186
    }
187
    
188
    /**
189
     * Check if the given component is disabled.
190
     * 
191
     * @param UI\AbstractComponent $component
192
     * @return boolean
193
     */
194
    private function is_disabled( $component )
195
    {
196
        return 
197
            $component instanceof DisableableComponentInterface &&
198
            (
199
                true === $component->disabled ||
200
                true === $component->readonly
201
            );
202
    }
203
204
    /**
205
     * Filter the component's value using its filter function (if applicable)
206
     * 
207
     * @param UI\AbstractComponent $component
208
     */
209
    private function filter( $component )
210
    {
211
        if( $component instanceof FilterableComponentInterface )
212
        {
213
            $filter = $component->filter;
214
215
            if( is_callable( $filter ) ) 
216
            {
217
                $component->value = \call_user_func_array($filter, array($this->final_instance[$component->name]));
218
                $this->final_instance[$component->name] = $component->value;
219
            }
220
        }
221
    }
222
    
223
    /**
224
     * Validate the component's value using its validation function.
225
     * 
226
     * If the value is invalid, the old value is used, and an error message is
227
     * saved into the errors array as component_name => error_message.
228
     * 
229
     * @param UI\AbstractComponent $component The component to validate.
230
     */
231
    private function validate( $component )
232
    {
233
        if( !($component instanceof ValidatableComponentInterface) ) return;
234
        
235
        $name     = $component->name;
236
        $validate = $component->validation;
237
        
238
        $component->validity = $component::VALID;
239
        
240
        if(is_callable($validate))
241
        {
242
            $error = '';
243
            $valid = \call_user_func_array($validate, array($this->final_instance[$name], &$error));
244
            
245
            // Invalid input, use old instance or default value
246
            if ( true !== $valid ) 
247
            {
248
                $this->errors[$name]         = $error ? $error : ValidatableComponentInterface::DEFAULT_MESSAGE;
249
                $component->value            = $this->old_instance[$name];
250
                $this->final_instance[$name] = $this->old_instance[$name];
251
                $component->set_validity($component::INVALID);
252
            }
253
        }
254
    }
255
    
256
    /**
257
     * Get the default values for all form components as an array of name => default_value
258
     * 
259
     * @return array
260
     */
261
    private function get_defaults()
262
    {
263
        $defaults = array();
264
        
265
        foreach( $this->component_list->get_value_components() as $component )
0 ignored issues
show
The method get_value_components cannot be called on $this->component_list (of type array<integer,object<Ama...\UI\AbstractComponent>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
266
        {
267
            $defaults[$component->name] = $component->default;
268
        }
269
        
270
        return $defaults;
271
    }
272
}