Completed
Push — master ( c33e51...621ab0 )
by lan tian
12s
created

Agent   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 277
Duplicated Lines 5.78 %

Coupling/Cohesion

Components 2
Dependencies 1

Test Coverage

Coverage 93.33%

Importance

Changes 0
Metric Value
wmc 40
lcom 2
cbo 1
dl 16
loc 277
ccs 56
cts 60
cp 0.9333
rs 8.2608
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A reset() 0 8 1
A config() 8 8 3
A params() 8 8 3
B sendSms() 0 12 5
B sendVoice() 0 16 9
A formatMobile() 0 6 2
B curlPost() 0 17 5
A curlGet() 0 9 2
A curl() 0 23 3
A result() 0 12 4
A __get() 0 4 1
A __isset() 0 4 1

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Agent often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Agent, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Toplan\PhpSms;
4
5
abstract class Agent
6
{
7
    const SUCCESS = 'success';
8
    const INFO = 'info';
9
    const CODE = 'code';
10
11
    /**
12
     * The configuration information.
13
     *
14
     * @var array
15
     */
16
    protected $config = [];
17
18
    /**
19
     * The custom params of request.
20
     *
21
     * @var array
22
     */
23
    protected $params = [];
24
25
    /**
26
     * The result data.
27
     *
28
     * @var array
29
     */
30
    protected $result = [];
31
32
    /**
33
     * Constructor.
34
     *
35
     * @param array $config
36
     */
37 27
    public function __construct(array $config = [])
38
    {
39 27
        $this->reset();
40 27
        $this->config($config);
41 27
    }
42
43
    /**
44
     * Reset states.
45
     */
46 42
    public function reset()
47
    {
48 42
        $this->result = [
49 42
            self::SUCCESS => false,
50 42
            self::INFO    => null,
51 42
            self::CODE    => 0,
52
        ];
53 42
    }
54
55
    /**
56
     * Get or set the configuration information.
57
     *
58
     * @param string|array $key
59
     * @param mixed        $value
60
     * @param bool         $override
61
     *
62
     * @return mixed
63
     */
64 27 View Code Duplication
    public function config($key = null, $value = null, $override = false)
0 ignored issues
show
Duplication introduced by
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...
65
    {
66 27
        if (is_array($key) && is_bool($value)) {
67 3
            $override = $value;
68 2
        }
69
70 27
        return Util::operateArray($this->config, $key, $value, null, null, $override);
71
    }
72
73
    /**
74
     * Get or set the custom params.
75
     *
76
     * @param string|array $key
77
     * @param mixed        $value
78
     * @param bool         $override
79
     *
80
     * @return mixed
81
     */
82 27 View Code Duplication
    public function params($key = null, $value = null, $override = false)
0 ignored issues
show
Duplication introduced by
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...
83
    {
84 27
        if (is_array($key) && is_bool($value)) {
85 27
            $override = $value;
86 18
        }
87
88 27
        return Util::operateArray($this->params, $key, $value, null, null, $override);
89
    }
90
91
    /**
92
     * SMS send process.
93
     *
94
     * @param       $to
95
     * @param       $content
96
     * @param       $tempId
97
     * @param array $data
98
     * @param array $params
99
     */
100 24
    public function sendSms($to, $content = null, $tempId = null, array $data = [], array $params = [])
101
    {
102 24
        $this->reset();
103 24
        $this->params($params, true);
104 24
        $to = $this->formatMobile(Util::formatMobiles($to));
105
106 24
        if ($tempId && $this instanceof TemplateSms) {
107 3
            $this->sendTemplateSms($to, $tempId, $data);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Toplan\PhpSms\Agent as the method sendTemplateSms() does only exist in the following sub-classes of Toplan\PhpSms\Agent: Toplan\PhpSms\AlidayuAgent, Toplan\PhpSms\AliyunAgent, Toplan\PhpSms\JuHeAgent, Toplan\PhpSms\LogAgent, Toplan\PhpSms\ParasiticAgent, Toplan\PhpSms\QcloudAgent, Toplan\PhpSms\SendCloudAgent, Toplan\PhpSms\SubMailAgent, Toplan\PhpSms\UcpaasAgent, Toplan\PhpSms\YunTongXunAgent. 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...
108 23
        } elseif ($content && $this instanceof ContentSms) {
109 21
            $this->sendContentSms($to, $content);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Toplan\PhpSms\Agent as the method sendContentSms() does only exist in the following sub-classes of Toplan\PhpSms\Agent: Toplan\PhpSms\LogAgent, Toplan\PhpSms\LuosimaoAgent, Toplan\PhpSms\ParasiticAgent, Toplan\PhpSms\QcloudAgent, Toplan\PhpSms\SmsBaoAgent, Toplan\PhpSms\YunPianAgent. 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...
110 14
        }
111 24
    }
112
113
    /**
114
     * Voice send process.
115
     *
116
     * @param       $to
117
     * @param       $content
118
     * @param       $tempId
119
     * @param array $data
120
     * @param       $code
121
     * @param       $fileId
122
     * @param array $params
123
     */
124 6
    public function sendVoice($to, $content = null, $tempId = null, array $data = [], $code = null, $fileId = null, array $params = [])
125
    {
126 6
        $this->reset();
127 6
        $this->params($params, true);
128 6
        $to = $this->formatMobile(Util::formatMobiles($to));
129
130 6
        if ($tempId && $this instanceof TemplateVoice) {
131
            $this->sendTemplateVoice($to, $tempId, $data);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Toplan\PhpSms\Agent as the method sendTemplateVoice() does only exist in the following sub-classes of Toplan\PhpSms\Agent: Toplan\PhpSms\AlidayuAgent, Toplan\PhpSms\ParasiticAgent. 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...
132 6
        } elseif ($fileId && $this instanceof FileVoice) {
133
            $this->sendFileVoice($to, $fileId);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Toplan\PhpSms\Agent as the method sendFileVoice() does only exist in the following sub-classes of Toplan\PhpSms\Agent: Toplan\PhpSms\ParasiticAgent. 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...
134 6
        } elseif ($code && $this instanceof VoiceCode) {
135 6
            $this->sendVoiceCode($to, $code);
0 ignored issues
show
Bug introduced by
The method sendVoiceCode() does not exist on Toplan\PhpSms\Agent. Did you maybe mean sendVoice()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
136 4
        } elseif ($content && $this instanceof ContentVoice) {
137
            $this->sendContentVoice($to, $content);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Toplan\PhpSms\Agent as the method sendContentVoice() does only exist in the following sub-classes of Toplan\PhpSms\Agent: Toplan\PhpSms\ParasiticAgent, Toplan\PhpSms\QcloudAgent. 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...
138
        }
139 6
    }
140
141
    /**
142
     * Formatting a mobile number from the list of mobile numbers.
143
     *
144
     * @param array $list
145
     *
146
     * @return string
147
     */
148
    public function formatMobile(array $list)
149
    {
150 27
        return implode(',', array_unique(array_map(function ($value) {
151 27
            return is_array($value) ? "{$value['number']}" : $value;
152 27
        }, $list)));
153
    }
154
155
    /**
156
     * @codeCoverageIgnore
157
     *
158
     * @param       $url
159
     * @param array $params
160
     * @param array $opts
161
     *
162
     * @return array
163
     */
164
    public function curlPost($url, array $params = [], array $opts = [])
165
    {
166
        $options = [
167
            CURLOPT_POST    => true,
168
            CURLOPT_URL     => $url,
169
        ];
170
        foreach ($opts as $key => $value) {
171
            if ($key !== CURLOPT_POST && $key !== CURLOPT_URL) {
172
                $options[$key] = $value;
173
            }
174
        }
175
        if (!array_key_exists(CURLOPT_POSTFIELDS, $options)) {
176
            $options[CURLOPT_POSTFIELDS] = $this->params($params);
177
        }
178
179
        return self::curl($options);
180
    }
181
182
    /**
183
     * @codeCoverageIgnore
184
     *
185
     * @param       $url
186
     * @param array $params
187
     * @param array $opts
188
     *
189
     * @return array
190
     */
191
    public function curlGet($url, array $params = [], array $opts = [])
192
    {
193
        $params = $this->params($params);
194
        $queryStr = http_build_query($params);
195
        $opts[CURLOPT_POST] = false;
196
        $opts[CURLOPT_URL] = $queryStr ? "$url?$queryStr" : $url;
197
198
        return self::curl($opts);
199
    }
200
201
    /**
202
     * cURl
203
     *
204
     * @codeCoverageIgnore
205
     *
206
     * @param array $opts curl options
207
     *
208
     * @return array ['request', 'response']
209
     *               request: Whether request success.
210
     *               response: Response data.
211
     */
212
    public static function curl(array $opts = [])
213
    {
214
        $ch = curl_init();
215
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
216
        curl_setopt($ch, CURLOPT_HEADER, false);
217
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
218
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
219
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
220
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
221
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
222
        foreach ($opts as $key => $value) {
223
            curl_setopt($ch, $key, $value);
224
        }
225
226
        $response = curl_exec($ch);
227
        $request = $response !== false;
228
        if (!$request) {
229
            $response = curl_getinfo($ch);
230
        }
231
        curl_close($ch);
232
233
        return compact('request', 'response');
234
    }
235
236
    /**
237
     * Get or set the result data.
238
     *
239
     * @param $name
240
     * @param $value
241
     *
242
     * @return mixed
243
     */
244 33
    public function result($name = null, $value = null)
245
    {
246 33
        if ($name === null) {
247 30
            return $this->result;
248
        }
249 30
        if (array_key_exists($name, $this->result)) {
250 30
            if ($value === null) {
251 6
                return $this->result[$name];
252
            }
253 30
            $this->result[$name] = $value;
254 20
        }
255 30
    }
256
257
    /**
258
     * Overload object properties.
259
     *
260
     * @param $name
261
     *
262
     * @return mixed
263
     */
264 6
    public function __get($name)
265
    {
266 6
        return $this->config($name);
267
    }
268
269
    /**
270
     * When using isset() or empty() on inaccessible object properties,
271
     * the __isset() overloading method will be called.
272
     *
273
     * @param $name
274
     *
275
     * @return bool
276
     */
277 3
    public function __isset($name)
278
    {
279 3
        return isset($this->config[$name]);
280
    }
281
}
282