Completed
Push — master ( 56e42e...5267c5 )
by Bram
01:44
created

Reservation   C

Complexity

Total Complexity 44

Size/Duplication

Total Lines 452
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 19

Importance

Changes 0
Metric Value
wmc 44
lcom 2
cbo 19
dl 0
loc 452
rs 5.5062
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A getCMSFields() 0 20 1
A getBetterButtonsActions() 0 8 1
A onBeforeWrite() 0 12 3
A singular_name() 0 5 1
A getGatewayNice() 0 4 1
A isDiscarded() 0 5 2
A getName() 0 9 2
A getState() 0 4 1
A getStates() 0 6 1
A calculateTotal() 0 16 3
A changeState() 0 11 2
A setMainContact() 0 5 1
A createReservationCode() 0 4 1
A fileFolder() 0 4 1
A createFiles() 0 9 2
B sendReservation() 0 24 2
B sendTickets() 0 48 4
B sendNotification() 0 24 3
A send() 0 7 1
A getDownloadLink() 0 4 1
A canView() 0 4 1
A canEdit() 0 4 1
A canDelete() 0 4 1
A canCreate() 0 4 1
B onBeforeDelete() 0 17 6

How to fix   Complexity   

Complex Class

Complex classes like Reservation 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 Reservation, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Reservation.php
4
 *
5
 * @author Bram de Leeuw
6
 * Date: 09/03/17
7
 */
8
9
namespace Broarm\EventTickets;
10
11
use BetterButtonCustomAction;
12
use CalendarEvent;
13
use CheckboxField;
14
use Config;
15
use DataObject;
16
use DropdownField;
17
use Email;
18
use FieldList;
19
use Folder;
20
use GridField;
21
use GridFieldConfig_RecordViewer;
22
use HasManyList;
23
use ManyManyList;
24
use ReadonlyField;
25
use SilverStripe\Omnipay\GatewayInfo;
26
use SiteConfig;
27
use Tab;
28
use TabSet;
29
30
/**
31
 * Class Reservation
32
 *
33
 * @package Broarm\EventTickets
34
 *
35
 * @property string Status
36
 * @property string Title
37
 * @property float  Subtotal
38
 * @property float  Total
39
 * @property string Comments
40
 * @property string ReservationCode
41
 * @property string Gateway
42
 *
43
 * @property int    EventID
44
 * @property int    MainContactID
45
 *
46
 * @method CalendarEvent|TicketExtension Event()
47
 * @method Attendee MainContact()
48
 * @method HasManyList Payments()
49
 * @method HasManyList Attendees()
50
 * @method ManyManyList PriceModifiers()
51
 */
52
class Reservation extends DataObject
53
{
54
    /**
55
     * Time to wait before deleting the discarded cart
56
     * Give a string that is parsable by strtotime
57
     *
58
     * @var string
59
     */
60
    private static $delete_after = '+1 hour';
1 ignored issue
show
Unused Code introduced by
The property $delete_after is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
61
62
    /**
63
     * The address to whom the ticket notifications are sent
64
     * By default the admin email is used
65
     *
66
     * @config
67
     * @var string
68
     */
69
    private static $mail_sender;
1 ignored issue
show
Unused Code introduced by
The property $mail_sender is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
70
71
    /**
72
     * The address from where the ticket mails are sent
73
     * By default the admin email is used
74
     *
75
     * @config
76
     * @var string
77
     */
78
    private static $mail_receiver;
1 ignored issue
show
Unused Code introduced by
The property $mail_receiver is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
79
80
    private static $db = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
81
        'Status' => 'Enum("CART,PENDING,PAID,CANCELED","CART")',
82
        'Title' => 'Varchar(255)',
83
        'Subtotal' => 'Currency',
84
        'Total' => 'Currency',
85
        'Gateway' => 'Varchar(255)',
86
        'Comments' => 'Text',
87
        'AgreeToTermsAndConditions' => 'Boolean',
88
        'ReservationCode' => 'Varchar(255)'
89
    );
90
91
    private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
92
        'Event' => 'CalendarEvent',
93
        'MainContact' => 'Broarm\EventTickets\Attendee'
94
    );
95
96
    private static $has_many = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
97
        'Payments' => 'Payment',
98
        'Attendees' => 'Broarm\EventTickets\Attendee.Reservation'
99
    );
100
101
    private static $belongs_many_many = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $belongs_many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
102
        'PriceModifiers' => 'Broarm\EventTickets\PriceModifier'
103
    );
104
105
    private static $indexes = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $indexes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
