Completed
Push — master ( 7b61d0...d90799 )
by Zhmayev
01:25
created

Session::getVtigerVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
* Vtiger Web Services PHP Client Library
4
*
5
* The MIT License (MIT)
6
*
7
* Copyright (c) 2015, Zhmayev Yaroslav <[email protected]>
8
*
9
* Permission is hereby granted, free of charge, to any person obtaining a copy
10
* of this software and associated documentation files (the "Software"), to deal
11
* in the Software without restriction, including without limitation the rights
12
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
* copies of the Software, and to permit persons to whom the Software is
14
* furnished to do so, subject to the following conditions:
15
*
16
* The above copyright notice and this permission notice shall be included in
17
* all copies or substantial portions of the Software.
18
*
19
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
* THE SOFTWARE.
26
*
27
* @author    Zhmayev Yaroslav <[email protected]>
28
* @copyright 2015-2016 Zhmayev Yaroslav
29
* @license   The MIT License (MIT)
30
*/
31
32
namespace Salaros\Vtiger\VTWSCLib;
33
34
use GuzzleHttp\Client;
35
use GuzzleHttp\Exception\RequestException;
36
37
/**
38
* Vtiger Web Services PHP Client Session class
39
*
40
* Class Session
41
* @package Salaros\Vtiger\VTWSCLib
42
* @internal
43
*/
44
class Session
45
{
46
    // HTTP Client instance
47
    protected $httpClient = null;
48
49
    // Service URL to which client connects to
50
    protected $vtigerUrl = null;
51
    protected $wsBaseURL = null;
52
53
    // Vtiger CRM and WebServices API version
54
    private $vtigerApiVersion = '0.0';
55
    private $vtigerVersion = '0.0';
56
    
57
    // Webservice login validity
58
    # private $serviceServerTime = null;
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
59
    private $serviceExpireTime = null;
60
    private $serviceToken = null;
61
62
    // Webservice user credentials
63
    private $userName = null;
64
    private $accessKey = null;
65
66
    // Webservice login credentials
67
    private $userID = null;
68
    private $sessionName = null;
69
70
    /**
71
     * Class constructor
72
     * @param string $vtigerUrl  The URL of the remote WebServices server
73
     * @param string [$wsBaseURL = 'webservice.php']  WebServices base URL appended to vTiger root URL
74
     */
75
    public function __construct($vtigerUrl, $wsBaseURL = 'webservice.php')
76
    {
77
        $this->vtigerUrl = self::fixVtigerBaseUrl($vtigerUrl);
78
        $this->serviceBaseURL = $wsBaseURL;
0 ignored issues
show
Bug introduced by
The property serviceBaseURL does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
79
80
        // Gets target URL for WebServices API requests
81
        $this->httpClient = new Client([
82
            'base_uri' => $this->vtigerUrl
83
        ]);
84
    }
85
86
    /**
87
     * Login to the server using username and VTiger access key token
88
     * @access public
89
     * @param  string $username VTiger user name
90
     * @param  string $accessKey VTiger access key token (visible on user profile/settings page)
91
     * @return boolean Returns true if login operation has been successful
92
     */
93
    public function login($username, $accessKey)
94
    {
95
        // Do the challenge before loggin in
96
        if ($this->passChallenge($username) === false) {
97
            return false;
98
        }
99
100
        $postdata = [
101
            'operation' => 'login',
102
            'username'  => $username,
103
            'accessKey' => md5($this->serviceToken . $accessKey)
104
        ];
105
106
        $result = $this->sendHttpRequest($postdata);
107
        if (!$result || !is_array($result)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
108
            return false;
109
        }
110
111
        // Backuping logged in user credentials
112
        $this->userName = $username;
113
        $this->accessKey = $accessKey;
114
115
        // Session data
116
        $this->sessionName = $result['sessionName'];
117
        $this->userID = $result['userId'];
118
119
        // Vtiger CRM and WebServices API version
120
        $this->vtigerApiVersion = $result['version'];
121
        $this->vtigerVersion = $result['vtigerVersion'];
122
123
        return true;
124
    }
125
126
    /**
127
     * Allows you to login using username and password instead of access key (works on some VTige forks)
128
     * @access public
129
     * @param  string $username VTiger user name
130
     * @param  string $password VTiger password (used to access CRM using the standard login page)
131
     * @param  string $accessKey This parameter will be filled with user's VTiger access key
132
     * @return boolean  Returns true if login operation has been successful
133
     */
134
    public function loginPassword($username, $password, &$accessKey = null)
135
    {
136
        // Do the challenge before loggin in
137
        if ($this->passChallenge($username) === false) {
138
            return false;
139
        }
140
141
        $postdata = [
142
            'operation' => 'login_pwd',
143
            'username' => $username,
144
            'password' => $password
145
        ];
146
147
        $result = $this->sendHttpRequest($postdata);
148
        if (!$result || !is_array($result) || count($result) !== 1) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
149
            return false;
150
        }
151
152
        $this->accessKey = array_key_exists('accesskey', $result)
153
            ? $result['accesskey']
154
            : $result[0];
155
156
        return $this->login($username, $accessKey);
157
    }
