Completed
Push — master ( fd5d01...725272 )
by Gareth
03:16
created

API   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 498
Duplicated Lines 7.63 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 84.38%

Importance

Changes 15
Bugs 2 Features 4
Metric Value
wmc 50
c 15
b 2
f 4
lcom 1
cbo 4
dl 38
loc 498
ccs 189
cts 224
cp 0.8438
rs 8.6206

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A getPrimarySmtpMailbox() 0 4 1
A getCalendar() 0 8 1
A getMailbox() 0 8 1
A setClient() 0 6 1
A getClient() 0 4 1
A getPrimarySmptEmailAddress() 0 8 2
A setPrimarySmtpEmailAddress() 0 6 1
A withUsernameAndPassword() 0 9 1
A withCallbackToken() 0 8 1
A createItems() 0 17 2
A updateItems() 0 23 3
B createCalendars() 0 27 3
B createFolders() 0 23 4
A deleteFolder() 12 13 1
A moveItem() 11 11 1
B deleteItems() 0 30 5
A getFolder() 0 14 1
A getFolderByDistinguishedId() 0 9 1
A getFolderByFolderId() 0 6 1
B getChildrenFolders() 0 25 2
A getFolderByDisplayName() 0 12 3
A getItem() 15 15 2
A listItemChanges() 0 21 2
A getServerTimezones() 0 19 3
A convertIdFormat() 0 17 1
A getNextPage() 0 23 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like API often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use API, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace garethp\ews;
4
5
use garethp\ews\API\Enumeration\DisposalType;
6
use garethp\ews\API\Enumeration\IndexBasePointType;
7
use garethp\ews\API\ExchangeWebServices;
8
use garethp\ews\API\ItemUpdateBuilder;
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\CalendarAPI;
14
use garethp\ews\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 33
    public function __construct(ExchangeWebServices $client = null)
29
    {
30 33
        if ($client) {
31 33
            $this->setClient($client);
32 33
        }
33 33
    }
34
35
    /**
36
     * @return Type\EmailAddressType
37
     */
38 25
    public function getPrimarySmtpMailbox()
39
    {
40 25
        return $this->getClient()->getPrimarySmtpMailbox();
41
    }
42
43
    /**
44
     * Storing the API client
45
     * @var ExchangeWebServices
46
     */
47
    private $client;
48
49
    /**
50
     * Get a calendar item
51
     *
52
     * @param string $name
53
     * @return CalendarAPI
54
     */
55 6
    public function getCalendar($name = null)
56
    {
57 6
        $calendar = new CalendarAPI();
58 6
        $calendar->setClient($this->getClient());
59 6
        $calendar->pickCalendar($name);
60
61 6
        return $calendar;
62
    }
63
64
    /**
65
     * @param string $folderName
66
     * @return MailAPI
67
     */
68 6
    public function getMailbox($folderName = null)
69
    {
70 6
        $mailApi = new MailAPI();
71 6
        $mailApi->setClient($this->getClient());
72 6
        $mailApi->pickMailFolder($folderName);
73
74 6
        return $mailApi;
75
    }
76
77
    /**
78
     * Set the API client
79
     *
80
     * @param ExchangeWebServices $client
81
     * @return $this
82
     */
83 33
    public function setClient($client)
84
    {
85 33
        $this->client = $client;
86
87 33
        return $this;
88
    }
89
90
    /**
91
     * Get the API client
92
     *
93
     * @return ExchangeWebServices
94
     */
95 32
    public function getClient()
96
    {
97 32
        return $this->client;
98
    }
99
100 32
    public static function withUsernameAndPassword($server, $username, $password, $options = [])
101
    {
102 32
        return new static(ExchangeWebServices::fromUsernameAndPassword(
103 32
            $server,
104 32
            $username,
105 32
            $password,
106 32
            array_replace_recursive(self::$defaultClientOptions, $options)
107 32
        ));
108
    }
109
110 1
    public static function withCallbackToken($server, $token, $options = [])
111
    {
112 1
        return new static(ExchangeWebServices::fromCallbackToken(
113 1
            $server,
114 1
            $token,
115 1
            array_replace_recursive(self::$defaultClientOptions, $options)
116 1
        ));
117
    }
118
119 1
    public function getPrimarySmptEmailAddress()
