Completed
Push — master ( 396bd1...fbf0e7 )
by
unknown
03:26
created

code/model/address/EcommerceCountry.php (32 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
/**
4
 * @description: This class helps you to manage countries within the context of e-commerce.
5
 * For example: To what countries can be sold.
6
 * /dev/build/?resetecommercecountries=1 will reset the list of countries...
7
 *
8
 *
9
 * @authors: Nicolaas [at] Sunny Side Up .co.nz
10
 * @package: ecommerce
11
 * @sub-package: address
12
 * @inspiration: Silverstripe Ltd, Jeremy
13
 **/
14
class EcommerceCountry extends DataObject implements EditableEcommerceObject
15
{
16
    /**
17
     * what variables are accessible through  http://mysite.com/api/ecommerce/v1/EcommerceCountry/.
18
     *
19
     * @var array
20
     */
21
    private static $api_access = 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...
22
        'view' => array(
23
            'Code',
24
            'Name',
25
        )
26
     );
27
28
    /**
29
     * Standard SS Variable.
30
     *
31
     * @var array
32
     **/
33
    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...
34
        'Code' => 'Varchar(20)',
35
        'Name' => 'Varchar(200)',
36
        'DoNotAllowSales' => 'Boolean',
37
    );
38
39
    /**
40
     * Standard SS Variable.
41
     *
42
     * @var array
43
     **/
44
    private static $has_many = 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
        'Regions' => 'EcommerceRegion',
46
    );
47
48
    /**
49
     * Standard SS Variable.
50
     *
51
     * @var array
52
     **/
53
    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...
54
        'Code' => true,
55
        'DoNotAllowSales' => true,
56
    );
57
58
    /**
59
     * standard SS variable.
60
     *
61
     * @var array
62
     */
63
    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...
64
        'Code' => 'Code',
65
        'Name' => 'Name',
66
        'AllowSalesNice' => 'Allow Sales',
67
    );
68
69
    /**
70
     * standard SS variable.
71
     *
72
     * @var array
73
     */
74
    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...
75
        'AllowSales' => 'Boolean',
76
        'AllowSalesNice' => 'Varchar',
77
    );
78
79
    /**
80
     * STANDARD SILVERSTRIPE STUFF.
81
     *
82
     * @todo: how to translate this?
83
     **/
84
    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...
85
        'Code' => 'PartialMatchFilter',
86
        'Name' => 'PartialMatchFilter',
87
        'DoNotAllowSales' => array(
88
            'title' => 'Sales are prohibited',
89
        ),
90
    );
91
92
    /**
93
     * Standard SS Variable.
94
     *
95
     * @var string
96
     **/
97
    private static $default_sort = '"DoNotAllowSales" ASC, "Name" ASC';
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
99
    /**
100
     * Standard SS Variable.
101
     *
102
     * @var string
103
     **/
104
    private static $singular_name = 'Country';
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...
105
    public function i18n_singular_name()
106
    {
107
        return _t('EcommerceCountry.COUNTRY', 'Country');
108
    }
109
110
    /**
111
     * Standard SS Variable.
112
     *
113
     * @var string
114
     **/
115
    private static $plural_name = 'Countries';
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...
116
    public function i18n_plural_name()
117
    {
118
        return _t('EcommerceCountry.COUNTRIES', 'Countries');
119
    }
120
121
    /**
122
     * Standard SS variable.
123
     *
124
     * @var string
125
     */
126
    private static $description = 'A country.';
127
128
    /**
129
     * Standard SS Method.
130
     *
131
     * @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...
132
     *
133
     * @var bool
134
     */
135
    public function canCreate($member = null)
136
    {
137
        if (! $member) {
138
            $member = Member::currentUser();
139
        }
140
        $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...
141
        if ($extended !== null) {
142
            return $extended;
143
        }
144
145
        return false;
146
    }
147
148
    /**
149
     * Standard SS Method.
150
     *
151
     * @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...
152
     *
153
     * @var bool
154
     */
155
    public function canView($member = null)
