Completed
Branch master (09022f)
by Gareth
05:56 queued 03:06
created

API::moveItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
dl 11
loc 11
ccs 0
cts 5
cp 0
rs 9.4285
cc 1
eloc 6
nc 1
nop 3
crap 2
1
<?php
2
3
namespace garethp\ews;
4
5
use garethp\ews\API\Enumeration\DictionaryURIType;
6
use garethp\ews\API\Enumeration\UnindexedFieldURIType;
7
use garethp\ews\API\Exception\ExchangeException;
8
use garethp\ews\API\ExchangeWebServices;
9
use garethp\ews\API\Message\GetServerTimeZonesType;
10
use garethp\ews\API\Message\SyncFolderItemsResponseMessageType;
11
use garethp\ews\API\Type;
12
use garethp\ews\Calendar\CalendarAPI;
13
use garethp\ews\Mail\MailAPI;
14
15
/**
16
 * A base class for APIs
17
 *
18
 * Class BaseAPI
19
 * @package garethp\ews
20
 */
21
class API
22
{
23
    protected static $defaultClientOptions = array(
24
        'version' => ExchangeWebServices::VERSION_2010
25
    );
26
27 27
    public function __construct(ExchangeWebServices $client = null)
28
    {
29 27
        if ($client) {
30 15
            $this->setClient($client);
31
        }
32
    }
33
34
    /**
35
     * @return Type\EmailAddressType
36
     */
37 19
    public function getPrimarySmtpMailbox()
38
    {
39 19
        return $this->getClient()->getPrimarySmtpMailbox();
40
    }
41
42
    private $unIndexedFieldUris = array();
43
    private $dictionaryFieldUris = array();
44
45
    /**
46
     * Storing the API client
47
     * @var ExchangeWebServices
48
     */
49
    private $client;
50
51
    /**
52
     * @param $className
53
     * @return array
54
     */
55 4
    public function getFieldUrisFromClass($className)
56
    {
57
        //So, since we have to pass in URI's of everything we update, we need to fetch them
58 4
        $reflection = new \ReflectionClass($className);
59 4
        $constants = $reflection->getConstants();
60 4
        $constantsFound = array();
61
62
        //Loop through all URI's to list them in an array
63 4
        foreach ($constants as $constant) {
64 4
            $exploded = explode(":", $constant);
65 4
            if (count($exploded) == 1) {
66 4
                $exploded = ['item', $exploded[0]];
67
            }
68
69 4
            $name = strtolower($exploded[1]);
70 4
            $category = strtolower($exploded[0]);
71
72 4
            if (!isset($constantsFound[$name])) {
73 4
                $constantsFound[$name] = array();
74
            }
75 4
            $constantsFound[$name][$category] = $constant;
76
        }
77
78 4
        return $constantsFound;
79
    }
80
81 4
    public function setupFieldUris()
82
    {
83 4
        $this->unIndexedFieldUris = $this
84 4
            ->getFieldUrisFromClass(UnindexedFieldURIType::class);
85
86 4
        $this->dictionaryFieldUris = $this
87 4
            ->getFieldUrisFromClass(DictionaryURIType::class);
88
    }
89
90 4 View Code Duplication
    public function getFieldUriByName($fieldName, $preference = 'item')
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...
91
    {
92 4
        $fieldName = strtolower($fieldName);
93 4
        $preference = strtolower($preference);
94
95 4
        if (empty($this->unIndexedFieldUris)) {
96 4
            $this->setupFieldUris();
97
        }
98
99 4
        if (!isset($this->unIndexedFieldUris[$fieldName])) {
100 1
            return false;
101
        }
102
103 4
        if (!isset($this->unIndexedFieldUris[$fieldName][$preference])) {
104 1
            $preference = 'item';
105
        }
106
107 4
        if (!isset($this->unIndexedFieldUris[$fieldName][$preference])) {
108
            throw new ExchangeException("Could not find uri $preference:$fieldName");
109
        }
110
111 4
        return $this->unIndexedFieldUris[$fieldName][$preference];
112
    }
113
114 View Code Duplication
    public function getIndexedFieldUriByName($fieldName, $preference = 'item')
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...
115
    {
116
        $fieldName = strtolower($fieldName);
117
        $preference = strtolower($preference);
118
119
        if (empty($this->dictionaryFieldUris)) {
120
            $this->setupFieldUris();
121
        }
122
123
        if (!isset($this->dictionaryFieldUris[$fieldName])) {
124
            return false;
125
        }
126
127
        if (!isset($this->dictionaryFieldUris[$fieldName][$preference])) {
128
            $preference = 'item';
129
        }
130
131
        if (!isset($this->dictionaryFieldUris[$fieldName][$preference])) {
132
            throw new ExchangeException("Could not find uri $preference:$fieldName");
133
        }
134
135
        return $this->dictionaryFieldUris[$fieldName][$preference];
136
    }
137
138
    /**
139
     * Get a calendar item
140
     *
141
     * @param string $name
142
     * @return CalendarAPI
143
     */
144 6
    public function getCalendar($name = null)
145
    {
146 6
        $calendar = new CalendarAPI();
147 6
        $calendar->setClient($this->getClient());
148 6
        $calendar->pickCalendar($name);
149
150 6
        return $calendar;
151
    }
152
153
    /**
154
     * @param string $folderName
155
     * @return MailAPI
156
     */
157 6
    public function getMailbox($folderName = null)
158
    {
159 6
        $mailApi = new MailAPI();
160 6
        $mailApi->setClient($this->getClient());
161 6
        $mailApi->pickMailFolder($folderName);
162
163 6
        return $mailApi;
164
    }
165
166
    /**
167
     * Set the API client
168
     *
169
     * @param ExchangeWebServices $client
170
     * @return $this
171
     */
172 27
    public function setClient($client)
173
    {
174 27
        $this->client = $client;
175 27
        return $this;
176
    }
177
178
    /**
179
     * Get the API client
180
     *
181
     * @return ExchangeWebServices
182
     */
183 26
    public function getClient()
184
    {
185 26
        return $this->client;
186
    }
187
188
    /**
189
     * Instantiate and set a client (ExchangeWebServices) based on the parameters given
190
     *
191
     * @deprecated Since 0.6.3
192
     * @param $server
193
     * @param $username
194
     * @param $password
195
     * @param array $options
196
     * @return $this
197
     */
198 12
    public function buildClient(
199
        $server,
200
        $username,
201
        $password,
202
        $options = [ ]
203
    ) {
204 12
        $this->setClient(ExchangeWebServices::fromUsernameAndPassword(
205
            $server,
206
            $username,
207
            $password,
208 12
            array_replace_recursive(self::$defaultClientOptions, $options)
209
        ));
210
    }
211
212 14
    public static function withUsernameAndPassword($server, $username, $password, $options = [ ])
213
    {
214 14
        return new static(ExchangeWebServices::fromUsernameAndPassword(
215
            $server,
216
            $username,
217
            $password,
218 14
            array_replace_recursive(self::$defaultClientOptions, $options)
219
        ));
220
    }
221
222 1
    public static function withCallbackToken($server, $token, $options = [ ])
223
    {
224 1
        return new static(ExchangeWebServices::fromCallbackToken(
225
            $server,
226
            $token,
227 1
            array_replace_recursive(self::$defaultClientOptions, $options)
228
        ));
229
    }
230
231 1
    public function getPrimarySmptEmailAddress()
232
    {
233 1
        if ($this->getPrimarySmtpMailbox() == null) {
234 1
            return null;
235
        }
236
237 1
        return $this->getPrimarySmtpMailbox()->getEmailAddress();
238
    }
239
240 1
    public function setPrimarySmtpEmailAddress($emailAddress)
241
    {
242 1
        $this->getClient()->setPrimarySmtpEmailAddress($emailAddress);
243
244 1
        return $this;
245
    }
246
247
    /**
248
     * Create items through the API client
249
     *
250
     * @param $items
251
     * @param array $options
252
     * @return API\CreateItemResponseType
253
     */
254 11 View Code Duplication
    public function createItems($items, $options = array())
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...
255
    {
256 11
        if (!is_array($items)) {
257
            $items = array($items);
258
        }
259
260
        $request = array(
261 11
            'Items' => $items
262
        );
263
264 11
        $request = array_replace_recursive($request, $options);
265 11
        $request = Type::buildFromArray($request);
266
267 11
        $response = $this->getClient()->CreateItem($request);
268
269 11
        return $response;
270
    }
271
272 2
    public function updateItems($items, $options = array())
273
    {
274
        $request = array(
275 2
            'ItemChanges' => $items,
276 2
            'MessageDisposition' => 'SaveOnly',
277 2
            'ConflictResolution' => 'AlwaysOverwrite'
278
        );
279
280 2
        $request = array_replace_recursive($request, $options);
281
282 2
        $request = Type::buildFromArray($request);
283
284 2
        return $this->getClient()->UpdateItem($request)->getItems();
0 ignored issues
show
Documentation Bug introduced by
The method getItems does not exist on object<garethp\ews\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...
285
    }
286
287 2
    protected function getFieldURI($uriType, $key = null, $value = null)
288
    {
289 2
        if (strpos($key, ':') !== false) {
290
            try {
291
                $fieldUri = $this->getIndexedFieldUriByName(substr($key, 0, strpos($key, ':')), $uriType);
292
293
                list ($key, $index) = explode(':', $key);
294
295
                if (is_array($value)) {
296
                    $key = key($value);
297
                    $value = $value[$key];
298
                }
299
300
                return ['IndexedFieldURI', ['FieldURI' => $fieldUri, 'FieldIndex' => $index], $key, $value];
301
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
302
            }
303
        }
304
305 2
        $fullName = $this->getFieldUriByName($key, $uriType);
306 2
        return ['FieldURI', ['FieldURI' => $fullName], $key, $value];
307
    }
308
309 2
    protected function buildUpdateItemChanges($itemType, $uriType, $changes)
310
    {
311 2
        $setItemFields = array();
312 2
        $deleteItemFields = array();
313
314 2
        if (isset($changes['deleteFields'])) {
315
            foreach ($changes['deleteFields'] as $key) {
316
                list($fieldUriType, $fieldKey) = $this->getFieldURI($uriType, $key);
317
                $deleteItemFields[] = [$fieldUriType => $fieldKey];
318
            }
319
320
            unset($changes['deleteFields']);
321
        }
322
323
        //Add each property to a setItemField
324 2
        foreach ($changes as $key => $value) {
325 2
            list ($fieldUriType, $fieldKey, $valueKey, $value) = $this->getFieldURI($uriType, $key, $value);
326 2
            $setItemFields[] = array(
327 2
                $fieldUriType => $fieldKey,
328 2
                $itemType => [$valueKey => $value]
329
            );
330
        }
331
332 2
        return array('SetItemField' => $setItemFields, 'DeleteItemField' => $deleteItemFields);
333
    }
334
335 2
    public function createFolders($names, Type\FolderIdType $parentFolder, $options = array())
336
    {
337 2
        $request = array('Folders' => array('Folder' => array()));
338 2
        if (!empty($parentFolder)) {
339 2
            $request['ParentFolderId'] = array('FolderId' => $parentFolder->toArray());
340
        }
341
342 2
        if (!is_array($names)) {
343 2
            $names = array($names);
344
        }
345
346 2
        foreach ($names as $name) {
347 2
            $request['Folders']['Folder'][] = array(
348 2
                'DisplayName' => $name
349
            );
350
        }
351
352 2
        $request = array_merge_recursive($request, $options);
353
354 2
        $this->client->CreateFolder($request);
0 ignored issues
show
Documentation Bug introduced by
The method CreateFolder does not exist on object<garethp\ews\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...
355
356 2
        return true;
357
    }
358
359 2 View Code Duplication
    public function deleteFolder(Type\FolderIdType $folderId, $options = array())
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...
360
    {
361
        $request = array(
362 2
            'DeleteType' => 'HardDelete',
363
            'FolderIds' => array(
364 2
                'FolderId' => $folderId->toArray()
365
            )
366
        );
367
368 2
        $request = array_merge_recursive($request, $options);
369 2
        return $this->client->DeleteFolder($request);
0 ignored issues
show
Documentation Bug introduced by
The method DeleteFolder does not exist on object<garethp\ews\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...
370
    }
371
372 View Code Duplication
    public function moveItem(Type\ItemIdType $itemId, Type\FolderIdType $folderId, $options = array())
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...
373
    {
374
        $request = array(
375
            'ToFolderId' => array('FolderId' => $folderId->toArray()),
376
            'ItemIds' => array('ItemId' => $itemId->toArray())
377
        );
378
379
        $request = array_merge_recursive($request, $options);
380
381
        return $this->client->MoveItem($request);
0 ignored issues
show
Documentation Bug introduced by
The method MoveItem does not exist on object<garethp\ews\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...
382
    }
383
384
    /**
385
     * @param $items Type\ItemIdType|Type\ItemIdType[]
386
     * @param array $options
387
     * @return bool
388
     */
389 11
    public function deleteItems($items, $options = array())
390
    {
391 11
        if (!is_array($items) || Type::arrayIsAssoc($items)) {
392 11
            $items = array($items);
393
        }
394
395 11
        $itemIds = array();
396 11
        foreach ($items as $item) {
397 11
            if ($item instanceof Type\ItemIdType) {
0 ignored issues
show
Bug introduced by
The class garethp\ews\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...
398 10
                $item = $item->toArray();
399
            }
400 11
            $item = (array) $item;
401 11
            $itemIds[] = array(
402 11
                'Id' => $item['Id'],
403 11
                'ChangeKey' => $item['ChangeKey']
404
            );
405
        }
406
407
        $request = array(
408 11
            'ItemIds' => array('ItemId' => $itemIds),
409 11
            'DeleteType' => 'MoveToDeletedItems'
410
        );
411
412 11
        $request = array_replace_recursive($request, $options);
413 11
        $request = Type::buildFromArray($request);
414 11
        $this->getClient()->DeleteItem($request);
415
416
        //If the delete fails, an Exception will be thrown in processResponse before it gets here
417 11
        return true;
418
    }
419
420
    /**
421
     * @param $identifier
422
     * @return Type\BaseFolderType
423
     */
424 18
    public function getFolder($identifier)
425
    {
426
        $request = array(
427
            'FolderShape' => array(
428
                'BaseShape' => array('_' => 'Default')
429 18
            ),
430 18
            'FolderIds' => $identifier
431
        );
432 18
        $request = Type::buildFromArray($request);
433
434 18
        $response = $this->getClient()->GetFolder($request);
435 18
        return $response;
436
    }
437
438
    /**
439
     * Get a folder by it's distinguishedId
440
     *
441
     * @param string $distinguishedId
442
     * @return Type\BaseFolderType
443
     */
444 18
    public function getFolderByDistinguishedId($distinguishedId)
445
    {
446 18
        return $this->getFolder(array(
447
            'DistinguishedFolderId' => array(
448 18
                'Id' => $distinguishedId,
449 18
                'Mailbox' => $this->getPrimarySmtpMailbox()
450
            )
451
        ));
452
    }
453
454
    /**
455
     * @param $folderId
456
     * @return Type\BaseFolderType
457
     */
458 4
    public function getFolderByFolderId($folderId)
459
    {
460 4
        return $this->getFolder(array(
461 4
            'FolderId' => array('Id'=>$folderId, 'Mailbox' => $this->getPrimarySmtpMailbox())
462
        ));
463
    }
464
465
    /**
466
     * @param string|Type\FolderIdType $parentFolderId
467
     * @param array $options
468
     * @return bool|Type\BaseFolderType
469
     */
470 17
    public function getChildrenFolders($parentFolderId = 'root', $options = array())
471
    {
472 17
        if (is_string($parentFolderId)) {
473 15
            $parentFolderId = $this->getFolderByDistinguishedId($parentFolderId)->getFolderId();
0 ignored issues
show
Documentation Bug introduced by
The method getFolderId does not exist on object<garethp\ews\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...
474
        }
475
476
        $request = array(
477 17
            'Traversal' => 'Shallow',
478
            'FolderShape' => array(
479
                'BaseShape' => 'AllProperties'
480
            ),
481
            'ParentFolderIds' => array(
482 17
                'FolderId' => $parentFolderId->toArray()
483
            )
484
        );
485
486 17
        $request = array_replace_recursive($request, $options);
487
488 17
        $request = Type::buildFromArray($request);
489
490
        /** @var \garethp\ews\API\Message\FindFolderResponseMessageType $folders */
491 17
        return $this->getClient()->FindFolder($request);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getClient()->FindFolder($request); (garethp\ews\API\Type) is incompatible with the return type documented by garethp\ews\API::getChildrenFolders of type boolean|garethp\ews\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...
492
        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...
493
    }
494
495
    /**
496
     * @param $folderName
497
     * @param string|Type\FolderIdType $parentFolderId
498
     * @param array $options
499
     * @return bool|Type\BaseFolderType
500
     */
501 17
    public function getFolderByDisplayName($folderName, $parentFolderId = 'root', $options = array())
502
    {
503 17
        $folders = $this->getChildrenFolders($parentFolderId, $options);
504
505 17
        foreach ($folders as $folder) {
0 ignored issues
show
Bug introduced by
The expression $folders of type boolean|object<garethp\e...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...
506 17
            if ($folder->getDisplayName() == $folderName) {
507 17
                return $folder;
508
            }
509
        }
510
511 3
        return false;
512
    }
513
514
    /**
515
     * @param $itemId array|Type\ItemIdType
516
     * @param array $options
517
     * @return Type
518
     */
519 2 View Code Duplication
    public function getItem($itemId, $options = array())
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...
520
    {
521 2
        if ($itemId instanceof Type\ItemIdType) {
0 ignored issues
show
Bug introduced by
The class garethp\ews\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...
522 1
            $itemId = $itemId->toArray();
523
        }
524
525
        $request = array(
526 2
            'ItemShape' => array('BaseShape' => 'AllProperties'),
527 2
            'ItemIds' => array('ItemId' => $itemId)
528
        );
529
530 2
        $request = array_replace_recursive($request, $options);
531
532 2
        return $this->getClient()->GetItem($request);
533
    }
534
535
    /**
536
     * Get a list of sync changes on a folder
537
     *
538
     * @param Type\FolderIdType $folderId
539
     * @param null $syncState
540
     * @param array $options
541
     * @return SyncFolderItemsResponseMessageType
542
     */
543 2
    public function listItemChanges($folderId, $syncState = null, $options = array())
544
    {
545
        $request = array(
546 2
            'ItemShape' => array('BaseShape' => 'IdOnly'),
547 2
            'SyncFolderId' => array('FolderId' => $folderId->toXmlObject()),
548 2
            'SyncScope' => 'NormalItems',
549 2
            'MaxChangesReturned' => '10'
550
        );
551
552 2
        if ($syncState != null) {
553 1
            $request['SyncState'] = $syncState;
554 1
            $request['ItemShape']['BaseShape'] = 'AllProperties';
555
        }
556
557 2
        $request = array_replace_recursive($request, $options);
558
559 2
        $request = Type::buildFromArray($request);
560 2
        $response = $this->getClient()->SyncFolderItems($request);
561 2
        return $response;
562
    }
563
564 View Code Duplication
    public function getServerTimezones($timezoneIDs = array(), $fullTimezoneData = 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...
565
    {
566
        $request = GetServerTimeZonesType::buildFromArray(array(
567
            'returnFullTimeZoneData' => $fullTimezoneData
568
        ));
569
570
        if (!empty($timezoneIDs)) {
571
            $request->setIds($timezoneIDs);
572
        }
573
574
        $timezones = $this->getClient()->GetServerTimeZones($request);
0 ignored issues
show
Documentation Bug introduced by
The method GetServerTimeZones does not exist on object<garethp\ews\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...
575
        $timezones = $timezones->TimeZoneDefinition;
576
577
        if (!is_array($timezones)) {
578
            $timezones = array($timezones);
579
        }
580
581
        return $timezones;
582
    }
583
584
    /**
585
     * @param Type\ItemIdType $itemId
586
     * @param $fromType
587
     * @param $destinationType
588
     * @param $mailbox
589
     *
590
     * @return Type\ItemIdType
591
     */
592
    public function convertIdFormat(Type\ItemIdType $itemId, $fromType, $destinationType, $mailbox)
593
    {
594
        $result = $this->getClient()->ConvertId(array(
0 ignored issues
show
Documentation Bug introduced by
The method ConvertId does not exist on object<garethp\ews\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...
595
            'DestinationFormat' => $destinationType,
596
            'SourceIds' => array (
597
                'AlternateId' => array(
598
                    'Format' => $fromType,
599
                    'Id' => $itemId->getId(),
600
                    'Mailbox' => $mailbox
601
                )
602
            )
603
        ));
604
605
        $itemId->setId($result->getId());
606
607
        return $itemId;
608
    }
609
}
610