120
    {
121 1
        if ($this->getPrimarySmtpMailbox() == null) {
122 1
            return null;
123
        }
124
125 1
        return $this->getPrimarySmtpMailbox()->getEmailAddress();
126
    }
127
128 1
    public function setPrimarySmtpEmailAddress($emailAddress)
129
    {
130 1
        $this->getClient()->setPrimarySmtpEmailAddress($emailAddress);
131
132 1
        return $this;
133
    }
134
135
    /**
136
     * Create items through the API client
137
     *
138
     * @param $items
139
     * @param array $options
140
     * @return Type
141
     */
142 16
    public function createItems($items, $options = array())
143
    {
144 16
        if (!is_array($items)) {
145
            $items = array($items);
146
        }
147
148
        $request = array(
149
            'Items' => $items
150 16
        );
151
152 16
        $request = array_replace_recursive($request, $options);
153 16
        $request = Type::buildFromArray($request);
154
155 16
        $response = $this->getClient()->CreateItem($request);
156
157 16
        return $response;
158
    }
159
160 4
    public function updateItems($items, $options = array())
161
    {
162
        $request = array(
163 4
            'ItemChanges' => $items,
164 4
            'MessageDisposition' => 'SaveOnly',
165
            'ConflictResolution' => 'AlwaysOverwrite'
166 4
        );
167
168 4
        $request = array_replace_recursive($request, $options);
169
170 4
        $request = Type::buildFromArray($request);
171
172 4
        $response = $this->getClient()->UpdateItem($request);
173 4
        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...
174 4
            return $response->getItems();
175
        }
176
177
        if (!is_array($response)) {
178
            $response = array($response);
179
        }
180
181
        return $response;
182
    }
183
184 1
    public function createCalendars($names, Type\FolderIdType $parentFolder = null, $options = array())
185
    {
186 1
        if ($parentFolder == null) {
187 1
            $parentFolder = $this->getFolderByDistinguishedId('calendar')->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...
188 1
        }
189
190 1
        if (!is_array($names)) {
191 1
            $names = array($names);
192 1
        }
193
194 1
        $names = array_map(function ($name) {
195
            return array(
196 1
                'DisplayName' => $name,
197
                'FolderClass' => 'IPF.Appointment'
198 1
            );
199 1
        }, $names);
200
201
        $request = [
202 1
            'Folders' => ['Folder' => $names],
203 1
            'ParentFolderId' => ['FolderId' => $parentFolder->toArray()]
204 1
        ];
205
206 1
        $request = array_merge_recursive($request, $options);
207
208 1
        $this->client->CreateFolder($request);
209 1
        return true;
210
    }
211
212 2
    public function createFolders($names, Type\FolderIdType $parentFolder, $options = array())
213
    {
214 2
        $request = array('Folders' => array('Folder' => array()));
215 2
        if (!empty($parentFolder)) {
216 2
            $request['ParentFolderId'] = array('FolderId' => $parentFolder->toArray());
217 2
        }
218
219 2
        if (!is_array($names)) {
220 2
            $names = array($names);
221 2
        }
222
223 2
        foreach ($names as $name) {
224 2
            $request['Folders']['Folder'][] = array(
225
                'DisplayName' => $name
226 2
            );
227 2
        }
228
229 2
        $request = array_merge_recursive($request, $options);
230
231 2
        $this->client->CreateFolder($request);
232
233 2
        return true;
234
    }
235
236 3 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...
237
    {
238
        $request = array(
239 3
            'DeleteType' => 'HardDelete',
240
            'FolderIds' => array(
241 3
                'FolderId' => $folderId->toArray()
242 3
            )
243 3
        );
244
245 3
        $request = array_merge_recursive($request, $options);
246
247 3
        return $this->client->DeleteFolder($request);
248
    }
249
250 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...
251
    {
252
        $request = array(
253
            'ToFolderId' => array('FolderId' => $folderId->toArray()),
254
            'ItemIds' => array('ItemId' => $itemId->toArray())
255
        );
256
257
        $request = array_merge_recursive($request, $options);
258
259
        return $this->client->MoveItem($request);
260
    }
261
262
    /**
263
     * @param        $items Type\ItemIdType|Type\ItemIdType[]
264
     * @param array  $options
265
     * @return bool
266
     */
267 16
    public function deleteItems($items, $options = array())