156
    {
157
        if (! $member) {
158
            $member = Member::currentUser();
159
        }
160
        $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...
161
        if ($extended !== null) {
162
            return $extended;
163
        }
164
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
165
            return true;
166
        }
167
168
        return parent::canEdit($member);
0 ignored issues
show
It seems like $member defined by \Member::currentUser() on line 158 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...
Comprehensibility Bug introduced by
It seems like you call parent on a different method (canEdit() instead of canView()). Are you sure this is correct? If so, you might want to change this to $this->canEdit().

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...
169
    }
170
171
    /**
172
     * Standard SS Method.
173
     *
174
     * @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...
175
     *
176
     * @var bool
177
     */
178
    public function canEdit($member = null)
179
    {
180
        if (! $member) {
181
            $member = Member::currentUser();
182
        }
183
        $extended = $this->extendedCan(__FUNCTION__, $member);
184
        if ($extended !== null) {
185
            return $extended;
186
        }
187
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
188
            return true;
189
        }
190
191
        return parent::canEdit($member);
0 ignored issues
show
It seems like $member defined by \Member::currentUser() on line 181 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...
192
    }
193
194
    /**
195
     * Standard SS method.
196
     *
197
     * @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...
198
     *
199
     * @return bool
200
     */
201
    public function canDelete($member = null)
202
    {
203
        if (! $member) {
204
            $member = Member::currentUser();
205
        }
206
        $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...
207
        if ($extended !== null) {
208
            return $extended;
209
        }
210
        return false;
211
        if (ShippingAddress::get()->filter(array('ShippingCountry' => $this->Code))->count()) {
0 ignored issues
show
if (\ShippingAddress::ge...) { return false; } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
The property Code does not exist on object<EcommerceCountry>. 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...
212
            return false;
213
        }
214
        if (BillingAddress::get()->filter(array('Country' => $this->Code))->count()) {
0 ignored issues
show
The property Code does not exist on object<EcommerceCountry>. 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...
215
            return false;
216
        }
217
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
218
            return true;
219
        }
220
221
        return parent::canEdit($member);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (canEdit() instead of canDelete()). Are you sure this is correct? If so, you might want to change this to $this->canEdit().

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...
222
    }
223
224
    /**
225
     * returns the country based on the Visitor Country Provider.
226
     * this is some sort of IP recogniser system (e.g. Geoip Class).
227
     *
228
     * @return string (country code)
229
     **/
230
    public static function get_country_from_ip()
231
    {
232
        $visitorCountryProviderClassName = EcommerceConfig::get('EcommerceCountry', 'visitor_country_provider');
233
        if (!$visitorCountryProviderClassName) {
234
            $visitorCountryProviderClassName = 'EcommerceCountry_VisitorCountryProvider';
235
        }
236
        $visitorCountryProvider = new $visitorCountryProviderClassName();
237
238
        return $visitorCountryProvider->getCountry();
239
    }
240
241
    /**
242
     * returns the country based on the Visitor Country Provider.
243
     * this is some sort of IP recogniser system (e.g. Geoip Class).
244
     *
245
     * @return string (country code)
246
     **/
247
    public static function get_ip()
248
    {
249
        $visitorCountryProviderClassName = EcommerceConfig::get('EcommerceCountry', 'visitor_country_provider');
250
        if (!$visitorCountryProviderClassName) {
251
            $visitorCountryProviderClassName = 'EcommerceCountry_VisitorCountryProvider';
252
        }
253
        $visitorCountryProvider = new $visitorCountryProviderClassName();
254
255
        return $visitorCountryProvider->getIP();
256
    }
257
258
    private static $_countries_from_db_cache = array();
259
260
    /**
261
     *               e.g.
262
     *               "NZ" => "New Zealand"
263
     * @return array
264
     */
265
    public static function get_country_dropdown($showAllCountries = true, $addEmptyString = false, $useIDNotCode = false)