106
        'ReservationCode' => 'unique("ReservationCode")'
107
    );
108
109
    private static $summary_fields = array(
2 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
110
        'ReservationCode' => 'Reservation',
111
        'Title' => 'Customer',
112
        'Total.Nice' => 'Total',
113
        'State' => 'Status',
114
        'GatewayNice' => 'Payment method',
115
        'Created.Nice' => 'Date'
116
    );
117
118
    /**
119
     * Actions usable on the cms detail view
120
     *
121
     * @var array
122
     */
123
    private static $better_buttons_actions = array(
1 ignored issue
show
Unused Code introduced by
The property $better_buttons_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
124
        'send'
125
    );
126
127
    public function getCMSFields()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
128
    {
129
        $fields = new FieldList(new TabSet('Root', $mainTab = new Tab('Main')));
130
        $gridFieldConfig = GridFieldConfig_RecordViewer::create();
131
        $fields->addFieldsToTab('Root.Main', array(
132
            ReadonlyField::create('ReservationCode', _t('Reservation.Code', 'Code')),
133
            ReadonlyField::create('Created', _t('Reservation.Created', 'Date')),
134
            DropdownField::create('Status', _t('Reservation.Status', 'Status'), $this->getStates()),
135
            ReadonlyField::create('Title', _t('Reservation.MainContact', 'Main contact')),
136
            ReadonlyField::create('GateWayNice', _t('Reservation.Gateway', 'Gateway')),
137
            ReadonlyField::create('Total', _t('Reservation.Total', 'Total')),
138
            ReadonlyField::create('Comments', _t('Reservation.Comments', 'Comments')),
139
            CheckboxField::create('AgreeToTermsAndConditions', _t('Reservation.AgreeToTermsAndConditions', 'Agreed to terms and conditions'))->performReadonlyTransformation(),
140
            GridField::create('Attendees', 'Attendees', $this->Attendees(), $gridFieldConfig),
141
            GridField::create('Payments', 'Payments', $this->Payments(), $gridFieldConfig)
142
        ));
143
        $fields->addFieldsToTab('Root.Main', array());
144
        $this->extend('updateCMSFields', $fields);
145
        return $fields;
146
    }
147
148
    /**
149
     * Add utility actions to the reservation details view
150
     *
151
     * @return FieldList
152
     */
153
    public function getBetterButtonsActions()
154
    {
155
        /** @var FieldList $fields */
156
        $fields = parent::getBetterButtonsActions();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class DataObject as the method getBetterButtonsActions() does only exist in the following sub-classes of DataObject: Broarm\EventTickets\Reservation. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
157
        $fields->push(BetterButtonCustomAction::create('send', _t('Reservation.RESEND', 'Resend the reservation')));
158
159
        return $fields;
160
    }
161
162
    /**
163
     * Generate a reservation code if it does not yet exists
164
     */
165
    public function onBeforeWrite()
166
    {
167
        // Set the title to the name of the reservation holder
168
        $this->Title = $this->getName();
169
170
        // Create a validation code to be used for confirmation and in the barcode
171
        if ($this->exists() && empty($this->ReservationCode)) {
172
            $this->ReservationCode = $this->createReservationCode();
173
        }
174
175
        parent::onBeforeWrite();
176
    }
177
178
    /**
179
     * After deleting a reservation, delete the attendees and files
180
     */
181
    public function onBeforeDelete()
182
    {
183
        // If a reservation is deleted remove the names from the guest list
184
        foreach ($this->Attendees() as $attendee) {
185
            /** @var Attendee $attendee */
186
            if ($attendee->exists()) {
187
                $attendee->delete();
188
            }
189
        }
190
191
        // Remove the folder
192
        if (($folder = Folder::get()->find('Name', $this->ReservationCode)) && $folder->exists() && $folder->isEmpty()) {
193
            $folder->delete();
194
        }
195
196
        parent::onBeforeDelete();
197
    }
198
199
    /**
200
     * Gets a nice unnamespaced name
201
     *
202
     * @return string
203
     */
204
    public function singular_name()
205
    {
206
        $name = explode('\\', parent::singular_name());
207
        return trim(end($name));
208
    }
209
210
    /**
211
     * Returns the nice gateway title
212
     *
213
     * @return string
214
     */
215
    public function getGatewayNice()
216
    {
217
        return GatewayInfo::niceTitle($this->Gateway);
218
    }
219
220
    /**
221
     * Check if the cart is still in cart state and the delete_after time period has been exceeded
222
     *
223
     * @return bool
224
     */
225
    public function isDiscarded()
