Completed
Push — master ( 617cd0...5e87a2 )
by Gareth
08:23
created

API::listItemChanges()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 21
ccs 0
cts 7
cp 0
rs 9.3142
cc 2
eloc 13
nc 2
nop 3
crap 6
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\Message\UpdateItemResponseMessageType;
12
use garethp\ews\API\Type;
13
use garethp\ews\Calendar\CalendarAPI;
14
use garethp\ews\Mail\MailAPI;
15
use garethp\ews\API\FieldURIManager;
16
17
/**
18
 * A base class for APIs
19
 *
20
 * Class BaseAPI
21
 * @package garethp\ews
22
 */
23
class API
24
{
25
    protected static $defaultClientOptions = array(
26
        'version' => ExchangeWebServices::VERSION_2010
27
    );
28
29 30
    public function __construct(ExchangeWebServices $client = null)
30
    {
31 30
        if ($client) {
32 18
            $this->setClient($client);
33
        }
34
    }
35
36
    /**
37
     * @return Type\EmailAddressType
38
     */
39 21
    public function getPrimarySmtpMailbox()
40
    {
41 21
        return $this->getClient()->getPrimarySmtpMailbox();
42
    }
43
44
    /**
45
     * Storing the API client
46
     * @var ExchangeWebServices
47
     */
48
    private $client;
49
50
    /**
51
     * @deprecated This will be removed in 0.9
52
     *
53
     * @param $className
54
     * @return array
55
     */
56
    public function getFieldUrisFromClass($className)
57
    {
58
        return FieldURIManager::getFieldUrisFromClass($className);
0 ignored issues
show
Deprecated Code introduced by
The method garethp\ews\API\FieldURI...getFieldUrisFromClass() has been deprecated with message: This will be made protected in 0.9

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
59
    }
60
61
    public function setupFieldUris()
62
    {
63
        FieldURIManager::setupFieldUris();
0 ignored issues
show
Deprecated Code introduced by
The method garethp\ews\API\FieldURIManager::setupFieldUris() has been deprecated with message: This will be made protected in 0.9

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

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