Issues (2002)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

code/model/money/EcommercePayment.php (59 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * "Abstract" class for a number of different payment
4
 * types allowing a user to pay for something on a site.
5
 *
6
 *
7
 * This can't be an abstract class because sapphire doesn't
8
 * support abstract DataObject classes.
9
 */
10
class EcommercePayment extends DataObject implements EditableEcommerceObject
11
{
12
    /**
13
     * standard SS Variable.
14
     *
15
     * @var array
16
     */
17
    private static $dependencies = array(
18
        'supportedMethodsProvider' => '%$EcommercePaymentSupportedMethodsProvider',
19
    );
20
21
    /**
22
     * automatically populated by the dependency manager.
23
     *
24
     * @var EcommercePaymentSupportedMethodsProvider
25
     */
26
    public $supportedMethodsProvider = null;
27
28
    /**
29
     * Incomplete (default): Payment created but nothing confirmed as successful
30
     * Success: Payment successful
31
     * Failure: Payment failed during process
32
     * Pending: Payment awaiting receipt/bank transfer etc.
33
     */
34
    private static $db = 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...
35
        'Status' => "Enum('Incomplete,Success,Failure,Pending','Incomplete')",
36
        'Amount' => 'Money',
37
        'Message' => 'Text',
38
        'IP' => 'Varchar(45)', /* for IPv6 you have to make sure you have up to 45 characters */
39
        'ProxyIP' => 'Varchar(45)',
40
        'ExceptionError' => 'Text',
41
        'AlternativeEndPoint' => 'Varchar(255)'
42
    );
43
44
    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...
45
        'PaidBy' => 'Member',
46
        'Order' => 'Order',
47
    );
48
49
    private static $summary_fields = 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...
50
        'Created' => 'Created',
51
        'Order.Title' => "Order",
52
        'Title' => 'Type',
53
        'AmountCurrency' => 'Amount',
54
        'Amount.Nice' => 'Amount',
55
        'Status' => 'Status',
56
    );
57
58
    private static $casting = 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...
59
        'Title' => 'Varchar',
60
        'AmountValue' => 'Currency',
61
        'AmountCurrency' => 'Varchar',
62
    );
63
64
    private static $searchable_fields = 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...
65
        'OrderID' => array(
66
            'field' => 'NumericField',
67
            'title' => 'Order Number',
68
        ),
69
        'Created' => array(
70
            'title' => 'Date (e.g. today)',
71
            'field' => 'TextField',
72
            'filter' => 'EcommercePaymentFilters_AroundDateFilter',
73
        ),
74
        'IP' => array(
75
            'title' => 'IP Address',
76
            'filter' => 'PartialMatchFilter',
77
        ),
78
        'Status',
79
    );
80
81
    /**
82
     * standard SS variable.
83
     *
84
     * @Var String
85
     */
86
    private static $singular_name = 'Shop Payment';
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...
87
    public function i18n_singular_name()
0 ignored issues
show
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...
88
    {
89
        return $this->Config()->get('singular_name');
90
    }
91
92
    /**
93
     * standard SS variable.
94
     *
95
     * @Var String
96
     */
97
    private static $plural_name = 'Shop Payments';
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...
98
    public function i18n_plural_name()
0 ignored issues
show
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...
99
    {
100
        return $this->Config()->get('plural_name');
101
    }
102
103
104
    /**
105
     * CRUCIAL
106
     * makes sure all the relevant payment methods are available ...
107
     *
108
     * @return this | EcommercePayment
0 ignored issues
show
Should the return type not be EcommercePayment?

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...
109
     */
110
    public function init()
111
    {
112
        self::get_supported_methods($this->Order());
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<EcommercePayment>? 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...
113
        return $this;
114
    }
115
116
    private static $indexes = 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...
117
        'Status' => true,
118
        'LastEdited' => true
119
    );
120
121
    /**
122
     * standard SS variable.
123
     *
124
     * @var string
125
     */
126
    private static $default_sort = [
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...
127
        'LastEdited' => 'DESC',
128
        'ID' => 'DESC'
129
    ];
130
131
    public function getCMSFields()
132
    {
133
        $fields = parent::getCMSFields();
134
        $fields->replaceField('OrderID', new ReadonlyField('OrderID', 'Order ID'));
135
        $fields->replaceField('PaidByID', new ReadonlyField('PaidByID', 'Payment made by'));
136
        $fields->removeByName('AlternativeEndPoint');
137
138
        return $fields;
139
    }