226
    {
227
        $deleteAfter = strtotime(self::config()->get('delete_after'), strtotime($this->Created));
228
        return ($this->Status === 'CART') && (time() > $deleteAfter);
229
    }
230
231
    /**
232
     * Get the full name
233
     *
234
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
235
     */
236
    public function getName()
237
    {
238
        /** @var Attendee $attendee */
239
        if ($this->MainContact()->exists()) {
240
            return $this->MainContact()->getName();
241
        } else {
242
            return 'new reservation';
243
        }
244
    }
245
246
    /**
247
     * Return the translated state
248
     *
249
     * @return string
250
     */
251
    public function getState()
252
    {
253
        return _t("Reservation.{$this->Status}", $this->Status);
254
    }
255
256
    /**
257
     * Get a the translated map of available states
258
     *
259
     * @return array
260
     */
261
    private function getStates()
262
    {
263
        return array_map(function ($state) {
264
            return _t("Reservation.$state", $state);
265
        }, $this->dbObject('Status')->enumValues());
266
    }
267
268
    /**
269
     * Get the total by querying the sum of attendee ticket prices
270
     *
271
     * @return float
272
     */
273
    public function calculateTotal()
274
    {
275
        $total = $this->Subtotal = $this->Attendees()->leftJoin(
276
            'Broarm\EventTickets\Ticket',
277
            '`Broarm\EventTickets\Attendee`.`TicketID` = `Broarm\EventTickets\Ticket`.`ID`'
278
        )->sum('Price');
279
280
        // Calculate any price modifications if added
281
        if ($this->PriceModifiers()->exists()) {
282
            foreach ($this->PriceModifiers() as $priceModifier) {
283
                $priceModifier->updateTotal($total);
284
            }
285
        }
286
287
        return $this->Total = $total;
288
    }
289
290
    /**
291
     * Safely change to a state
292
     * todo check if state direction matches
293
     *
294
     * @param $state
295
     *
296
     * @return boolean
297
     */
298
    public function changeState($state)
299
    {
300
        $availableStates = $this->dbObject('Status')->enumValues();
301
        if (in_array($state, $availableStates)) {
302
            $this->Status = $state;
303
            return true;
304
        } else {
305
            user_error(_t('Reservation.STATE_CHANGE_ERROR', 'Selected state is not available'));
306
            return false;
307
        }
308
    }
309
310
    /**
311
     * Set the main contact id
312
     *
313
     * @param $id
314
     */
315
    public function setMainContact($id)
316
    {
317
        $this->MainContactID = $id;
318
        $this->write();
319
    }
320
321
    /**
322
     * Create a reservation code
323
     *
324
     * @return string
325
     */
326
    public function createReservationCode()
327
    {
328
        return uniqid($this->ID);
329
    }
330
331
    /**
332
     * Create the folder for the qr code and ticket file
333
     *
334
     * @return Folder|DataObject|null
335
     */
336
    public function fileFolder()
337
    {
338
        return Folder::find_or_make("/event-tickets/{$this->ReservationCode}/");
339
    }
340
341
    /**
342
     * Generate the qr codes and downloadable pdf
343
     */
344
    public function createFiles()
345
    {
346
        $folder = $this->fileFolder();
347
        /** @var Attendee $attendee */
348
        foreach ($this->Attendees() as $attendee) {
349
            $attendee->createQRCode($folder);
350
            $attendee->createTicketFile($folder);
351
        }
352
    }
353
354
    /**
355
     * Send the reservation mail
356
     */
357
    public function sendReservation()