266
    {
267
        $key = ($showAllCountries ? "all" : "notall");
268
        if (isset(self::$_countries_from_db_cache[$key])) {
269
            $array = self::$_countries_from_db_cache[$key];
270
        } else {
271
            $array = array();
272
            $objects = null;
273
            if (class_exists('Geoip') && $showAllCountries && ! $useIDNotCode) {
274
                $array = Geoip::getCountryDropDown();
275
            } elseif ($showAllCountries) {
276
                $objects = EcommerceCountry::get();
277
            } else {
278
                $objects = EcommerceCountry::get()->filter(array('DoNotAllowSales' => 0));
279
            }
280
            if ($objects && $objects->count()) {
281
                if ($useIDNotCode) {
282
                    $idField = 'ID';
283
                } else {
284
                    $idField = 'Code';
285
                }
286
                $array = $objects->map($idField, 'Name')->toArray();
287
            }
288
            self::$_countries_from_db_cache[$key] = $array;
289
        }
290
        if (count($array)) {
291
            if ($addEmptyString) {
292
                $array = array("", " -- please select -- ") + $array;
293
            }
294
        }
295
        return $array;
296
    }
297
298
    /**
299
     * This function exists as a shortcut.
300
     * If there is only ONE allowed country code
301
     * then a lot of checking of countries can be avoided.
302
     *
303
     * @return string | null - countrycode
304
     **/
305
    public static function get_fixed_country_code()
306
    {
307
        $a = EcommerceConfig::get('EcommerceCountry', 'allowed_country_codes');
308
        if (is_array($a) && count($a) == 1) {
309
            return array_shift($a);
310
        }
311
312
        return null;
313
    }
314
315
    /**
316
     * @alias for EcommerceCountry::find_title
317
     *
318
     * @param string $code
319
     *                     We have this as this is the same as Geoip
320
     *
321
     * @return string
322
     */
323
    public static function countryCode2name($code)
324
    {
325
        return self::find_title($code);
326
    }
327
328
    /**
329
     * returns the country name from a code.
330
     *
331
     * @return string
332
     **/
333
    public static function find_title($code)
334
    {
335
        $code = strtoupper($code);
336
        $options = self::get_country_dropdown($showAllCountries = true);
337
        // check if code was provided, and is found in the country array
338
        if (isset($options[$code])) {
339
            return $options[$code];
340
        } elseif ($code) {
341
            $obj = EcommerceCountry::get()->filter(array('Code' => $code))->first();
342
            if ($obj) {
343
                return $obj->Name;
344
            }
345
            return $code;
346
        } else {
347
            return _t('Ecommerce.COUNTRY_NOT_FOUND', '[COUNTRY NOT FOUND]');
348
        }
349
    }
350
351
    /**
352
     * Memory for the order's country.
353
     *
354
     * @var array
355
     */
356
    private static $_country_cache = array();
357
358
    /**
359
     * @param int (optional) $orderID
360
     */
361
    public static function reset_get_country_cache($orderID = 0)
362
    {
363
        $orderID = ShoppingCart::current_order_id($orderID);
364
        unset(self::$_country_cache[$orderID]);
365
    }
366
367
    /**
368
     * @param int (optional) $orderID
369
     */
370
    public static function get_country_cache($orderID = 0)
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...
371
    {
372
        $orderID = ShoppingCart::current_order_id($orderID);
373
374
        return isset(self::$_country_cache[$orderID]) ? self::$_country_cache[$orderID] : null;
375
    }
376
377
    /**
378
     * @param string         $countryCode
379
     * @param int (optional) $orderID
380
     */
381
    public static function set_country_cache($countryCode, $orderID = 0)
382
    {
383
        $orderID = ShoppingCart::current_order_id($orderID);
384
        self::$_country_cache[$orderID] = $countryCode;
385
    }
386
387
    /**
388
     * This function works out the most likely country for the current order.
389
     *
390
     * @param bool (optional) $recalculate
391
     * @param int (optional)  $orderID
392
     *
393
     * @return string - Country Code - e.g. NZ
394
     **/
395
    public static function get_country($recalculate = false, $orderID = 0)
