Completed
Branch Gutenberg/form-system (1dc3a2)
by
unknown
88:07 queued 79:28
created

CountrySubRegionDao   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 208
rs 10
c 0
b 0
f 0
wmc 28
lcom 1
cbo 4

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B saveCountrySubRegions() 0 28 8
A getCountrySubRegionDataVersion() 0 4 1
A updateCountrySubRegionDataVersion() 0 9 2
B processCountryData() 0 18 7
A retrieveJsonData() 0 28 4
A saveSubRegionData() 0 30 5
1
<?php
2
3
namespace EventEspresso\core\services\address;
4
5
use EE_Country;
6
use EE_Error;
7
use EEM_State;
8
use EventEspresso\core\exceptions\InvalidDataTypeException;
9
use EventEspresso\core\exceptions\InvalidInterfaceException;
10
use EventEspresso\core\services\validators\JsonValidator;
11
use InvalidArgumentException;
12
use ReflectionException;
13
use stdClass;
14
use WP_Error;
15
16
/**
17
 * Class CountrySubRegionDao
18
 * Data Access Object for retrieving Country and Country SubRegion data
19
 *
20
 * @package EventEspresso\core\services\address
21
 * @author  Brent Christensen
22
 * @since   4.9.70.p
23
 */
24
class CountrySubRegionDao
25
{
26
27
    const REPO_URL = 'https://raw.githubusercontent.com/eventespresso/countries-and-subregions/master/';
28
29
    const OPTION_NAME_COUNTRY_DATA_VERSION = 'espresso-country-sub-region-data-version';
30
31
    /**
32
     * @var EEM_State $state_model
33
     */
34
    private $state_model;
35
36
    /**
37
     * @var JsonValidator $json_validator
38
     */
39
    private $json_validator;
40
41
    /**
42
     * @var string $data_version
43
     */
44
    private $data_version;
45
46
    /**
47
     * @var array $countries
48
     */
49
    private $countries = array();
50
51
52
    /**
53
     * CountrySubRegionDao constructor.
54
     *
55
     * @param EEM_State     $state_model
56
     * @param JsonValidator $json_validator
57
     */
58
    public function __construct(EEM_State $state_model, JsonValidator $json_validator)
59
    {
60
        $this->state_model = $state_model;
61
        $this->json_validator = $json_validator;
62
    }
63
64
65
    /**
66
     * @param EE_Country $country_object
67
     * @return void
68
     * @throws EE_Error
69
     * @throws InvalidArgumentException
70
     * @throws InvalidDataTypeException
71
     * @throws InvalidInterfaceException
72
     * @throws ReflectionException
73
     */
74
    public function saveCountrySubRegions(EE_Country $country_object)
75
    {
76
        $CNT_ISO = $country_object->ID();
77
        $has_sub_regions = $this->state_model->count(array(array('Country.CNT_ISO' => $CNT_ISO)));
78
        $data = [];
79
        if (empty($this->countries)) {
80
            $this->data_version = $this->getCountrySubRegionDataVersion();
81
            $data = $this->retrieveJsonData(self::REPO_URL . 'countries.json');
82
        }
83
        if (empty($data)) {
84
            EE_Error::add_error(
85
                'Country Subregion Data could not be retrieved',
86
                __FILE__,
87
                __METHOD__,
88
                __LINE__
89
            );
90
        }
91
        if (! $has_sub_regions
92
            || (isset($data->version) && version_compare($data->version, $this->data_version))
93
        ) {
94
            if (isset($data->countries)
95
                && $this->processCountryData($CNT_ISO, $data->countries) > 0
96
            ) {
97
                $this->countries = $data->countries;
98
                $this->updateCountrySubRegionDataVersion($data->version);
99
            }
100
        }
101
    }
102
103
104
    /**
105
     * @since 4.9.70.p
106
     * @return string
107
     */
108
    private function getCountrySubRegionDataVersion()
109
    {
110
        return get_option(self::OPTION_NAME_COUNTRY_DATA_VERSION, null);
111
    }
112
113
114
    /**
115
     * @param string $version
116
     */
117
    private function updateCountrySubRegionDataVersion($version = '')
118
    {
119
        // add version option if it has never been added before, or update existing
120
        if ($this->data_version === null) {
121
            add_option(self::OPTION_NAME_COUNTRY_DATA_VERSION, $version, '', false);
122
        } else {
123
            update_option(self::OPTION_NAME_COUNTRY_DATA_VERSION, $version);
124
        }
125
    }
126
127
128
    /**
129
     * @param string $CNT_ISO
130
     * @param array  $countries
131
     * @since 4.9.70.p
132
     * @return int
133
     * @throws EE_Error
134
     * @throws InvalidArgumentException
135
     * @throws InvalidDataTypeException
136
     * @throws InvalidInterfaceException
137
     */
138
    private function processCountryData($CNT_ISO, $countries = array())
139
    {
140
        if (! empty($countries)) {
141
            foreach ($countries as $key => $country) {
142
                if ($country instanceof stdClass
143
                    && $country->code === $CNT_ISO
144
                    && empty($country->sub_regions)
145
                    && ! empty($country->filename)
146
                ) {
147
                    $country->sub_regions = $this->retrieveJsonData(
148
                        self::REPO_URL . 'countries/' . $country->filename . '.json'
149
                    );
150
                    return $this->saveSubRegionData($country, $country->sub_regions);
151
                }
152
            }
153
        }
154
        return 0;
155
    }
156
157
158
    /**
159
     * @param string $url
160
     * @return array
161
     */
162
    private function retrieveJsonData($url)
163
    {
164
        if (empty($url)) {
165
            EE_Error::add_error(
166
                'No URL was provided!',
167
                __FILE__,
168
                __METHOD__,
169
                __LINE__
170
            );
171
            return array();
172
        }
173
        $request = wp_safe_remote_get($url);
174
        if ($request instanceof WP_Error) {
0 ignored issues
show
Bug introduced by
The class WP_Error does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
175
            EE_Error::add_error(
176
                $request->get_error_message(),
177
                __FILE__,
178
                __METHOD__,
179
                __LINE__
180
            );
181
            return array();
182
        }
183
        $body = wp_remote_retrieve_body($request);
184
        $json = json_decode($body);
185
        if ($this->json_validator->isValid(__FILE__, __METHOD__, __LINE__)) {
186
            return $json;
187
        }
188
        return array();
189
    }
190
191
192
    /**
193
     * @param stdClass $country
194
     * @param array    $sub_regions
195
     * @return int
196
     * @throws EE_Error
197
     * @throws InvalidArgumentException
198
     * @throws InvalidDataTypeException
199
     * @throws InvalidInterfaceException
200
     */
201
    private function saveSubRegionData(stdClass $country, $sub_regions = array())
202
    {
203
        $results = 0;
204
        if (is_array($sub_regions)) {
205
            foreach ($sub_regions as $sub_region) {
206
                // remove country code from sub region code
207
                $abbrev = str_replace(
208
                    $country->code . '-',
209
                    '',
210
                    sanitize_text_field($sub_region->code)
211
                );
212
                // but NOT if sub region code results in only a number
213
                if (absint($abbrev) !== 0) {
214
                    $abbrev = sanitize_text_field($sub_region->code);
215
                }
216
                if ($this->state_model->insert(
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->state_model->inse...e), 'STA_active' => 1)) of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
217
                    array(
218
                        // STA_ID CNT_ISO STA_abbrev STA_name STA_active
219
                        'CNT_ISO'    => $country->code,
220
                        'STA_abbrev' => $abbrev,
221
                        'STA_name'   => sanitize_text_field($sub_region->name),
222
                        'STA_active' => 1,
223
                    )
224
                )) {
225
                    $results++;
226
                }
227
            }
228
        }
229
        return $results;
230
    }
231
}
232