140
141
    /**
142
     * link to edit the record.
143
     *
144
     * @param string | Null $action - e.g. edit
145
     *
146
     * @return string
0 ignored issues
show
Should the return type not be null|string?

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...
147
     */
148
    public function CMSEditLink($action = null)
149
    {
150
        return CMSEditLinkAPI::find_edit_link_for_object($this, $action);
151
    }
152
153
    /**
154
     * Standard SS method.
155
     *
156
     * @param Member $member
0 ignored issues
show
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
157
     *
158
     * @return bool
0 ignored issues
show
Should the return type not be boolean|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...
159
     */
160
    public function canCreate($member = null)
161
    {
162
        if (! $member) {
163
            $member = Member::currentUser();
164
        }
165
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
$member is of type object<DataObject>|null, but the function expects a object<Member>|integer.

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...
166
        if ($extended !== null) {
167
            return $extended;
168
        }
169
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
170
            return true;
171
        }
172
173
        return parent::canCreate($member);
0 ignored issues
show
It seems like $member defined by \Member::currentUser() on line 163 can also be of type object<DataObject>; however, DataObject::canCreate() does only seem to accept object<Member>|null, 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...
174
    }
175
176
    public function canView($member = null)
0 ignored issues
show
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...
177
    {
178
        if (! $member) {
179
            $member = Member::currentUser();
180
        }
181
        $extended = $this->extendedCan(__FUNCTION__, $member);
182
        if ($extended !== null) {
183
            return $extended;
184
        }
185
        $order = $this->Order();
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<EcommercePayment>? 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...
186
        if ($order && $order->exists()) {
187
            return $order->canView();
188
        }
189
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
190
            return true;
191
        }
192
193
        return parent::canCreate($member);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (canCreate() instead of canView()). Are you sure this is correct? If so, you might want to change this to $this->canCreate().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
194
    }
195
196
    /**
197
     * Standard SS method.
198
     *
199
     * @param Member $member
0 ignored issues
show
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
200
     *
201
     * @return bool
0 ignored issues
show
Should the return type not be boolean|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...
202
     */
203
    public function canEdit($member = null)
204
    {
205
        if (! $member) {
206
            $member = Member::currentUser();
207
        }
208
        if ($this->Status == 'Pending' || $this->Status == 'Incomplete') {
0 ignored issues
show
The property Status does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
209
            $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
$member is of type object<DataObject>|null, but the function expects a object<Member>|integer.

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...
210
            if ($extended !== null) {
211
                return $extended;
212
            }
213
            if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
214
                return true;
215
            }
216
217
            return parent::canEdit($member);
0 ignored issues
show
It seems like $member defined by \Member::currentUser() on line 206 can also be of type object<DataObject>; however, DataObject::canEdit() does only seem to accept object<Member>|null, 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...
218
        }
219
220
        return false;
221
    }
222
223
    /**
224
     * Standard SS method
225
     * set to false as a security measure...
226
     *
227
     * @param Member $member
0 ignored issues
show
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
228
     *
229
     * @return bool
230
     */
231
    public function canDelete($member = null)
232
    {
233
        return false;
234
    }
235
236
    /**
237
     * redirects to this link after order has been placed ....
238
     * @param  string $link
239
     */
240
    public function addAlternativeEndPoint($link, $write = true)
241
    {
242
        $this->AlternativeEndPoint = $link;
0 ignored issues
show
The property AlternativeEndPoint does not exist on object<EcommercePayment>. 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...
243
        if ($write) {
244
            $this->write();
245
        }
246
    }
247
248
    /**
249
     * redirect to order action.
250
     */
251
    public function redirectToOrder()
252
    {
253
        if ($this->AlternativeEndPoint) {
0 ignored issues
show
The property AlternativeEndPoint does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
254
            return Controller::curr()->redirect(Director::absoluteBaseURL().$this->AlternativeEndPoint);
0 ignored issues
show
The property AlternativeEndPoint does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
255
        }
256
        $order = $this->Order();
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<EcommercePayment>? 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...
257
        if ($order) {
258
            return Controller::curr()->redirect($order->Link());
259
        } else {
260
            user_error('No order found with this payment: '.$this->ID, E_USER_NOTICE);
261
        }
262
    }