396
    {
397
        $orderID = ShoppingCart::current_order_id($orderID);
398
        $countryCode = self::get_country_cache($orderID);
399
        if ($countryCode === null || $recalculate) {
400
            $countryCode = '';
0 ignored issues
show
$countryCode is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
401
            //1. fixed country is first
402
            $countryCode = self::get_fixed_country_code();
403
            if (!$countryCode) {
404
                //2. check order / shipping address / ip address
405
                //include $countryCode = self::get_country_from_ip();
406
                if ($o = ShoppingCart::current_order()) {
407
                    $countryCode = $o->getCountry();
408
                //3 ... if there is no shopping cart, then we still want it from IP
409
                } else {
410
                    $countryCode = self::get_country_from_ip();
411
                }
412
                //4 check default country set in GEO IP....
413
                if (!$countryCode) {
414
                    $countryCode = self::get_country_default();
415
                }
416
            }
417
            self::set_country_cache($countryCode, $orderID);
418
        }
419
420
        return $countryCode;
421
    }
422
423
    /**
424
     * A bling guess at the best country!
425
     *
426
     * @return string
0 ignored issues
show
Should the return type not be integer|string|array|double|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...
427
     */
428
    public static function get_country_default()
429
    {
430
        $countryCode = EcommerceConfig::get('EcommerceCountry', 'default_country_code');
431
        //5. take the FIRST country from the get_allowed_country_codes
432
        if (!$countryCode) {
433
            $countryArray = self::list_of_allowed_entries_for_dropdown();
434
            if (is_array($countryArray) && count($countryArray)) {
435
                foreach ($countryArray as $countryCode => $countryName) {
436
                    //we stop at the first one... as we have no idea which one is the best.
437
                    break;
438
                }
439
            }
440
        }
441
442
        return $countryCode;
443
    }
444
445
    /**
446
     * This function works out the most likely country for the current order
447
     * and returns the Country Object, if any.
448
     *
449
     * @param bool    (optional)   $recalculate
450
     * @param string  (optional)   $countryCode
451
     *
452
     * @return EcommerceCountry | Null
0 ignored issues
show
Should the return type not be EcommerceCountry|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...
453
     **/
454
    public static function get_country_object($recalculate = false, $countryCode = null)
455
    {
456
        if (! $countryCode) {
457
            $countryCode = self::get_country($recalculate);
458
        }
459
460
        return EcommerceCountry::get()->filter(array('Code' => $countryCode))->First();
461
    }
462
463
    /**
464
     * maps like this
465
     *     NZ => 1
466
     *     AU => 2
467
     *     etc...
468
     * @var array
469
     */
470
    private static $_code_to_id_map = array();
471
    /**
472
     * returns the ID of the country or 0.
473
     *
474
     * @param string (optional)   $countryCode
475
     * @param bool   (optional)   $recalculate
476
     *
477
     * @return int
478
     **/
479
    public static function get_country_id($countryCode = null, $recalculate = false)
480
    {
481
        if (!$countryCode) {
482
            $countryCode = self::get_country($recalculate);
483
        }
484
        if (isset(self::$_code_to_id_map[$countryCode])) {
485
            return self::$_code_to_id_map[$countryCode];
486
        }
487
        self::$_code_to_id_map[$countryCode] = 0;
488
        $country = EcommerceCountry::get()
489
            ->filter(array('Code' => $countryCode))
490
            ->first();
491
        if ($country) {
492
            self::$_code_to_id_map[$countryCode] = $country->ID;
493
            return $country->ID;
494
        }
495
496
        return 0;
497
    }
498
499
    /**
500
     * Memory for allow country to check.
501
     *
502
     * @var null | Boolean
503
     */
504
    private static $_allow_sales_cache = array();
505
506
    public static function reset_allow_sales_cache()
507
    {
508
        self::$_allow_sales_cache = null;
509
    }
510
511
    /**
512
     * Checks if we are allowed to sell to the current country.
513
     *
514
     * @param int (optional) $orderID
515
     *
516
     * @return bool
517
     */
518
    public static function allow_sales($orderID = 0)
