Test Failed
Pull Request — master (#4)
by
unknown
11:21
created

mergePresetTargetObjectTypes()   C

Complexity

Conditions 12
Paths 54

Size

Total Lines 55
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 6.8009
c 0
b 0
f 0
cc 12
eloc 32
nc 54
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Charcoal\Relation\Traits;
4
5
use InvalidArgumentException;
6
7
// From 'charcoal-config'
8
use Charcoal\Config\ConfigInterface;
9
10
// From 'charcoal-core'
11
use Charcoal\Model\ModelInterface;
12
13
// From 'charcoal-admin'
14
use Charcoal\Admin\Ui\ObjectContainerInterface;
15
16
// From 'charcoal-cms'
17
use Charcoal\Relation\RelationConfig;
18
19
/**
20
 * Configurable Relation Trait
21
 *
22
* An implementation, as Trait, of the {@see \Charcoal\Config\ConfigurableInterface}.
23
*/
24
trait ConfigurableRelationTrait
25
{
26
    /**
27
     * The relation configset.
28
     *
29
     * @var ConfigInterface
30
     */
31
    private $config;
32
33
    /**
34
     * Set the object's configuration container.
35
     *
36
     * @param  ConfigInterface|array $config The datas to set.
37
     * @throws InvalidArgumentException If the parameter is invalid.
38
     * @return ConfigurableInterface Chainable
39
     */
40
    public function setConfig($config)
41
    {
42
        if (is_array($config)) {
43
            $this->config = $this->createConfig($config);
44
        } elseif ($config instanceof ConfigInterface) {
45
            $this->config = $config;
46
        } else {
47
            throw new InvalidArgumentException(
48
                'Configuration must be an array or a ConfigInterface object.'
49
            );
50
        }
51
        return $this;
52
    }
53
54
    /**
55
     * Retrieve the object's configuration container, or one of its entry.
56
     *
57
     * If the object has no existing config, create one.
58
     *
59
     * If a key is provided, return the configuration key value instead of the full object.
60
     *
61
     * @param  string $key Optional. If provided, the config key value will be returned, instead of the full object.
62
     * @return ConfigInterface
63
     */
64
    public function config($key = null)
65
    {
66
        if ($this->config === null) {
67
            $this->config = $this->createConfig();
68
        }
69
70
        if ($key !== null) {
71
            return $this->config->get($key);
72
        } else {
73
            return $this->config;
74
        }
75
    }
76
77
    /**
78
     * Retrieve a new RelationConfig instance for the class.
79
     *
80
     * @param  array|null $data Optional data to pass to the new configset.
81
     * @return ConfigInterface
82
     */
83
    protected function createConfig($data = null)
84
    {
85
        return new RelationConfig($data);
86
    }
87
88
    /**
89
     * Parse the given data and recursively merge presets from relation config.
90
     *
91
     * @param  array $data The widget data.
92
     * @return array Returns the merged widget data.
93
     */
94
    protected function mergePresets(array $data)
95
    {
96
        if (isset($data['target_object_types'])) {
97
            $data['target_object_types'] = $this->mergePresetTargetObjectTypes($data['target_object_types']);
98
        }
99
100
        return $data;
101
    }
102
103
    /**
104
     * Parse the given data and merge the preset object types.
105
     *
106
     * @param  string|array $data The target object type data.
107
     * @throws InvalidArgumentException If the object type or structure is invalid.
108
     * @return array Returns the merged target object type data.
109
     */
110
    private function mergePresetTargetObjectTypes($data)
111
    {
112
        if (is_string($data)) {
113
            $groupIdent = $data;
114
            if ($this instanceof ObjectContainerInterface) {
0 ignored issues
show
Bug introduced by
The class Charcoal\Admin\Ui\ObjectContainerInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
115
                if ($this->hasObj()) {
116
                    $groupIdent = $this->obj()->render($groupIdent);
117
                }
118
            } elseif ($this instanceof ModelInterface) {
119
                $groupIdent = $this->render($groupIdent);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Charcoal\Model\ModelInterface as the method render() does only exist in the following implementations of said interface: Charcoal\Attachment\Object\Accordion, Charcoal\Attachment\Object\Attachment, Charcoal\Attachment\Object\Category\Generic, Charcoal\Attachment\Object\Container, Charcoal\Attachment\Object\Embed, Charcoal\Attachment\Object\File, Charcoal\Attachment\Object\Gallery, Charcoal\Attachment\Object\Image, Charcoal\Attachment\Object\Join, Charcoal\Attachment\Object\Link, Charcoal\Attachment\Object\Text, Charcoal\Attachment\Object\Video, Charcoal\Cms\AbstractEvent, Charcoal\Cms\AbstractFaq, Charcoal\Cms\AbstractNews, Charcoal\Cms\AbstractSection, Charcoal\Cms\Config, Charcoal\Cms\EmptySection, Charcoal\Cms\Event, Charcoal\Cms\EventCategory, Charcoal\Cms\ExternalSection, Charcoal\Cms\Faq, Charcoal\Cms\FaqCategory, Charcoal\Cms\News, Charcoal\Cms\NewsCategory, Charcoal\Cms\Section, Charcoal\Cms\Section\BlocksSection, Charcoal\Cms\Section\ContentSection, Charcoal\Cms\Tag, Charcoal\Model\AbstractModel, Charcoal\Model\Model, Charcoal\Object\Content, Charcoal\Object\ObjectRevision, Charcoal\Object\ObjectRoute, Charcoal\Object\ObjectSchedule, Charcoal\Object\UserData, Charcoal\Relation\Pivot.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
120
            }
121
122
            $presetGroups = $this->config('groups');
123
            if (isset($presetGroups[$groupIdent])) {
124
                $data = $presetGroups[$groupIdent];
125
            }
126
        }
127
128
        if (is_array($data)) {
129
            $presetTypes = $this->config('targetObjectTypes');
130
            $targetObjectTypes = [];
131
            foreach ($data as $type => $struct) {
132
                if (is_string($struct)) {
133
                    $type = $struct;
134
                    $struct = [];
135
                }
136
137
                if (!is_string($type)) {
138
                    throw new InvalidArgumentException(
139
                        'The object type must be a string'
140
                    );
141
                }
142
143
                if (!is_array($struct)) {
144
                    throw new InvalidArgumentException(sprintf(
145
                        'The object structure for "%s" must be an array',
146
                        $type
147
                    ));
148
                }
149
150
                if (isset($presetTypes[$type])) {
151
                    $struct = array_replace_recursive(
152
                        $presetTypes[$type],
153
                        $struct
154
                    );
155
                }
156
157
                $targetObjectTypes[$type] = $struct;
158
            }
159
160
            $data = $targetObjectTypes;
161
        }
162
163
        return $data;
164
    }
165
}
166