Completed
Push — master ( fb1c43...54cc50 )
by Gareth
04:48
created

API::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

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