519
    {
520
        $orderID = ShoppingCart::current_order_id($orderID);
521
        if (!isset(self::$_allow_sales_cache[$orderID])) {
522
            self::$_allow_sales_cache[$orderID] = true;
523
            $countryCode = self::get_country(false, $orderID);
524
            if ($countryCode) {
525
                $countries = EcommerceCountry::get()
526
                    ->filter(array(
527
                        'DoNotAllowSales' => 1,
528
                        'Code' => $countryCode,
529
                    ));
530
                if ($countries->count()) {
531
                    self::$_allow_sales_cache[$orderID] = false;
532
                }
533
            }
534
        }
535
536
        return self::$_allow_sales_cache[$orderID];
537
    }
538
539
    /**
540
     * returns an array of Codes => Names of all countries that can be used.
541
     * Use "list_of_allowed_entries_for_dropdown" to get the list.
542
     *
543
     * @todo add caching
544
     *
545
     * @return array
546
     **/
547
    protected static function get_default_array()
548
    {
549
        $defaultArray = array();
550
        $countries = null;
0 ignored issues
show
$countries is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
551
        if ($code = self::get_fixed_country_code()) {
552
            $defaultArray[$code] = self::find_title($code);
553
554
            return $defaultArray;
555
        }
556
        $countries = EcommerceCountry::get()->exclude(array('DoNotAllowSales' => 1));
557
        if ($countries && $countries->count()) {
558
            foreach ($countries as $country) {
559
                $defaultArray[$country->Code] = $country->Name;
560
            }
561
        }
562
563
        return $defaultArray;
564
    }
565
566
    public function getCMSFields()