263
264
    /**
265
     * alias
266
     * @return string
267
     */
268
    public function Title()
269
    {
270
        return $this->getTitle();
271
    }
272
273
    /**
274
     * @return string
275
     */
276
    public function getTitle()
277
    {
278
        return $this->i18n_singular_name();
279
    }
280
281
    /**
282
     * alias for getAmountValue
283
     * @return float
284
     */
285
    public function AmountValue()
286
    {
287
        return $this->getAmountValue();
288
    }
289
290
    /**
291
     * @return float
292
     */
293
    public function getAmountValue()
294
    {
295
        return $this->Amount->getAmount();
0 ignored issues
show
The property Amount does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
296
    }
297
298
    /**
299
     * alias for getAmountCurrency
300
     * @return string
301
     */
302
    public function AmountCurrency()
303
    {
304
        return $this->getAmountCurrency();
305
    }
306
307
    /**
308
     * @return string
309
     */
310
    public function getAmountCurrency()
311
    {
312
        return $this->Amount->getCurrency();
0 ignored issues
show
The property Amount does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
313
    }
314
315
    /**
316
     * standard SS method
317
     * try to finalise order if payment has been made.
318
     */
319
    public function onBeforeWrite()
320
    {
321
        parent::onBeforeWrite();
322
        $this->PaidByID = Member::currentUserID();
0 ignored issues
show
The property PaidByID does not exist on object<EcommercePayment>. 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...
323
    }
324
325
    /**
326
     * standard SS method
327
     * try to finalise order if payment has been made.
328
     */
329
    public function onAfterWrite()
330
    {
331
        parent::onAfterWrite();
332
        $order = $this->Order();
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<EcommercePayment>? 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...
333
        if ($order && is_a($order, Object::getCustomClass('Order')) && $order->IsSubmitted()) {
334
            $order->tryToFinaliseOrder();
335
        }
336
    }
337
338
    /**
339
     *@return string
340
     **/
341
    public function Status()
342
    {
343
        return _t('Payment.'.strtoupper($this->Status), $this->Status);
0 ignored issues
show
The property Status does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
344
    }
345
346
    /**
347
     * Return the site currency in use.
348
     *
349
     * @return string
0 ignored issues
show
Should the return type not be array|integer|double|string|boolean?

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...
350
     */
351
    public static function site_currency()
352
    {
353
        $currency = EcommerceConfig::get('EcommerceCurrency', 'default_currency');
354
        if (!$currency) {
355
            user_error('It is highly recommended that you set a default currency using the config files (EcommerceCurrency.default_currency)', E_USER_NOTICE);
356
        }
357
358
        return $currency;
359
    }
360
361
    /**
362
     * Set currency to default one.
363
     * Set IP address.
364
     */
365
    public function populateDefaults()
366
    {
367
        parent::populateDefaults();
368
        $this->Amount->Currency = EcommerceConfig::get('EcommerceCurrency', 'default_currency');
0 ignored issues
show
The property Amount does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
369
        $this->setClientIP();
370
    }
371
372
    /**
373
     * Set the IP address of the user to this payment record.
374
     * This isn't perfect - IP addresses can be hidden fairly easily.
375
     */
376
    protected function setClientIP()
377
    {
378
        $proxy = null;
379
        $ip = null;
380
        if (Controller::has_curr()) {
381
            $ip = Controller::curr()->getRequest()->getIP();
382
        }
383
384
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
385
            //swapsies
386
            $proxy = $ip;
387
        }
388
389
        // Only set the IP and ProxyIP if none currently set
390
        if (! $this->IP) {
0 ignored issues
show
The property IP does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
391
            $this->IP = $ip;
0 ignored issues
show
The property IP does not exist on object<EcommercePayment>. 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...
392
        }
393
        if (! $this->ProxyIP) {
0 ignored issues
show
The property ProxyIP does not exist on object<EcommercePayment>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read 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.");
        }
    }

}

If the property has read access only, you can use the @property-read 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...
394
            $this->ProxyIP = $proxy;
0 ignored issues
show
The property ProxyIP does not exist on object<EcommercePayment>. 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...
395
        }
396
    }
397
398
    /**
399
     * Returns the Payment type currently in use.
400
     *
401
     * @return string | null
402
     */