268
    {
269 16
        if (!is_array($items) || Type::arrayIsAssoc($items)) {
270 15
            $items = array($items);
271 15
        }
272
273 16
        $itemIds = array();
274 16
        foreach ($items as $item) {
275 16
            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...
276 15
                $item = $item->toArray();
277 15
            }
278 16
            $item = (array)$item;
279 16
            $itemIds[] = array(
280 16
                'Id' => $item['Id'],
281 16
                'ChangeKey' => $item['ChangeKey']
282 16
            );
283 16
        }
284
285
        $request = array(
286 16
            'ItemIds' => array('ItemId' => $itemIds),
287
            'DeleteType' => 'MoveToDeletedItems'
288 16
        );
289
290 16
        $request = array_replace_recursive($request, $options);
291 16
        $request = Type::buildFromArray($request);
292 16
        $this->getClient()->DeleteItem($request);
293
294
        //If the delete fails, an Exception will be thrown in processResponse before it gets here
295 16
        return true;
296
    }
297
298
    /**
299
     * @param $identifier
300
     * @return Type\BaseFolderType
301
     */
302 24
    public function getFolder($identifier)
303
    {
304
        $request = array(
305
            'FolderShape' => array(
306 24
                'BaseShape' => array('_' => 'Default')
307 24
            ),
308
            'FolderIds' => $identifier
309 24
        );
310 24
        $request = Type::buildFromArray($request);
311
312 24
        $response = $this->getClient()->GetFolder($request);
313
314 24
        return $response;
315
    }
316
317
    /**
318
     * Get a folder by it's distinguishedId
319
     *
320
     * @param string $distinguishedId
321
     * @return Type\BaseFolderType
322
     */
323 24
    public function getFolderByDistinguishedId($distinguishedId)
324
    {
325 24
        return $this->getFolder(array(
326
            'DistinguishedFolderId' => array(
327 24
                'Id' => $distinguishedId,
328 24
                'Mailbox' => $this->getPrimarySmtpMailbox()
329 24
            )
330 24
        ));
331
    }
332
333
    /**
334
     * @param $folderId
335
     * @return Type\BaseFolderType
336
     */
337 4
    public function getFolderByFolderId($folderId)
338
    {
339 4
        return $this->getFolder(array(
340 4
            'FolderId' => array('Id' => $folderId, 'Mailbox' => $this->getPrimarySmtpMailbox())
341 4
        ));
342
    }
343
344
    /**
345
     * @param string|Type\FolderIdType $parentFolderId
346
     * @param array $options
347
     * @return bool|Type\BaseFolderType
348
     */
349 23
    public function getChildrenFolders($parentFolderId = 'root', $options = array())
350
    {
351 23
        if (is_string($parentFolderId)) {
352 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...
353 15
        }
354
355
        $request = array(
356 23
            'Traversal' => 'Shallow',
357
            'FolderShape' => array(
358
                'BaseShape' => 'AllProperties'
359 23
            ),
360
            'ParentFolderIds' => array(
361 23
                'FolderId' => $parentFolderId->toArray()
362 23
            )
363 23
        );
364
365 23
        $request = array_replace_recursive($request, $options);
366
367 23
        $request = Type::buildFromArray($request);
368
369
        /** @var \garethp\ews\API\Message\FindFolderResponseMessageType $folders */
370 23
        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...
371
372
        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...
373
    }
374
375
    /**
376
     * @param string $folderName
377
     * @param string|Type\FolderIdType $parentFolderId
378
     * @param array $options
379
     * @return bool|Type\BaseFolderType
380
     */
381 23
    public function getFolderByDisplayName($folderName, $parentFolderId = 'root', $options = array())
382
    {
383 23
        $folders = $this->getChildrenFolders($parentFolderId, $options);
384
385 23
        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...
386 23
            if ($folder->getDisplayName() == $folderName) {
387 22
                return $folder;
388
            }
389 17
        }
390
391 4
        return false;
392
    }
393
394
    /**
395
     * @param $itemId array|Type\ItemIdType
396
     * @param array $options
397
     * @return Type
398
     */
399 5 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...
400
    {
401 5
        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...
402 4
            $itemId = $itemId->toArray();
403 4
        }
404
405
        $request = array(
406 5
            'ItemShape' => array('BaseShape' => 'AllProperties'),
407 5
            'ItemIds' => array('ItemId' => $itemId)
408 5
        );