567
    {
568
        $fields = parent::getCMSFields();
569
        $fields->addFieldToTab('Root.Main', new LiteralField(
570
            'Add Add Countries',
571
            '
572
                <h3>Short-Cuts</h3>
573
                <h6>
574
                    <a href="/dev/tasks/EcommerceTaskCountryAndRegion_DisallowAllCountries" target="_blank">'._t('EcommerceCountry.DISALLOW_ALL', 'disallow sales to all countries').'</a> |||
575
                    <a href="/dev/tasks/EcommerceTaskCountryAndRegion_AllowAllCountries" target="_blank">'._t('EcommerceCountry.ALLOW_ALL', 'allow sales to all countries').'</a>
576
                </h6>
577
            ')
578
        );
579
580
        return $fields;
581
    }
582
583
584
585
    /**
586
     * link to edit the record.
587
     *
588
     * @param string | Null $action - e.g. edit
589
     *
590
     * @return string
591
     */
592
    public function CMSEditLink($action = null)
593
    {
594
        return CMSEditLinkAPI::find_edit_link_for_object($this, $action);
595
    }
596
597
    /**
598
     * standard SS method.
599
     */
600
    public function requireDefaultRecords()
601
    {
602
        parent::requireDefaultRecords();
603
        if ((! EcommerceCountry::get()->count()) || isset($_REQUEST['resetecommercecountries'])) {
604
            $task = new EcommerceTaskCountryAndRegion();
605
            $task->run(null);
606
        }
607
    }
608
609
    /**
610
     * standard SS method
611
     * cleans up codes.
612
     */
613
    public function onBeforeWrite()
614
    {
615
        parent::onBeforeWrite();
616
        $filter = EcommerceCodeFilter::create();
617
        $filter->checkCode($this);
618
    }
619
620
    //DYNAMIC LIMITATIONS
621
622
    /**
623
     * these variables and methods allow to "dynamically limit the countries available,
624
     * based on, for example: ordermodifiers, item selection, etc....
625
     * for example, if a person chooses delivery within Australasia (with modifier) -
626
     * then you can limit the countries available to "Australasian" countries.
627
     */
628
629
    /**
630
     * List of countries that should be shown.
631
     *
632
     * @param array $a: should be country codes e.g. array("NZ", "NP", "AU");
633
     *
634
     * @var array
635
     */
636
    private static $for_current_order_only_show_countries = array();
637
    public static function get_for_current_order_only_show_countries()
638
    {
639
        return self::$for_current_order_only_show_countries;
640
    }
641
    public static function set_for_current_order_only_show_countries(array $a)
642
    {
643
        if (count(self::$for_current_order_only_show_countries)) {
644
            //we INTERSECT here so that only countries allowed by all forces (modifiers) are added.
645
                self::$for_current_order_only_show_countries = array_intersect($a, self::$for_current_order_only_show_countries);
646
        } else {
647
            self::$for_current_order_only_show_countries = $a;
648
        }
649
    }
650
651
    /**
652
     * List of countries that should NOT be shown.
653
     *
654
     * @param array $a: should be country codes e.g. array("NZ", "NP", "AU");
655
     *
656
     * @var array
657
     */
658
    private static $for_current_order_do_not_show_countries = array();
659
    public static function get_for_current_order_do_not_show_countries()
660
    {
661
        return self::$for_current_order_do_not_show_countries;
662
    }
663
    public static function set_for_current_order_do_not_show_countries(array $a)
664
    {
665
        //We MERGE here because several modifiers may limit the countries
666
            self::$for_current_order_do_not_show_countries = array_merge($a, self::$for_current_order_do_not_show_countries);
667
    }
668
669
    /**
670
     * @var array
671
     */
672
    private static $list_of_allowed_entries_for_dropdown_array = array();
673
674
    /**
675
     * takes the defaultArray and limits it with "only show" and "do not show" value, relevant for the current order.
676
     *
677
     * @return array (Code, Title)
678
     **/
679
    public static function list_of_allowed_entries_for_dropdown()
680
    {
681
        if (!self::$list_of_allowed_entries_for_dropdown_array) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::$list_of_allowed_entries_for_dropdown_array of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
682
            $defaultArray = self::get_default_array();
683
            $onlyShow = self::$for_current_order_only_show_countries;
684
            $doNotShow = self::$for_current_order_do_not_show_countries;
685
            if (is_array($onlyShow) && count($onlyShow)) {
686
                foreach ($defaultArray as $key => $value) {
687
                    if (!in_array($key, $onlyShow)) {
688
                        unset($defaultArray[$key]);
689
                    }
690
                }
691
            }
692
            if (is_array($doNotShow) && count($doNotShow)) {
693
                foreach ($doNotShow as $code) {
694
                    if (isset($defaultArray[$code])) {
695
                        unset($defaultArray[$code]);
696
                    }
697
                }
698
            }
699
            self::$list_of_allowed_entries_for_dropdown_array = $defaultArray;
700
        }
701
702
        return self::$list_of_allowed_entries_for_dropdown_array;
703
    }
704
705
    /**
706
     * checks if a code is allowed.
707
     *
708
     * @param string $code - e.g. NZ, NSW, or CO
709
     *
710
     * @return bool
711
     **/
712
    public static function code_allowed($code)
713
    {
714
        return array_key_exists($code, self::list_of_allowed_entries_for_dropdown());
715
    }
716
717
    /**
718
     * Casted variable to show if sales are allowed to this country.
719
     *
720
     * @return bool
721
     */
722
    public function AllowSales()
723
    {
724
        return $this->getAllowSales();
725
    }
726
    public function getAllowSales()
727
    {
728
        if ($this->DoNotAllowSales) {
0 ignored issues
show
The property DoNotAllowSales does not exist on object<EcommerceCountry>. 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...
The if-else statement can be simplified to return !$this->DoNotAllowSales;.
Loading history...
729
            return false;
730
        } else {
731
            return true;
732
        }
733
    }
734
735
    /**
736
     * Casted variable to show if sales are allowed to this country.
737
     *
738
     * @return string
739
     */
740
    public function AllowSalesNice()
741
    {
742
        return $this->getAllowSalesNice();
743
    }
744
    public function getAllowSalesNice()
745
    {
746
        if ($this->AllowSales()) {
747
            return _t('EcommerceCountry.YES', 'Yes');
748
        } else {
749
            return _t('EcommerceCountry.NO', 'No');
750
        }
751
    }
752
}
753