Completed
Push — master ( ed11ea...d84521 )
by Gareth
08:53
created

API::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
crap 2
1
<?php
2
3
namespace jamesiarmes\PEWS;
4
5
use jamesiarmes\PEWS\API\ExchangeWebServices;
6
use jamesiarmes\PEWS\API\ExchangeWebServicesAuth;
7
use jamesiarmes\PEWS\API\Type;
8
use jamesiarmes\PEWS\Calendar\CalendarAPI;
9
use jamesiarmes\PEWS\Mail\MailAPI;
10
11
/**
12
 * A base class for APIs
13
 *
14
 * Class BaseAPI
15
 * @package jamesiarmes\PEWS
16
 */
17
class API
18
{
19
    protected static $defaultClientOptions = array(
20
        'version' => ExchangeWebServices::VERSION_2010
21
    );
22
23 23
    public function __construct(ExchangeWebServices $client = null)
24
    {
25 23
        if ($client) {
26 10
            $this->setClient($client);
27 10
        }
28 23
    }
29
30
    /**
31
     * @return Type\EmailAddressType
32
     */
33 15
    public function getPrimarySmtpMailbox()
34
    {
35 15
        return $this->getClient()->getPrimarySmtpMailbox();
36
    }
37
38
    private $fieldUris = array();
39
40
    /**
41
     * Storing the API client
42
     * @var ExchangeWebServices
43
     */
44
    private $client;
45
46 4
    public function setupFieldUris()
47
    {
48
        //So, since we have to pass in URI's of everything we update, we need to fetch them
49 4
        $reflection = new \ReflectionClass('jamesiarmes\PEWS\API\Enumeration\UnindexedFieldURIType');
50 4
        $constants = $reflection->getConstants();
51 4
        $constantsFound = array();
52
53
        //Loop through all URI's to list them in an array
54 4
        foreach ($constants as $constant) {
55 4
            $exploded = explode(":", $constant);
56 4
            if (count($exploded) == 1) {
57 4
                $exploded = ['item', $exploded[0]];
58 4
            }
59
60 4
            $name = strtolower($exploded[1]);
61 4
            $category = strtolower($exploded[0]);
62
63 4
            if (!isset($constantsFound[$name])) {
64 4
                $constantsFound[$name] = array();
65 4
            }
66 4
            $constantsFound[$name][$category] = $constant;
67 4
        }
68
69 4
        $this->fieldUris = $constantsFound;
70 4
    }
71
72 4
    public function getFieldUriByName($fieldName, $preference = 'item')
73
    {
74 4
        $fieldName = strtolower($fieldName);
75 4
        $preference = strtolower($preference);
76
77 4
        if (empty($this->fieldUris)) {
78 4
            $this->setupFieldUris();
79 4
        }
80
81 4
        if (!isset($this->fieldUris[$fieldName])) {
82 1
            return false;
83
        }
84
85 4
        if (!isset($this->fieldUris[$fieldName][$preference])) {
86 1
            $preference = 'item';
87 1
        }
88
89 4
        if (!isset($this->fieldUris[$fieldName][$preference])) {
90
            throw new \Exception("Could not find uri $preference:$fieldName");
91
        }
92
93 4
        return $this->fieldUris[$fieldName][$preference];
94
    }
95
96
    /**
97
     * Get a calendar item
98
     *
99
     * @param string $name
100
     * @return CalendarAPI
101
     */
102 6
    public function getCalendar($name = null)
103
    {
104 6
        $calendar = new CalendarAPI();
105 6
        $calendar->setClient($this->getClient());
106 6
        $calendar->pickCalendar($name);
107
108 6
        return $calendar;
109
    }
110
111
    /**
112
     * @param string $folderName
113
     * @return MailAPI
114
     */
115 8
    public function getMailbox($folderName = null)
116
    {
117 8
        $mailApi = new MailAPI();
118 6
        $mailApi->setClient($this->getClient());
119 6
        $mailApi->pickMailFolder($folderName);
120
121 6
        return $mailApi;
122
    }
123
124
    /**
125
     * Set the API client
126
     *
127
     * @param ExchangeWebServices $client
128
     * @return $this
129
     */
130 23
    public function setClient($client)
131
    {
132 23
        $this->client = $client;
133 23
        return $this;
134
    }
135
136
    /**
137
     * Get the API client
138
     *
139
     * @return ExchangeWebServices
140
     */
141 22
    public function getClient()
142
    {
143 22
        return $this->client;
144
    }
145
146
    /**
147
     * Instantiate and set a client (ExchangeWebServices) based on the parameters given
148
     *
149
     * @deprecated Since 0.6.3
150
     * @param $server
151
     * @param $username
152
     * @param $password
153
     * @param array $options
154
     * @return $this
155
     */
156
    public function buildClient(
157
        $server,
158
        $username,
159
        $password,
160
        $options = [ ]
161
    ) {
162
        $this->setClient(ExchangeWebServices::fromUsernameAndPassword(
163
            $server,
164
            $username,
165
            $password,
166
            array_replace_recursive(self::$defaultClientOptions, $options)
167
        ));
168
    }
169
170 9
    public static function withUsernameAndPassword($server, $username, $password, $options = [ ])
171
    {
172 9
        return new static(ExchangeWebServices::fromUsernameAndPassword(
173 9
            $server,
174 9
            $username,
175 9
            $password,
176 9
            array_replace_recursive(self::$defaultClientOptions, $options)
177 9
        ));
178
    }
179
180 1
    public static function withCallbackToken($server, $token, $options = [ ])
181
    {
182 1
        return new static(ExchangeWebServices::fromCallbackToken(
183 1
            $server,
184 1
            $token,
185 1
            array_replace_recursive(self::$defaultClientOptions, $options)
186 1
        ));
187
    }
188
189
    public function getPrimarySmptEmailAddress()
190
    {
191
        if ($this->getPrimarySmtpMailbox() == null) {
192
            return null;
193
        }
194
195
        return $this->getPrimarySmtpMailbox()->getEmailAddress();
196
    }
197
198
    public function setPrimarySmtpEmailAddress($emailAddress)
199
    {
200
        $this->getClient()->setPrimarySmtpEmailAddress($emailAddress);
201
202
        return $this;
203
    }
204
205
    /**
206
     * Create items through the API client
207
     *
208
     * @param $items
209
     * @param array $options
210
     * @return API\CreateItemResponseType
211
     */
212 9
    public function createItems($items, $options = array())
213
    {
214 9
        if (!is_array($items)) {
215
            $items = array($items);
216
        }
217
218
        $request = array(
219
            'Items' => $items
220 9
        );
221
222 9
        $request = array_replace_recursive($request, $options);
223 9
        $request = Type::buildFromArray($request);
224
225 9
        $response = $this->getClient()->CreateItem($request);
226
227 9
        return $response;
228
    }
229
230 2
    public function updateItems($items, $options = array())
231
    {
232
        $request = array(
233 2
            'ItemChanges' => $items,
234 2
            'MessageDisposition' => 'SaveOnly',
235
            'ConflictResolution' => 'AlwaysOverwrite'
236 2
        );
237
238 2
        $request = array_replace_recursive($request, $options);
239
240 2
        $request = Type::buildFromArray($request);
241
242 2
        return $this->getClient()->UpdateItem($request)->getItems();
0 ignored issues
show
Documentation Bug introduced by
The method getItems does not exist on object<jamesiarmes\PEWS\API\Type>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
243
    }
244
245 2
    protected function buildUpdateItemChanges($itemType, $uriType, $changes)
246
    {
247 2
        $setItemFields = array();
248
249
        //Add each property to a setItemField
250 2
        foreach ($changes as $key => $value) {
251 2
            $fullName = $this->getFieldUriByName($key, $uriType);
252
253 2
            $setItemFields[] = array(
254 2
                'FieldURI' => array('FieldURI' => $fullName),
255 2
                $itemType => array($key => $value)
256 2
            );
257 2
        }
258
259 2
        return $setItemFields;
260
    }
261
262
    public function createFolders($names, Type\FolderIdType $parentFolder, $options = array())
263
    {
264
        $request = array('Folders' => array('Folder' => array()));
265
        if (!empty($parentFolder)) {
266
            $request['ParentFolderId'] = array('FolderId' => $parentFolder->toArray());
267
        }
268
269
        if (!is_array($names)) {
270
            $names = array($names);
271
        }
272
273
        foreach ($names as $name) {
274
            $request['Folders']['Folder'][] = array(
275
                'DisplayName' => $name
276
            );
277
        }
278
279
        $request = array_merge_recursive($request, $options);
280
281
        $this->client->CreateFolder($request);
0 ignored issues
show
Documentation Bug introduced by
The method CreateFolder does not exist on object<jamesiarmes\PEWS\API\ExchangeWebServices>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
282
283
        return true;
284
    }
285
286
    public function moveItem(Type\ItemIdType $itemId, Type\FolderIdType $folderId, $options = array())
287
    {
288
        $request = array(
289
            'ToFolderId' => array('FolderId' => $folderId->toArray()),
290
            'ItemIds' => array('ItemId' => $itemId->toArray())
291
        );
292
293
        $request = array_merge_recursive($request, $options);
294
295
        return $this->client->MoveItem($request);
0 ignored issues
show
Documentation Bug introduced by
The method MoveItem does not exist on object<jamesiarmes\PEWS\API\ExchangeWebServices>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
296
    }
297
298
    /**
299
     * @param $items Type\ItemIdType|Type\ItemIdType[]
300
     * @param array $options
301
     * @return bool
302
     */
303 8
    public function deleteItems($items, $options = array())
304
    {
305 8
        if (!is_array($items) || Type::arrayIsAssoc($items)) {
306 8
            $items = array($items);
307 8
        }
308
309 8
        $itemIds = array();
310 8
        foreach ($items as $item) {
311 8
            if ($item instanceof Type\ItemIdType) {
0 ignored issues
show
Bug introduced by
The class jamesiarmes\PEWS\API\Type\ItemIdType 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...
312 7
                $item = $item->toArray();
313 7
            }
314 8
            $item = (array) $item;
315 8
            $itemIds[] = array(
316 8
                'Id' => $item['Id'],
317 8
                'ChangeKey' => $item['ChangeKey']
318 8
            );
319 8
        }
320
321
        $request = array(
322 8
            'ItemIds' => array('ItemId' => $itemIds),
323
            'DeleteType' => 'MoveToDeletedItems'
324 8
        );
325
326 8
        $request = array_replace_recursive($request, $options);
327 8
        $request = Type::buildFromArray($request);
328 8
        $this->getClient()->DeleteItem($request);
329
330
        //If the delete fails, an Exception will be thrown in processResponse before it gets here
331 8
        return true;
332
    }
333
334
    /**
335
     * @param $identifier
336
     * @return Type\BaseFolderType
337
     */
338 15
    public function getFolder($identifier)
339
    {
340
        $request = array(
341
            'FolderShape' => array(
342 15
                'BaseShape' => array('_' => 'Default')
343 15
            ),
344
            'FolderIds' => $identifier
345 15
        );
346 15
        $request = Type::buildFromArray($request);
347
348 15
        $response = $this->getClient()->GetFolder($request);
349 15
        return $response;
350
    }
351
352
    /**
353
     * Get a folder by it's distinguishedId
354
     *
355
     * @param string $distinguishedId
356
     * @return Type\BaseFolderType
357
     */
358 15
    public function getFolderByDistinguishedId($distinguishedId)
359
    {
360 15
        return $this->getFolder(array(
361
            'DistinguishedFolderId' => array(
362 15
                'Id' => $distinguishedId,
363 15
                'Mailbox' => $this->getPrimarySmtpMailbox()
364 15
            )
365 15
        ));
366
    }
367
368
    /**
369
     * @param $folderId
370
     * @return Type\BaseFolderType
371
     */
372 4
    public function getFolderByFolderId($folderId)
373
    {
374 4
        return $this->getFolder(array(
375 4
            'FolderId' => array('Id'=>$folderId, 'Mailbox' => $this->getPrimarySmtpMailbox())
376 4
        ));
377
    }
378
379
    /**
380
     * @param string|Type\FolderIdType $parentFolderId
381
     * @param array $options
382
     * @return bool|Type\BaseFolderType
383
     */
384 14
    public function getChildrenFolders($parentFolderId = 'root', $options = array())
385
    {
386 14
        if (is_string($parentFolderId)) {
387 14
            $parentFolderId = $this->getFolderByDistinguishedId($parentFolderId)->getFolderId();
0 ignored issues
show
Documentation Bug introduced by
The method getFolderId does not exist on object<jamesiarmes\PEWS\API\Type>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
388 14
        }
389
390
        $request = array(
391 14
            'Traversal' => 'Shallow',
392
            'FolderShape' => array(
393
                'BaseShape' => 'AllProperties'
394 14
            ),
395
            'ParentFolderIds' => array(
396 14
                'FolderId' => $parentFolderId->toArray()
397 14
            )
398 14
        );
399
400 14
        $request = array_replace_recursive($request, $options);
401
402 14
        $request = Type::buildFromArray($request);
403
404
        /** @var \jamesiarmes\PEWS\API\Message\FindFolderResponseMessageType $folders */
405 14
        return $this->getClient()->FindFolder($request);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getClient()->FindFolder($request); (jamesiarmes\PEWS\API\Type) is incompatible with the return type documented by jamesiarmes\PEWS\API::getChildrenFolders of type boolean|jamesiarmes\PEWS\API\Type\BaseFolderType.

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...
406
        return $folders->getFolders();
0 ignored issues
show
Unused Code introduced by
return $folders->getFolders(); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
407
    }
408
409
    /**
410
     * @param $folderName
411
     * @param string|Type\FolderIdType $parentFolderId
412
     * @param array $options
413
     * @return bool|Type\BaseFolderType
414
     */
415 14
    public function getFolderByDisplayName($folderName, $parentFolderId = 'root', $options = array())
416
    {
417 14
        $folders = $this->getChildrenFolders($parentFolderId, $options);
418
419 14
        foreach ($folders as $folder) {
0 ignored issues
show
Bug introduced by
The expression $folders of type boolean|object<jamesiarm...PI\Type\BaseFolderType> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
420 14
            if ($folder->getDisplayName() == $folderName) {
421 14
                return $folder;
422
            }
423 13
        }
424
425
        return false;
426
    }
427
428
    /**
429
     * @param $itemId array|Type\ItemIdType
430
     * @param array $options
431
     * @return Type
432
     */
433 1
    public function getItem($itemId, $options = array())
434
    {
435 1
        if ($itemId instanceof Type\ItemIdType) {
0 ignored issues
show
Bug introduced by
The class jamesiarmes\PEWS\API\Type\ItemIdType 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...
436
            $itemId = $itemId->toArray();
437
        }
438
439
        $request = array(
440 1
            'ItemShape' => array('BaseShape' => 'AllProperties'),
441 1
            'ItemIds' => array('ItemId' => $itemId)
442 1
        );
443
444 1
        $request = array_replace_recursive($request, $options);
445
446 1
        return $this->getClient()->GetItem($request);
447
    }
448
449
    /**
450
     * Get a list of sync changes on a folder
451
     *
452
     * @param Type\FolderIdType $folderId
453
     * @param null $syncState
454
     * @param array $options
455
     * @return mixed
456
     */
457 2
    public function listItemChanges($folderId, $syncState = null, $options = array())
458
    {
459
        $request = array(
460 2
            'ItemShape' => array('BaseShape' => 'IdOnly'),
461 2
            'SyncFolderId' => array('FolderId' => $folderId->toXmlObject()),
462 2
            'SyncScope' => 'NormalItems',
463
            'MaxChangesReturned' => '10'
464 2
        );
465
466 2
        if ($syncState != null) {
467
            $request['SyncState'] = $syncState;
468
            $request['ItemShape']['BaseShape'] = 'AllProperties';
469
        }
470
471 2
        $request = array_replace_recursive($request, $options);
472
473 2
        $request = Type::buildFromArray($request);
474 2
        $response = $this->getClient()->SyncFolderItems($request);
475 2
        return $response;
476
    }
477
}
478