Completed
Push — master ( 42a4de...b05bb8 )
by Gareth
03:31
created

API::getIndexedFieldUriByName()   C

Complexity

Conditions 8
Paths 22

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 10.6925

Importance

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