403
    public function PaymentMethod()
404
    {
405
        $supportedMethods = self::get_supported_methods($this->Order());
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<EcommercePayment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
406
        if (isset($supportedMethods[$this->ClassName])) {
407
            return $supportedMethods[$this->ClassName];
408
        }
409
    }
410
411
    /**
412
     * Static method to quickly update the payment method on runtime
413
     * associative array that goes like ClassName => Description ...
414
     *
415
     * e.g. MyPaymentClass => Best Payment Method Ever	 * @param array $array -
416
     * @param array $array
417
     */
418
    public static function set_supported_methods($array)
419
    {
420
        Config::inst()->update('EcommercePayment', 'supported_methods', null);
421
        Config::inst()->update('EcommercePayment', 'supported_methods', $array);
422
    }
423
424
    /**
425
     * returns the list of supported methods
426
     * test methods are included if the site is in DEV mode OR
427
     * the current user is a ShopAdmin.
428
     *
429
     * @return array
0 ignored issues
show
Should the return type not be array|integer|double|string|boolean?

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...
430
     *     [Code] => "Description",
431
     *     [Code] => "Description",
432
     *     [Code] => "Description"
433
     */
434
    public static function get_supported_methods($order = null)
435
    {
436
        $obj = self::create();
437
        return $obj->supportedMethodsProvider->SupportedMethods($order);
438
    }
439
440
    /**
441
     * Return the form requirements for all the payment methods.
442
     *
443
     * @param null | Array
444
     *
445
     * @return An array suitable for passing to CustomRequiredFields
0 ignored issues
show
Should the return type not be An|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...
446
     */
447
    public static function combined_form_requirements($order = null)
0 ignored issues
show
The parameter $order is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
448
    {
449
        return;
450
    }
451
452
    /**
453
     * Return a set of payment fields from all enabled
454
     * payment methods for this site, given the .
455
     * is used to define which methods are available.
456
     *
457
     * @param string       $amount formatted amount (e.g. 12.30) without the currency
458
     * @param null | Order $order
459
     *
460
     * @return FieldList
461
     */
462
    public static function combined_form_fields($amount, $order = null)
463
    {
464
        // Create the initial form fields, which defines an OptionsetField
465
        // allowing the user to choose which payment method to use.
466
        $supportedMethods = self::get_supported_methods($order);
467
        $fields = new FieldList(
468
            new OptionsetField(
469
                'PaymentMethod',
470
                '',
471
                $supportedMethods
472
            )
473
        );
474
        foreach ($supportedMethods as $methodClass => $methodName) {
0 ignored issues
show
The expression $supportedMethods of type array|integer|double|string|boolean 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...
475
            // Create a new CompositeField with method specific fields,
476
            // as defined on each payment method class using getPaymentFormFields()
477
            $methodFields = new CompositeField($methodClass::create()->getPaymentFormFields());
478
            $methodFields->addExtraClass("methodFields_$methodClass");
479
            $methodFields->addExtraClass('paymentfields');
480
            // Add those fields to the initial FieldSet we first created
481
            $fields->push($methodFields);
482
        }
483
484
        // Add the amount and subtotal fields for the payment amount
485
        $fields->push(new HeaderField('Amount', _t('Payment.AMOUNT_COLON', 'Amount to be charged: ').'<u class="totalAmountToBeCharged">'.$amount.'</u>', 4));
486
487
        return $fields;
488
    }
489
490
    /**
491
     * Return the payment form fields that should
492
     * be shown on the checkout order form for the
493
     * payment type. Example: for {@link DPSPayment},
494
     * this would be a set of fields to enter your
495
     * credit card details.
496
     *
497
     * @return FieldList
0 ignored issues
show
Should the return type not be FieldList|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...
498
     */
499
    public function getPaymentFormFields()
500
    {
501
        user_error("Please implement getPaymentFormFields() on $this->class", E_USER_ERROR);
502
    }
503
504
    /**
505
     * Define what fields defined in {@link Order->getPaymentFormFields()}
506
     * should be required.
507
     *
508
     * @see DPSPayment->getPaymentFormRequirements() for an example on how
509
     * this is implemented.
510
     *
511
     * @return array
0 ignored issues
show
Should the return type not be array|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...
512
     */
