Completed
Push — master ( 59b3e2...6dd6b9 )
by Andrii
15:38
created

AbstractConnection::getHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Tools to use API as ActiveRecord for Yii2
4
 *
5
 * @link      https://github.com/hiqdev/yii2-hiart
6
 * @package   yii2-hiart
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\hiart;
12
13
use Closure;
14
use Yii;
15
use hiqdev\hiart\stream\Request;
16
use yii\base\Component;
17
use yii\base\InvalidParamException;
18
use yii\helpers\Json;
19
20
/**
21
 * Abstract connection class.
22
 */
23
abstract class AbstractConnection extends Component implements ConnectionInterface
24
{
25
    const EVENT_AFTER_OPEN = 'afterOpen';
26
27
    /**
28
     * @var string to be specified in concrete implementation
29
     */
30
    public $queryBuilderClass;
31
32
    public $requestClass = Request::class;
33
34
    public $commandClass = Command::class;
35
36
    public $queryClass = Query::class;
37
38
    public $activeQueryClass = ActiveQuery::class;
39
40
    public static $dbname = 'hiart';
41
42
    public $name = 'hiart';
43
44
    public $userAgent = 'HiArt/0.x';
45
46
    public $baseUri;
47
48
    /**
49
     * @var array transport config will be used in Request for handler or proxy request
50
     */
51
    public $config = [];
52
53
    /**
54
     * @var object request handler common for all requests of this connection
55
     */
56
    protected $_handler;
57
58
    /**
59
     * @var QueryBuilder the query builder for this connection
60
     */
61
    protected $_builder;
62
63
    /**
64
     * @var array authorization config
65
     */
66
    protected $_auth = [];
67
68
    /**
69
     * @var bool is auth disabled
70
     */
71
    protected $_disabledAuth = false;
72
73
    /**
74
     * @var Closure callback to test if API response has error
75
     * The function signature: `function ($response)`
76
     * Must return `null`, if the response does not contain an error
77
     */
78
    protected $_errorChecker;
79
80
    public static function getDb($dbname = null)
81
    {
82
        return $dbname ? Yii::$app->get($dbname) : Yii::$container->get(ConnectionInterface::class);
83
    }
84
85
    public function setAuth($auth)
86
    {
87
        $this->_auth = $auth;
88
    }
89
90
    /**
91
     * Returns auth settings.
92
     * @return array
93
     */
94
    public function getAuth()
95
    {
96
        if ($this->_disabledAuth) {
97
            return [];
98
        }
99
        if ($this->_auth instanceof Closure) {
100
            $this->_auth = call_user_func($this->_auth, $this);
0 ignored issues
show
Documentation Bug introduced by
It seems like call_user_func($this->_auth, $this) of type * is incompatible with the declared type array of property $_auth.

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...
101
        }
102
103
        return $this->_auth;
104
    }
105
106
    public function disableAuth()
107
    {
108
        $this->_disabledAuth = true;
109
    }
110
111
    public function enableAuth()
112
    {
113
        $this->_disabledAuth = false;
114
    }
115
116
    /**
117
     * Closes the connection when this component is being serialized.
118
     * @return array
119
     */
120
    public function __sleep()
121
    {
122
        return array_keys(get_object_vars($this));
123
    }
124
125
    /**
126
     * Returns the name of the DB driver for the current [[dsn]].
127
     * @return string name of the DB driver
128
     */
129
    public function getDriverName()
130
    {
131
        return 'hiart';
132
    }
133
134
    /**
135
     * Creates a command for execution.
136
     * @param array $config the configuration for the Command class
137
     * @return Command the DB command
138
     */
139
    public function createCommand(array $config = [])
140
    {
141
        $config['db'] = $this;
142
143
        return new $this->commandClass($config);
144
    }
145
146
    /**
147
     * @return QueryBuilder the query builder for this connection
148
     */
149
    public function getQueryBuilder()
150
    {
151
        if ($this->_builder === null) {
152
            $this->_builder = new $this->queryBuilderClass($this);
153
        }
154
155
        return $this->_builder;
156
    }
157
158
    /**
159
     * Handler is created and set by request.
160
     * @see setHandler
161
     * @return object
162
     */
163
    public function getHandler()
164
    {
165
        return $this->_handler;
166
    }
167
168
    /**
169
     * Requests use this function to keep request handler.
170
     * @param object $handler
171
     */
172
    public function setHandler($handler)
173
    {
174
        $this->_handler = $handler;
175
    }
176
177
    /**
178
     * @return boolean
179
     */
180
    public function isDisabledAuth()
181
    {
182
        return $this->_disabledAuth;
183
    }
184
185
    /**
186
     * @param boolean $disabledAuth
187
     */
188
    public function setDisabledAuth($disabledAuth)
189
    {
190
        $this->_disabledAuth = $disabledAuth;
191
    }
192
193
    /**
194
     * Try to decode error information if it is valid json, return it if not.
195
     * @param $body
196
     * @return mixed
197
     */
198
    protected function decodeErrorBody($body)
199
    {
200
        try {
201
            $decoded = Json::decode($body);
202
            if (isset($decoded['error'])) {
203
                $decoded['error'] = preg_replace('/\b\w+?Exception\[/',
204
                    "<span style=\"color: red;\">\\0</span>\n               ", $decoded['error']);
205
            }
206
207
            return $decoded;
208
        } catch (InvalidParamException $e) {
209
            return $body;
210
        }
211
    }
212
213
    /**
214
     * Setter for errorChecker.
215
     * @param Closure $checker
216
     */
217
    public function setErrorChecker($checker)
218
    {
219
        $this->_errorChecker = $checker;
220
    }
221
222
    /**
223
     * Checks response with checkError method and raises exception if error.
224
     * @param ResponseInterface $response response data from API
225
     * @throws ErrorResponseException
226
     * @return mixed response data
227
     */
228
    public function checkResponse(ResponseInterface $response)
229
    {
230
        $error = $this->checkError($response);
231
        if ($error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
232
            throw new ErrorResponseException($error, [
233
                'request' => $response->getRequest()->getParts(),
234
                'response' => $response->getData(),
235
            ]);
236
        }
237
    }
238
239
    /**
240
     * Checks response with errorChecker callback and returns error text if error.
241
     * @param ResponseInterface $response
242
     * @return string|false error text or false
243
     */
244
    public function checkError(ResponseInterface $response)
245
    {
246
        if (isset($this->_errorChecker)) {
247
            return call_user_func($this->_errorChecker, $response);
248
        } else {
249
            return $this->isError($response);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->isError($response); (boolean) is incompatible with the return type documented by hiqdev\hiart\AbstractConnection::checkError of type string|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
250
        }
251
    }
252
253
    /**
254
     * Default error checker. TODO check something in response?
255
     * @param ResponseInterface $response
256
     * @return bool
257
     */
258
    public function isError(ResponseInterface $response)
0 ignored issues
show
Unused Code introduced by
The parameter $response 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...
259
    {
260
        return false;
261
    }
262
263
    /**
264
     * Return API base uri.
265
     * Adds trailing slash if uri is domain only.
266
     * @return string
267
     */
268
    public function getBaseUri()
269
    {
270
        static $checked;
271
        if (empty($checked)) {
272
            if (count(explode('/', $this->baseUri, 4)) === 3) {
273
                $this->baseUri .= '/';
274
            }
275
            $checked = true;
276
        }
277
278
        return $this->baseUri;
279
    }
280
281
    public function getUserAgent()
282
    {
283
        return $this->userAgent;
284
    }
285
}
286