409
410 5
        $request = array_replace_recursive($request, $options);
411
412 5
        return $this->getClient()->GetItem($request);
413
    }
414
415
    /**
416
     * Get a list of sync changes on a folder
417
     *
418
     * @param Type\FolderIdType $folderId
419
     * @param null $syncState
420
     * @param array $options
421
     * @return SyncFolderItemsResponseMessageType
422
     */
423 2
    public function listItemChanges($folderId, $syncState = null, $options = array())
424
    {
425
        $request = array(
426 2
            'ItemShape' => array('BaseShape' => 'IdOnly'),
427 2
            'SyncFolderId' => array('FolderId' => $folderId->toXmlObject()),
428 2
            'SyncScope' => 'NormalItems',
429
            'MaxChangesReturned' => '100'
430 2
        );
431
432 2
        if ($syncState != null) {
433 1
            $request['SyncState'] = $syncState;
434 1
            $request['ItemShape']['BaseShape'] = 'AllProperties';
435 1
        }
436
437 2
        $request = array_replace_recursive($request, $options);
438
439 2
        $request = Type::buildFromArray($request);
440 2
        $response = $this->getClient()->SyncFolderItems($request);
441
442 2
        return $response;
443
    }
444
445
    public function getServerTimezones($timezoneIDs = array(), $fullTimezoneData = false)
446
    {
447
        $request = GetServerTimeZonesType::buildFromArray(array(
448
            'returnFullTimeZoneData' => $fullTimezoneData
449
        ));
450
451
        if (!empty($timezoneIDs)) {
452
            $request->setIds($timezoneIDs);
453
        }
454
455
        $timezones = $this->getClient()->GetServerTimeZones($request);
456
        $timezones = $timezones->TimeZoneDefinition;
0 ignored issues
show
Documentation introduced by
The property TimeZoneDefinition does not exist on object<garethp\ews\API\Type>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
457
458
        if (!is_array($timezones)) {
459
            $timezones = array($timezones);
460
        }
461
462
        return $timezones;
463
    }
464
465
    /**
466
     * @param Type\ItemIdType $itemId
467
     * @param $fromType
468
     * @param $destinationType
469
     * @param $mailbox
470
     *
471
     * @return Type\ItemIdType
472
     */
473
    public function convertIdFormat(Type\ItemIdType $itemId, $fromType, $destinationType, $mailbox)
474
    {
475
        $result = $this->getClient()->ConvertId(array(
476
            'DestinationFormat' => $destinationType,
477
            'SourceIds' => array(
478
                'AlternateId' => array(
479
                    'Format' => $fromType,
480
                    'Id' => $itemId->getId(),
481
                    'Mailbox' => $mailbox
482
                )
483
            )
484
        ));
485
486
        $itemId->setId($result->getId());
0 ignored issues
show
Documentation Bug introduced by
The method getId 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...
487
488
        return $itemId;
489
    }
490
491
    /**
492
     * @param Type\FindItemParentType | Type\FindFolderParentType $result
493
     *
494
     * @return Type\FindItemParentType | Type\FindFolderParentType
495
     */
496 1
    public function getNextPage($result)
497
    {
498 1
        if ($result->isIncludesLastItemInRange()) {
499
            return $result;
500
        }
501
502 1
        $offset = 0;
503 1
        $maxEntries = count($result);
504 1
        $basePoint = IndexBasePointType::BEGINNING;
505
506 1
        $lastRequest = $result->getLastRequest();
507 1
        if ($lastRequest->getIndexedPageItemView()) {
508 1
            $indexedView = $lastRequest->getIndexedPageItemView();
509
            /* @var $indexedView \garethp\ews\API\Type\IndexedPageViewType */
510
511 1
            $offset = $indexedView->getOffset() + $maxEntries;
512 1
            $basePoint = $indexedView->getBasePoint();
513 1
        }
514
515 1
        $lastRequest->setIndexedPageItemView(new Type\IndexedPageViewType($maxEntries, $offset, $basePoint));
516
517 1
        return $this->getClient()->FindItem($lastRequest);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getClient(...FindItem($lastRequest); (garethp\ews\API\Type) is incompatible with the return type documented by garethp\ews\API::getNextPage of type garethp\ews\API\Type\FindItemParentType.

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...
518
    }
519
}
520