513
    public function getPaymentFormRequirements()
514
    {
515
        user_error("Please implement getPaymentFormRequirements() on $this->class", E_USER_ERROR);
516
    }
517
518
    /**
519
     * Checks if all the data for payment is correct (e.g. credit card)
520
     * By default it returns true, because lots of payments gatewawys
521
     * do not have any fields required here.
522
     *
523
     * @param array     $data The form request data - see OrderForm
524
     * @param OrderForm $form The form object submitted on
525
     */
526
    public function validatePayment($data, $form)
0 ignored issues
show
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
527
    {
528
        return true;
529
    }
530
531
    /**
532
     * Perform payment processing for the type of
533
     * payment. For example, if this was a credit card
534
     * payment type, you would perform the data send
535
     * off to the payment gateway on this function for
536
     * your payment subclass.
537
     *
538
     * This is used by {@link OrderForm} when it is
539
     * submitted.
540
     *
541
     * @param array     $data The form request data - see OrderForm
542
     * @param OrderForm $form The form object submitted on
543
     *
544
     * @return EcommercePayment_Result
0 ignored issues
show
Should the return type not be EcommercePayment_Result|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...
545
     */
546
    public function processPayment($data, $form)
0 ignored issues
show
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
547
    {
548
        user_error("Please implement processPayment() on $this->class", E_USER_ERROR);
549
    }
550
551
    protected function handleError($e)
552
    {
553
        $this->ExceptionError = $e->getMessage();
0 ignored issues
show
The property ExceptionError does not exist on object<EcommercePayment>. 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...
554
        $this->write();
555
    }
556
557
    public function PaidObject()
0 ignored issues
show
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...
558
    {
559
        return $this->Order();
0 ignored issues
show
Documentation Bug introduced by
The method Order does not exist on object<EcommercePayment>? 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...
560
    }
561
562
    /**
563
     * Debug helper method.
564
     * Access through : /shoppingcart/debug/.
565
     */
566
    public function debug()
567
    {
568
        $html = EcommerceTaskDebugCart::debug_object($this);
569
570
        return $html;
571
    }
572
573
    /**
574
     * LEGACY METHOD
575
     * Process payment form and return next step in the payment process.
576
     * Steps taken are:
577
     * 1. create new payment
578
     * 2. save form into payment
579
     * 3. return payment result.
580
     *
581
     * @param Order $order - the order that is being paid
582
     * @param Form  $form  - the form that is being submitted
583
     * @param array $data  - Array of data that is submittted
584
     *
585
     * @return bool - if successful, this method will return TRUE
586
     */
587
    public static function process_payment_form_and_return_next_step($order, $data, $form)
588
    {
589
        $formHelper = $this->ecommercePaymentFormSetupAndValidationObject();
0 ignored issues
show
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
590
591
        return $formHelper->processPaymentFormAndReturnNextStep($order, $data, $form);
592
    }
593
594
    /**
595
     * LEGACY METHOD.
596
     *
597
     * @param Order $order - the order that is being paid
598
     * @param array $data  - Array of data that is submittted
599
     * @param Form  $form  - the form that is being submitted
600
     *
601
     * @return bool - true if the data is valid
602
     */
603
    public static function validate_payment($order, $data, $form)
604
    {
605
        $formHelper = $this->ecommercePaymentFormSetupAndValidationObject();
0 ignored issues
show
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
606
607
        return $formHelper->validatePayment($order, $data, $form);
608
    }
609
610
    private $ecommercePaymentFormSetupAndValidationObject = null;
611
612
    /**
613
     * @return EcommercePaymentFormSetupAndValidation
614
     */
615
    protected function ecommercePaymentFormSetupAndValidationObject()
616
    {
617
        if (!$this->ecommercePaymentFormSetupAndValidationObject) {
618
            $this->ecommercePaymentFormSetupAndValidationObject = Injector::inst()->create('EcommercePaymentFormSetupAndValidation');
619
        }
620
621
        return $this->ecommercePaymentFormSetupAndValidationObject;
622
    }
623
624
    /**
625
     * @return EcommercePaymentFormSetupAndValidation
626
     */
627
    public static function ecommerce_payment_form_setup_and_validation_object()
628
    {
629
        return Injector::inst()->create('EcommercePaymentFormSetupAndValidation');
630
    }
631
}
632