Completed
Pull Request — master (#830)
by Jeroen De
65:14
created

testGivenNewTransactionIdForBookedDonation_childTransac()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 31
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 22
nc 1
nop 0
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace WMDE\Fundraising\Frontend\DonationContext\Tests\Integration\UseCases\HandlePayPalPaymentNotification;
6
7
use WMDE\Fundraising\Frontend\DonationContext\DataAccess\DoctrineDonationRepository;
8
use WMDE\Fundraising\Frontend\DonationContext\Domain\Model\Donation;
9
use WMDE\Fundraising\Frontend\DonationContext\Domain\Model\DonorName;
10
use WMDE\Fundraising\Frontend\DonationContext\Infrastructure\DonationConfirmationMailer;
11
use WMDE\Fundraising\Frontend\DonationContext\Infrastructure\DonationEventLogger;
12
use WMDE\Fundraising\Frontend\DonationContext\Tests\Data\ValidPayPalNotificationRequest;
13
use WMDE\Fundraising\Frontend\DonationContext\Tests\Fixtures\DonationEventLoggerSpy;
14
use WMDE\Fundraising\Frontend\DonationContext\Tests\Fixtures\DonationRepositorySpy;
15
use WMDE\Fundraising\Frontend\DonationContext\Tests\Fixtures\FailingDonationAuthorizer;
16
use WMDE\Fundraising\Frontend\DonationContext\Tests\Fixtures\FakeDonationRepository;
17
use WMDE\Fundraising\Frontend\DonationContext\Tests\Fixtures\SucceedingDonationAuthorizer;
18
use WMDE\Fundraising\Frontend\DonationContext\UseCases\HandlePayPalPaymentNotification\HandlePayPalPaymentNotificationUseCase;
19
use WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\PayPalData;
20
use WMDE\Fundraising\Frontend\DonationContext\Tests\Data\ValidDonation;
21
use WMDE\Fundraising\Frontend\Tests\Fixtures\ThrowingEntityManager;
22
23
/**
24
 * @covers WMDE\Fundraising\Frontend\DonationContext\UseCases\HandlePayPalPaymentNotification\HandlePayPalPaymentNotificationUseCase
25
 *
26
 * @licence GNU GPL v2+
27
 * @author Kai Nissen < [email protected] >
28
 * @author Gabriel Birke < [email protected] >
29
 */
30
class HandlePayPalPaymentNotificationUseCaseTest extends \PHPUnit\Framework\TestCase {
31
32
	public function testWhenRepositoryThrowsException_errorResponseIsReturned() {
33
		$useCase = new HandlePayPalPaymentNotificationUseCase(
34
			new DoctrineDonationRepository( ThrowingEntityManager::newInstance( $this ) ),
35
			new FailingDonationAuthorizer(),
36
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
37
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
38
		);
39
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
40
		$reponse = $useCase->handleNotification( $request );
41
		$this->assertFalse( $reponse->notificationWasHandled() );
42
		$this->assertTrue( $reponse->hasErrors() );
43
	}
44
45
	public function testWhenAuthorizationFails_unhandledResponseIsReturned() {
46
		$fakeRepository = new FakeDonationRepository();
47
		$fakeRepository->storeDonation( ValidDonation::newIncompletePayPalDonation() );
48
49
		$useCase = new HandlePayPalPaymentNotificationUseCase(
50
			$fakeRepository,
51
			new FailingDonationAuthorizer(),
52
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
53
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
54
		);
55
56
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
57
		$this->assertFalse( $useCase->handleNotification( $request )->notificationWasHandled() );
58
	}
59
60
	public function testWhenAuthorizationSucceeds_successResponseIsReturned() {
61
		$fakeRepository = new FakeDonationRepository();
62
		$fakeRepository->storeDonation( ValidDonation::newIncompletePayPalDonation() );
63
64
		$useCase = new HandlePayPalPaymentNotificationUseCase(
65
			$fakeRepository,
66
			new SucceedingDonationAuthorizer(),
67
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
68
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
69
		);
70
71
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
72
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
73
	}
74
75
	public function testWhenPaymentTypeIsNonPayPal_unhandledResponseIsReturned() {
76
		$fakeRepository = new FakeDonationRepository();
77
		$fakeRepository->storeDonation( ValidDonation::newDirectDebitDonation() );
78
79
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
80
		$useCase = new HandlePayPalPaymentNotificationUseCase(
81
			$fakeRepository,
82
			new SucceedingDonationAuthorizer(),
83
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
84
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
85
		);
86
87
		$this->assertFalse( $useCase->handleNotification( $request )->notificationWasHandled() );
88
	}
89
90
	public function testWhenPaymentStatusIsPending_unhandledResponseIsReturned() {
91
		$request = ValidPayPalNotificationRequest::newPendingPayment();
92
93
		$useCase = new HandlePayPalPaymentNotificationUseCase(
94
			new FakeDonationRepository(),
95
			new SucceedingDonationAuthorizer(),
96
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
97
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
98
		);
99
100
		$this->assertFalse( $useCase->handleNotification( $request )->notificationWasHandled() );
101
	}
102
103
	public function testWhenTransactionTypeIsForSubscriptionChanges_unhandledResponseIsReturned() {
104
		$request = ValidPayPalNotificationRequest::newSubscriptionModification();
105
106
		$useCase = new HandlePayPalPaymentNotificationUseCase(
107
			new FakeDonationRepository(),
108
			new SucceedingDonationAuthorizer(),
109
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
110
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
111
		);
112
		$this->assertFalse( $useCase->handleNotification( $request )->notificationWasHandled() );
113
	}
114
115
	public function testWhenAuthorizationSucceeds_confirmationMailIsSent() {
116
		$donation = ValidDonation::newIncompletePayPalDonation();
117
		$fakeRepository = new FakeDonationRepository();
118
		$fakeRepository->storeDonation( $donation );
119
120
		$mailer = $this->getMailer();
121
		$mailer->expects( $this->once() )
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in WMDE\Fundraising\Fronten...ationConfirmationMailer.

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...
122
			->method( 'sendConfirmationMailFor' );
123
			// TODO: assert that the correct values are passed to the mailer
124
125
		$useCase = new HandlePayPalPaymentNotificationUseCase(
126
			$fakeRepository,
127
			new SucceedingDonationAuthorizer(),
128
			$mailer,
0 ignored issues
show
Bug introduced by
It seems like $mailer defined by $this->getMailer() on line 120 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
129
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
130
		);
131
132
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
133
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
134
	}
135
136
	public function testWhenAuthorizationSucceedsForAnonymousDonation_confirmationMailIsNotSent() {
137
		$donation = ValidDonation::newIncompleteAnonymousPayPalDonation();
138
		$fakeRepository = new FakeDonationRepository();
139
		$fakeRepository->storeDonation( $donation );
140
141
		$mailer = $this->getMailer();
142
		$mailer->expects( $this->never() )
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in WMDE\Fundraising\Fronten...ationConfirmationMailer.

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...
143
			->method( 'sendConfirmationMailFor' );
144
145
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
146
		$useCase = new HandlePayPalPaymentNotificationUseCase(
147
			$fakeRepository,
148
			new SucceedingDonationAuthorizer(),
149
			$mailer,
0 ignored issues
show
Bug introduced by
It seems like $mailer defined by $this->getMailer() on line 141 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
150
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
151
		);
152
153
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
154
	}
155
156
	public function testWhenAuthorizationSucceeds_donationIsStored() {
157
		$donation = ValidDonation::newIncompletePayPalDonation();
158
		$repositorySpy = new DonationRepositorySpy( $donation );
159
160
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
161
		$useCase = new HandlePayPalPaymentNotificationUseCase(
162
			$repositorySpy,
163
			new SucceedingDonationAuthorizer(),
164
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
165
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
166
		);
167
168
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
169
		$this->assertCount( 1, $repositorySpy->getStoreDonationCalls() );
170
	}
171
172
	public function testWhenAuthorizationSucceeds_donationIsBooked() {
173
		$donation = ValidDonation::newIncompletePayPalDonation();
174
		$repository = new FakeDonationRepository( $donation );
175
176
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
177
		$useCase = new HandlePayPalPaymentNotificationUseCase(
178
			$repository,
179
			new SucceedingDonationAuthorizer(),
180
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
181
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
182
		);
183
184
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
185
		$this->assertTrue( $repository->getDonationById( $donation->getId() )->isBooked() );
186
	}
187
188
	public function testWhenAuthorizationSucceeds_bookingEventIsLogged() {
189
		$donation = ValidDonation::newIncompletePayPalDonation();
190
		$repositorySpy = new DonationRepositorySpy( $donation );
191
192
		$eventLogger = new DonationEventLoggerSpy();
193
194
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
195
		$useCase = new HandlePayPalPaymentNotificationUseCase(
196
			$repositorySpy,
197
			new SucceedingDonationAuthorizer(),
198
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
199
			$eventLogger
200
		);
201
202
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
203
204
		$this->assertEventLogContainsExpression( $eventLogger, $donation->getId(), '/booked/' );
205
	}
206
207
	public function testWhenSendingConfirmationMailFails_handlerReturnsTrue() {
208
		$fakeRepository = new FakeDonationRepository();
209
		$fakeRepository->storeDonation( ValidDonation::newIncompletePayPalDonation() );
210
211
		$mailer = $this->getMailer();
212
		$mailer->expects( $this->once() )
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in WMDE\Fundraising\Fronten...ationConfirmationMailer.

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...
213
			->method( 'sendConfirmationMailFor' )
214
			->willThrowException( new \RuntimeException( 'Oh noes!' ) );
215
216
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
217
		$useCase = new HandlePayPalPaymentNotificationUseCase(
218
			$fakeRepository,
219
			new SucceedingDonationAuthorizer(),
220
			$mailer,
0 ignored issues
show
Bug introduced by
It seems like $mailer defined by $this->getMailer() on line 211 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
221
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
222
		);
223
224
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
225
	}
226
227
	public function testGivenNewTransactionIdForBookedDonation_transactionIdShowsUpInChildPayments() {
228
		$donation = ValidDonation::newBookedPayPalDonation();
229
		$transactionId = '16R12136PU8783961';
230
231
		$fakeRepository = new FakeDonationRepository();
232
		$fakeRepository->storeDonation( $donation );
233
234
		$request = ValidPayPalNotificationRequest::newDuplicatePayment( $donation->getId(), $transactionId );
235
236
		$useCase = new HandlePayPalPaymentNotificationUseCase(
237
			$fakeRepository,
238
			new SucceedingDonationAuthorizer(),
239
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
240
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
241
		);
242
243
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
244
245
		/** @var \WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\PayPalPayment $payment */
246
		$payment = $fakeRepository->getDonationById( $donation->getId() )->getPaymentMethod();
247
248
		$this->assertTrue(
249
			$payment->getPayPalData()->hasChildPayment( $transactionId ),
250
			'Parent payment must have new transaction ID in its list'
251
		);
252
	}
253
254
	public function testGivenNewTransactionIdForBookedDonation_childTransactionWithSameDataIsCreated() {
255
		$donation = ValidDonation::newBookedPayPalDonation();
256
		$transactionId = '16R12136PU8783961';
257
258
		$fakeRepository = new FakeDonationRepository();
259
		$fakeRepository->storeDonation( $donation );
260
261
		$request = ValidPayPalNotificationRequest::newDuplicatePayment( $donation->getId(), $transactionId );
262
263
		$useCase = new HandlePayPalPaymentNotificationUseCase(
264
			$fakeRepository,
265
			new SucceedingDonationAuthorizer(),
266
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
267
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
268
		);
269
270
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
271
272
		$donation = $fakeRepository->getDonationById( $donation->getId() );
273
		/** @var \WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\PayPalPayment $payment */
274
		$payment = $donation->getPaymentMethod();
275
		$childDonation = $fakeRepository->getDonationById( $payment->getPayPalData()->getChildPaymentEntityId( $transactionId ) );
276
		$this->assertNotNull( $childDonation );
277
		/** @var \WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\PayPalPayment $childDonationPaymentMethod */
278
		$childDonationPaymentMethod = $childDonation->getPaymentMethod();
279
		$this->assertEquals( $transactionId, $childDonationPaymentMethod->getPayPalData()->getPaymentId() );
280
		$this->assertEquals( $donation->getAmount(), $childDonation->getAmount() );
281
		$this->assertEquals( $donation->getDonor(), $childDonation->getDonor() );
282
		$this->assertEquals( $donation->getPaymentIntervalInMonths(), $childDonation->getPaymentIntervalInMonths() );
283
		$this->assertTrue( $childDonation->isBooked() );
284
	}
285
286
	public function testGivenNewTransactionIdForBookedDonation_childCreationeventIsLogged() {
287
		$donation = ValidDonation::newBookedPayPalDonation();
288
		$transactionId = '16R12136PU8783961';
289
290
		$fakeRepository = new FakeDonationRepository();
291
		$fakeRepository->storeDonation( $donation );
292
293
		$request = ValidPayPalNotificationRequest::newDuplicatePayment( $donation->getId(), $transactionId );
294
295
		$eventLogger = new DonationEventLoggerSpy();
296
297
		$useCase = new HandlePayPalPaymentNotificationUseCase(
298
			$fakeRepository,
299
			new SucceedingDonationAuthorizer(),
300
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
301
			$eventLogger
302
		);
303
304
		$this->assertTrue( $useCase->handleNotification( $request )->notificationWasHandled() );
305
306
		$donation = $fakeRepository->getDonationById( $donation->getId() );
307
		/** @var \WMDE\Fundraising\Frontend\PaymentContext\Domain\Model\PayPalPayment $payment */
308
		$payment = $donation->getPaymentMethod();
309
		$childDonationId = $payment->getPayPalData()->getChildPaymentEntityId( $transactionId );
310
311
		$this->assertEventLogContainsExpression( $eventLogger, $donation->getId(), '/child donation.*' . $childDonationId .'/' );
312
		$this->assertEventLogContainsExpression( $eventLogger, $childDonationId, '/parent donation.*' . $donation->getId() .'/' );
313
	}
314
315
	public function testGivenExistingTransactionIdForBookedDonation_handlerReturnsFalse() {
316
		$fakeRepository = new FakeDonationRepository();
317
		$fakeRepository->storeDonation( ValidDonation::newBookedPayPalDonation() );
318
319
		$request = ValidPayPalNotificationRequest::newInstantPayment( 1 );
320
321
		$useCase = new HandlePayPalPaymentNotificationUseCase(
322
			$fakeRepository,
323
			new SucceedingDonationAuthorizer(),
324
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
325
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
326
		);
327
328
		$this->assertFalse( $useCase->handleNotification( $request )->notificationWasHandled() );
329
	}
330
331
	public function testGivenTransactionIdInBookedChildDonation_noNewDonationIsCreated() {
332
		$transactionId = '16R12136PU8783961';
333
		$fakeChildEntityId = 2;
334
		$donation = ValidDonation::newBookedPayPalDonation();
335
		$donation->getPaymentMethod()->getPaypalData()->addChildPayment( $transactionId, $fakeChildEntityId );
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface WMDE\Fundraising\Fronten...ain\Model\PaymentMethod as the method getPaypalData() does only exist in the following implementations of said interface: WMDE\Fundraising\Fronten...ain\Model\PayPalPayment.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
336
337
		$fakeRepository = new FakeDonationRepository();
338
		$fakeRepository->storeDonation( $donation );
339
340
		$request = ValidPayPalNotificationRequest::newDuplicatePayment( $donation->getId(), $transactionId );
341
342
		$useCase = new HandlePayPalPaymentNotificationUseCase(
343
			$fakeRepository,
344
			new SucceedingDonationAuthorizer(),
345
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
346
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
347
		);
348
349
		$this->assertFalse( $useCase->handleNotification( $request )->notificationWasHandled() );
350
	}
351
352
	public function testWhenNotificationIsForNonExistingDonation_newDonationIsCreated() {
353
		$repositorySpy = new DonationRepositorySpy();
354
355
		$request = ValidPayPalNotificationRequest::newInstantPayment( 12345 );
356
		$useCase = new HandlePayPalPaymentNotificationUseCase(
357
			$repositorySpy,
358
			new SucceedingDonationAuthorizer(),
359
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
360
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
361
		);
362
363
		$useCase->handleNotification( $request );
364
365
		$storeDonationCalls = $repositorySpy->getStoreDonationCalls();
366
		$this->assertCount( 1, $storeDonationCalls, 'Donation is stored' );
367
		$this->assertNull( $storeDonationCalls[0]->getId(), 'ID is not taken from request' );
368
		$this->assertDonationIsCreatedWithNotficationRequestData( $storeDonationCalls[0] );
369
	}
370
371
	public function testGivenRecurringPaymentForIncompleteDonation_donationIsBooked() {
372
		$donation = ValidDonation::newIncompletePayPalDonation();
373
		$repositorySpy = new DonationRepositorySpy( $donation );
374
375
		$request = ValidPayPalNotificationRequest::newRecurringPayment( $donation->getId() );
376
377
		$useCase = new HandlePayPalPaymentNotificationUseCase(
378
			$repositorySpy,
379
			new SucceedingDonationAuthorizer(),
380
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
381
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
382
		);
383
384
		$useCase->handleNotification( $request );
385
		$donation = $repositorySpy->getDonationById( $donation->getId() );
386
387
		$this->assertCount( 1, $repositorySpy->getStoreDonationCalls() );
388
		$this->assertEquals( $donation, $repositorySpy->getStoreDonationCalls()[0] );
389
		$this->assertTrue( $donation->isBooked() );
390
	}
391
392
	public function testWhenNotificationIsForNonExistingDonation_confirmationMailIsSent() {
393
		$request = ValidPayPalNotificationRequest::newInstantPayment( 12345 );
394
		$mailer = $this->getMailer();
395
		$mailer->expects( $this->once() )
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in WMDE\Fundraising\Fronten...ationConfirmationMailer.

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...
396
			->method( 'sendConfirmationMailFor' )
397
			->with( $this->anything() );
398
		$useCase = new HandlePayPalPaymentNotificationUseCase(
399
			new FakeDonationRepository(),
400
			new SucceedingDonationAuthorizer(),
401
			$mailer,
0 ignored issues
show
Bug introduced by
It seems like $mailer defined by $this->getMailer() on line 394 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
402
			$this->getEventLogger()
0 ignored issues
show
Bug introduced by
It seems like $this->getEventLogger() targeting WMDE\Fundraising\Fronten...eTest::getEventLogger() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...re\DonationEventLogger>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
403
		);
404
405
		$useCase->handleNotification( $request );
406
	}
407
408
	public function testWhenNotificationIsForNonExistingDonation_bookingEventIsLogged() {
409
		$request = ValidPayPalNotificationRequest::newInstantPayment( 12345 );
410
		$eventLogger = new DonationEventLoggerSpy();
411
412
		$useCase = new HandlePayPalPaymentNotificationUseCase(
413
			new FakeDonationRepository(),
414
			new SucceedingDonationAuthorizer(),
415
			$this->getMailer(),
0 ignored issues
show
Bug introduced by
It seems like $this->getMailer() targeting WMDE\Fundraising\Fronten...seCaseTest::getMailer() can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, WMDE\Fundraising\Fronten...nUseCase::__construct() does only seem to accept object<WMDE\Fundraising\...tionConfirmationMailer>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
416
			$eventLogger
417
		);
418
419
		$useCase->handleNotification( $request );
420
421
		$this->assertEventLogContainsExpression( $eventLogger, 1, '/booked/' ); // 1 is the generated donation id
422
	}
423
424
	private function assertDonationIsCreatedWithNotficationRequestData( Donation $donation ) {
425
		$this->assertSame( 0, $donation->getPaymentIntervalInMonths(), 'Payment interval is always empty' );
426
		$this->assertTrue( $donation->isBooked() );
427
428
		$donorName = $donation->getDonor()->getName();
429
		$this->assertSame( DonorName::PERSON_PRIVATE, $donorName->getPersonType(), 'Person is always private' );
430
		$this->assertSame( ValidPayPalNotificationRequest::PAYER_ADDRESS_NAME, $donorName->getFullName() );
431
432
		$this->assertSame( ValidPayPalNotificationRequest::PAYER_EMAIL, $donation->getDonor()->getEmailAddress() );
433
434
		$address = $donation->getDonor()->getPhysicalAddress();
435
		$this->assertSame( ValidPayPalNotificationRequest::PAYER_ADDRESS_STREET, $address->getStreetAddress() );
436
		$this->assertSame( ValidPayPalNotificationRequest::PAYER_ADDRESS_CITY, $address->getCity() );
437
		$this->assertSame( ValidPayPalNotificationRequest::PAYER_ADDRESS_POSTAL_CODE, $address->getPostalCode() );
438
		$this->assertSame( ValidPayPalNotificationRequest::PAYER_ADDRESS_COUNTRY_CODE, $address->getCountryCode() );
439
440
		$payment = $donation->getPayment();
441
		$this->assertSame( ValidPayPalNotificationRequest::AMOUNT_GROSS_CENTS, $payment->getAmount()->getEuroCents() );
442
443
		/** @var PayPalData $paypalData */
444
		$paypalData = $payment->getPaymentMethod()->getPaypalData();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface WMDE\Fundraising\Fronten...ain\Model\PaymentMethod as the method getPaypalData() does only exist in the following implementations of said interface: WMDE\Fundraising\Fronten...ain\Model\PayPalPayment.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
445
		$this->assertSame( ValidPayPalNotificationRequest::PAYER_ADDRESS_NAME, $paypalData->getAddressName() );
446
	}
447
448
	private function assertEventLogContainsExpression( DonationEventLoggerSpy $eventLoggerSpy, int $donationId, string $expr ) {
449
		$foundCalls = array_filter( $eventLoggerSpy->getLogCalls(), function( $call ) use ( $donationId, $expr ) {
450
			return $call[0] == $donationId && preg_match( $expr, $call[1] );
451
		} );
452
		$assertMsg = 'Failed to assert that donation event log log contained "' . $expr . '" for donation id '.$donationId;
453
		$this->assertCount( 1, $foundCalls, $assertMsg );
454
	}
455
456
	/**
457
	 * @return DonationConfirmationMailer|\PHPUnit_Framework_MockObject_MockObject
458
	 */
459
	private function getMailer(): DonationConfirmationMailer {
460
		return $this->getMockBuilder( DonationConfirmationMailer::class )->disableOriginalConstructor()->getMock();
461
	}
462
463
	/**
464
	 * @return DonationEventLogger|\PHPUnit_Framework_MockObject_MockObject
465
	 */
466
	private function getEventLogger(): DonationEventLogger {
467
		return $this->createMock( DonationEventLogger::class );
468
	}
469
470
}
471