Completed
Push — master ( d9dfd9...4ffba9 )
by
unknown
01:27
created

Aeria/Field/Fields/BaseField.php (4 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 Aeria\Field\Fields;
4
5
use Aeria\Aeria;
6
use Aeria\Field\FieldError;
7
use Aeria\Validator\Validator;
8
use Aeria\Structure\Node;
9
use Aeria\Field\Interfaces\FieldInterface;
10
11
/**
12
 * BaseField is the class that represents every Aeria field.
13
 *
14
 * @category Field
15
 *
16
 * @author   Andrea Longo <[email protected]>
17
 * @license  https://github.com/caffeinalab/aeria/blob/master/LICENSE  MIT license
18
 *
19
 * @see     https://github.com/caffeinalab/aeria
20
 */
21
class BaseField extends Node implements FieldInterface
22
{
23
    protected $original_config;
24
25
    public $is_multiple_field = false;
26
27
    /**
28
     * Transform the config array; note that this does not operate on
29
     * `$this->config`: this way it can be called from outside.
30
     *
31
     * @param array $config the field's config
32
     *
33
     * @return array the transformed config
34
     */
35
    public static function transformConfig(array $config)
36
    {
37
        return $config;
38
    }
39
40
    /**
41
     * Constructs the field.
42
     *
43
     * @param string $parent_key the field's parent key
44
     * @param array  $config     the field's config
45
     * @param array  $sections   Aeria's sections config
46
     * @param array  $index      index for multiple fields
47
     *
48
     * @since  Method available since Release 3.0.0
49
     */
50
    public function __construct($parent_key, $config, $sections, $index = null)
51
    {
52
        $this->parent_key = $parent_key;
53
        $this->original_config = isset($config['original_config']) ? $config['original_config'] : $config;
54
55
        unset($config['original_config']);
56
57
        $this->config = $config;
58
59
        $this->id = isset($config['id'])
60
            ? $config['id']
61
            : null;
62
        $this->index = $index;
63
        $this->key = $this->getKey();
64
        $this->sections = $sections;
65
    }
66
67
    /**
68
     * Checks whether a field should be child of another.
69
     *
70
     * @param Node $possible_parent the field's possible parent
71
     *
72
     * @return bool whether the field should be child of $possible_parent
73
     *
74
     * @since  Method available since Release 3.0.0
75
     */
76
    public function shouldBeChildOf(Node $possible_parent)
77
    {
78
        if ($possible_parent->is_multiple_field) {
79
            if (preg_match('/^'.$possible_parent->getKey().'.{1,}/', $this->getKey())) {
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class Aeria\Structure\Node as the method getKey() does only exist in the following sub-classes of Aeria\Structure\Node: Aeria\Field\Fields\BaseField, Aeria\Field\Fields\DateRangeField, Aeria\Field\Fields\FieldsetField, Aeria\Field\Fields\GalleryField, Aeria\Field\Fields\MapField, Aeria\Field\Fields\MediaField, Aeria\Field\Fields\PictureField, Aeria\Field\Fields\PostTypesField, Aeria\Field\Fields\RelationField, Aeria\Field\Fields\RepeaterField, Aeria\Field\Fields\SectionsField, Aeria\Field\Fields\SelectField, Aeria\Field\Fields\SwitchField, Aeria\Field\Fields\TermsField. 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...
80
                return true;
81
            } else {
82
                return false;
83
            }
84
        } elseif (get_class($possible_parent) == 'RootNode') { // Check if possible_parent is root
85
            return true;
86
        } else {
87
            return false;
88
        }
89
    }
90
91
    /**
92
     * Gets the field full key.
93
     *
94
     * @return string the field's key
95
     *
96
     * @since  Method available since Release 3.0.0
97
     */
98
    public function getKey()
99
    {
100
        return $this->parent_key
101
            .(!is_null($this->index) ? '-'.$this->index : '')
102
            .(!is_null($this->id) ? '-'.$this->id : '');
103
    }
104
105
    /**
106
     * Gets the field's value.
107
     *
108
     * @param array $saved_fields the FieldGroup's saved fields
109
     * @param bool  $skip_filter  whether to skip or not WP's filter
110
     *
111
     * @return mixed the field's value
112
     *
113
     * @since  Method available since Release 3.0.0
114
     */
115
    public function get(array $saved_fields, bool $skip_filter = false)
116
    {
117
        if (!isset($saved_fields[$this->key])) {
118
            return null;
119
        }
120
        if (!$skip_filter) {
121
            $saved_fields[$this->key] = apply_filters('aeria_get_base', $saved_fields[$this->key], $this->config);
122
            $saved_fields[$this->key] = apply_filters('aeria_get_'.$this->key, $saved_fields[$this->key], $this->config);
123
        }
124
        if (is_array($saved_fields[$this->key])) {
125
            return $saved_fields[$this->key][0];
126
        } else {
127
            return $saved_fields[$this->key];
128
        }
129
    }
130
131
    /**
132
     * Gets the field's value and its errors.
133
     *
134
     * @param array $saved_fields the FieldGroup's saved fields
135
     * @param array $errors       the saving errors
136
     *
137
     * @return array the field's config, hydrated with values and errors
138
     *
139
     * @since  Method available since Release 3.0.0
140
     */
141
    public function getAdmin(array $saved_fields, array $errors)
142
    {
143
        if (isset($errors[$this->key])) {
144
            $result = [
145
                'value' => $errors[$this->key]['value'],
146
                'error' => $errors[$this->key]['message'],
147
            ];
148
        } else {
149
            $result = [
150
                'value' => $this->get($saved_fields, true),
151
            ];
152
        }
153
154
        $config = array_merge(
155
            $this->config,
156
            $result
157
        );
158
159
        $config = apply_filters('aeria_get_admin_base', $config);
160
        $config = apply_filters('aeria_get_admin_'.$this->id, $config);
161
        $config = apply_filters('aeria_get_admin_'.$this->key, $config);
162
163
        return $config;
164
    }
165
166
    /**
167
     * Saves the new values to the fields.
168
     *
169
     * @param int       $context_ID        the context ID. For posts, post's ID
170
     * @param string    $context_type      the context type. Right now, options|meta
171
     * @param array     $saved_fields      the saved fields
172
     * @param array     $new_values        the values we're saving
173
     * @param Validator $validator_service Aeria's validator service
174
     * @param Query     $query_service     Aeria's query service
175
     *
176
     * @return array the results of the saving
177
     *
178
     * @since  Method available since Release 3.0.0
179
     */
180
    public function set($context_ID, $context_type, array $saved_fields, array $new_values, $validator_service, $query_service)
181
    {
182
        $value = isset($new_values[$this->key]) ? $new_values[$this->key] : null;
183
        $old = isset($saved_fields[$this->key][0]) ? $saved_fields[$this->key][0] : '';
184
        $value = apply_filters('aeria_set_'.$this->key, $value, $this->config);
185
        if ($value == $old) {
186
            return ['value' => $value];
187
        }
188
        if (is_null($value) || $value == '') {
189
            $this->deleteField($context_ID, $context_type, $query_service);
190
191
            return ['value' => $value];
192
        } else {
193
            $validators = (isset($this->config['validators'])) ? $this->config['validators'] : '';
194
            $error = $validator_service->validate($value, $validators);
195
196
            if (!$error['status']) {
197
                $this->saveField($context_ID, $context_type, $value, $old);
198
199
                return ['value' => $value];
200
            } else {
201
                FieldError::make($context_ID)
202
                    ->addError($this->key, $error);
203
204
                return $error;
205
            }
206
        }
207
    }
208
209
    /**
210
     * Saves a single field to the DB.
211
     *
212
     * @param int    $context_ID   the context ID. For posts, post's ID
213
     * @param string $context_type the context type. Right now, options|meta
214
     * @param mixed  $value        the new value
215
     * @param mixed  $old          the old value
216
     *
217
     * @throws Exception if the node context is invalid
218
     *
219
     * @since  Method available since Release 3.0.0
220
     */
221 View Code Duplication
    private function saveField($context_ID, $context_type, $value, $old)
0 ignored issues
show
This method seems to be duplicated in 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...
222
    {
223
        switch ($context_type) {
224
        case 'options':
225
            update_option($this->key, $value);
226
            break;
227
        case 'meta':
228
            update_post_meta($context_ID, $this->key, $value, $old);
229
            break;
230
        default:
231
            throw new Exception('Node context is not valid.');
232
            break;
233
        }
234
    }
235
236
    /**
237
     * Deletes a field value.
238
     *
239
     * @param int    $context_ID    the context ID. For posts, post's ID
240
     * @param string $context_type  the context type. Right now, options|meta
241
     * @param Query  $query_service Aeria's query service
242
     *
243
     * @throws Exception if the node context is invalid
244
     *
245
     * @since  Method available since Release 3.0.0
246
     */
247 View Code Duplication
    private function deleteField($context_ID, $context_type, $query_service)
0 ignored issues
show
The parameter $query_service is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
This method seems to be duplicated in 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...
248
    {
249
        switch ($context_type) {
250
        case 'options':
251
            delete_option($this->key);
252
            break;
253
        case 'meta':
254
            delete_post_meta($context_ID, $this->key);
255
            break;
256
257
        default:
258
            throw new Exception('Node context is not valid.');
259
            break;
260
        }
261
    }
262
}
263