358
    {
359
        // Get the mail sender or fallback to the admin email
360
        if (empty($from = self::config()->get('mail_sender'))) {
361
            $from = Config::inst()->get('Email', 'admin_email');
362
        }
363
364
        // Create the email with given template and reservation data
365
        $email = new Email();
366
        $email->setSubject(_t(
367
            'ReservationMail.TITLE',
368
            'Your order at {sitename}',
369
            null,
370
            array(
0 ignored issues
show
Documentation introduced by
array('sitename' => \Sit...t_site_config()->Title) is of type array<string,string,{"sitename":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
371
                'sitename' => SiteConfig::current_site_config()->Title
372
            )
373
        ));
374
        $email->setFrom($from);
375
        $email->setTo($this->MainContact()->Email);
376
        $email->setTemplate('ReservationMail');
377
        $email->populateTemplate($this);
378
        $this->extend('updateReservationMail', $email);
379
        $email->send();
380
    }
381
382
    /**
383
     * Send the reserved tickets
384
     */
385
    public function sendTickets()
386
    {
387
        // Get the mail sender or fallback to the admin email
388
        if (empty($from = self::config()->get('mail_sender'))) {
389
            $from = Config::inst()->get('Email', 'admin_email');
390
        }
391
392
        // Send the tickets to the main contact
393
        $email = new Email();
394
        $email->setSubject(_t(
395
            'MainContactMail.TITLE',
396
            'Uw tickets voor {event}',
397
            null,
398
            array(
0 ignored issues
show
Documentation introduced by
array('event' => $this->Event()->Title) is of type array<string,?,{"event":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
399
                'event' => $this->Event()->Title
400
            )
401
        ));
402
        $email->setFrom($from);
403
        $email->setTo($this->MainContact()->Email);
404
        $email->setTemplate('MainContactMail');
405
        $email->populateTemplate($this);
406
        $this->extend('updateMainContactMail', $email);
407
        $email->send();
408
409
410
        // Get the attendees for this event that are checked as receiver
411
        $ticketReceivers = $this->Attendees()->filter('TicketReceiver', 1)->exclude('ID', $this->MainContactID);
412
        if ($ticketReceivers->exists()) {
413
            /** @var Attendee $ticketReceiver */
414
            foreach ($ticketReceivers as $ticketReceiver) {
415
                $email = new Email();
416
                $email->setSubject(_t(
417
                    'AttendeeMail.TITLE',
418
                    'Your ticket for {event}',
419
                    null,
420
                    array(
0 ignored issues
show
Documentation introduced by
array('event' => $this->Event()->Title) is of type array<string,?,{"event":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
421
                        'event' => $this->Event()->Title
422
                    )
423
                ));
424
                $email->setFrom($from);
425
                $email->setTo($ticketReceiver->Email);
426
                $email->setTemplate('AttendeeMail');
427
                $email->populateTemplate($ticketReceiver);
428
                $this->extend('updateTicketMail', $email);
429
                $email->send();
430
            }
431
        }
432
    }
433
434
435
    /**
436
     * Send a booking notification to the ticket mail sender or the site admin
437
     */
438
    public function sendNotification()
439
    {
440
        if (empty($from = self::config()->get('mail_sender'))) {
441
            $from = Config::inst()->get('Email', 'admin_email');
442
        }
443
444
        if (empty($to = self::config()->get('mail_receiver'))) {
445
            $to = Config::inst()->get('Email', 'admin_email');
446
        }
447
448
        $email = new Email();
449
        $email->setSubject(_t(
450
            'NotificationMail.TITLE',
451
            'Nieuwe reservering voor {event}',
452
            null, array('event' => $this->Event()->Title)
0 ignored issues
show
Documentation introduced by
array('event' => $this->Event()->Title) is of type array<string,?,{"event":"?"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
453
        ));
454
455
        $email->setFrom($from);
456
        $email->setTo($to);
457
        $email->setTemplate('NotificationMail');
458
        $email->populateTemplate($this);
459
        $this->extend('updateNotificationMail', $email);
460
        $email->send();
461
    }
462
463
    /**
464
     * Create the files and send the reservation, notification and tickets
465
     */
466
    public function send()
467
    {
468
        $this->createFiles();
469
        $this->sendReservation();
470
        $this->sendNotification();
471
        $this->sendTickets();
472
    }
473
474
    /**
475
     * Get the download link
476
     *
477
     * @return string
478
     */
479
    public function getDownloadLink()
480
    {
481
        return $this->reservation->Attendees()->first()->TicketFile()->Link();
0 ignored issues
show
Bug introduced by
The property reservation does not seem to exist. Did you mean ReservationCode?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
482
    }
483
484
    public function canView($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
485
    {
486
        return $this->Event()->canView($member);
487
    }
488
489
    public function canEdit($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
490
    {
491
        return $this->Event()->canEdit($member);
1 ignored issue
show
Bug introduced by
The method canEdit does only exist in Broarm\EventTickets\TicketExtension, but not in CalendarEvent.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
492
    }
493
494
    public function canDelete($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
495
    {
496
        return $this->Event()->canDelete($member);
1 ignored issue
show
Bug introduced by
The method canDelete does only exist in Broarm\EventTickets\TicketExtension, but not in CalendarEvent.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
497
    }
498
499
    public function canCreate($member = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
500
    {
501
        return $this->Event()->canCreate($member);
1 ignored issue
show
Bug introduced by
The method canCreate does only exist in Broarm\EventTickets\TicketExtension, but not in CalendarEvent.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
502
    }
503
}
504