158
159
    /**
160
     * Gets a challenge token from the server and stores for future requests
161
     * @access private
162
     * @param  string $username VTiger user name
163
     * @return boolean Returns false in case of failure
164
     */
165
    private function passChallenge($username)
166
    {
167
        $getdata = [
168
            'operation' => 'getchallenge',
169
            'username'  => $username
170
        ];
171
        $result = $this->sendHttpRequest($getdata, 'GET');
172
        
173
        if (!is_array($result) || !isset($result['token'])) {
174
            return false;
175
        }
176
177
        # $this->serviceServerTime = $result['serverTime'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
178
        $this->serviceExpireTime = $result['expireTime'];
179
        $this->serviceToken = $result['token'];
180
181
        return true;
182
    }
183
184
    /**
185
     * Gets an array containing the basic information about current API user
186
     * @access public
187
     * @return array Basic information about current API user
188
     */
189
    public function getUserInfo()
190
    {
191
        return [
192
            'id' => $this->userID,
193
            'userName' => $this->userName,
194
            'accessKey' => $this->accessKey,
195
        ];
196
    }
197
198
    /**
199
     * Gets vTiger version, retrieved on successful login
200
     * @access public
201
     * @return string vTiger version, retrieved on successful login
202
     */
203
    public function getVtigerVersion()
204
    {
205
        return $this->vtigerVersion;
206
    }
207
208
    /**
209
     * Gets vTiger WebServices API version, retrieved on successful login
210
     * @access public
211
     * @return string vTiger WebServices API version, retrieved on successful login
212
     */
213
    public function getVtigerApiVersion()
214
    {
215
        return $this->vtigerApiVersion;
216
    }
217
218
    /**
219
     * Sends HTTP request to VTiger web service API endpoint
220
     * @access private
221
     * @param  array $requestData HTTP request data
222
     * @param  string $method HTTP request method (GET, POST etc)
223
     * @return array Returns request result object (null in case of failure)
224
     */
225
    public function sendHttpRequest(array $requestData, $method = 'POST')
226
    {
227
        if (!isset($requestData['operation'])) {
228
            throw new WSException('Request data must contain the name of the operation!');
229
        }
230
231
        $requestData['sessionName'] = $this->sessionName;
232
233
        // Perform re-login if required.
234
        if ('getchallenge' !== $requestData['operation'] && time() > $this->serviceExpireTime) {
235
            $this->login($this->userName, $this->accessKey);
236
        }
237
        
238
        try {
239
            switch ($method) {
240
                case 'GET':
241
                    $response = $this->httpClient->get($this->serviceBaseURL, ['query' => $requestData]);
242
                    break;
243
                case 'POST':
244
                    $response = $this->httpClient->post($this->serviceBaseURL, ['form_params' => $requestData]);
245
                    break;
246
                default:
247
                    throw new WSException("Unsupported request type {$method}");
248
            }
249
        } catch (RequestException $ex) {
250
            $urlFailed = $this->httpClient->getConfig('base_uri') . $this->serviceBaseURL;
251
            throw new WSException(
252
                sprintf('Failed to execute %s call on "%s" URL', $method, $urlFailed),
253
                'FAILED_SENDING_REQUEST',
254
                $ex
255
            );
256
        }
257
258
        $jsonRaw = $response->getBody();
259
        $jsonObj = json_decode($jsonRaw, true);
260
261
        return (!is_array($jsonObj) || self::checkForError($jsonObj))
262
            ? null
263
            : $jsonObj['result'];
264
    }
265
266
    /**
267
     *  Cleans and fixes vTiger URL
268
     * @access private
269
     * @static
270
     * @param  string  Base URL of vTiger CRM
271
     * @return boolean Returns cleaned and fixed vTiger URL
272
     */
273
    private static function fixVtigerBaseUrl($baseUrl)
274
    {
275
        if (!preg_match('/^https?:\/\//i', $baseUrl)) {
276
            $baseUrl = sprintf('http://%s', $baseUrl);
277
        }
278
        if (strripos($baseUrl, '/') !== strlen($baseUrl)-1) {
279
            $baseUrl .= '/';
280
        }
281
        return $baseUrl;
282
    }
283
284
    /**
285
     * Check if server response contains an error, therefore the requested operation has failed
286
     * @access private
287
     * @static
288
     * @param  array $jsonResult Server response object to check for errors
289
     * @return boolean  True if response object contains an error
290
     */
291
    private static function checkForError(array $jsonResult)
292
    {
293
        if (isset($jsonResult['success']) && (bool)$jsonResult['success'] === true) {
294
            return false;
295
        }
296
297
        if (isset($jsonResult['error'])) {
298
            $error = $jsonResult['error'];
299
            throw new WSException(
300
                $error['message'],
301
                $error['code']
302
            );
303
        }
304
305
        // This should never happen
306
        throw new WSException('Unknown error');
307
    }
308
}
309