This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||
2 | /** |
||
3 | * Contains the customer class |
||
4 | * |
||
5 | * @since 1.0.15 |
||
6 | */ |
||
7 | |||
8 | defined( 'ABSPATH' ) || exit; |
||
9 | |||
10 | /** |
||
11 | * Customer class. |
||
12 | * |
||
13 | * @since 1.0.15 |
||
14 | * |
||
15 | */ |
||
16 | class GetPaid_Customer extends GetPaid_Data { |
||
17 | |||
18 | /** |
||
19 | * Which data store to load. |
||
20 | * |
||
21 | * @var string |
||
22 | */ |
||
23 | protected $data_store_name = 'customer'; |
||
24 | |||
25 | /** |
||
26 | * This is the name of this object type. |
||
27 | * |
||
28 | * @var string |
||
29 | */ |
||
30 | protected $object_type = 'customer'; |
||
31 | |||
32 | /** |
||
33 | * Get the customer if ID is passed, otherwise the customer is new and empty. |
||
34 | * |
||
35 | * @param int|string|GetPaid_Customer|object $customer customer id, object, or email. |
||
36 | */ |
||
37 | public function __construct( $customer = 0 ) { |
||
38 | |||
39 | // Setup default customer data. |
||
40 | $this->setup_default_data(); |
||
41 | |||
42 | if ( is_numeric( $customer ) ) { |
||
43 | $this->set_id( $customer ); |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
44 | } elseif ( $customer instanceof self ) { |
||
45 | $this->set_id( $customer->get_id() ); |
||
46 | } elseif ( is_string( $customer ) && $customer_id = self::get_customer_id_by( $customer, 'email' ) ) { |
||
47 | $this->set_id( $customer_id ); |
||
48 | } elseif ( ! empty( $customer->id ) ) { |
||
49 | $this->set_id( $customer->id ); |
||
50 | } |
||
51 | |||
52 | // Load the datastore. |
||
53 | $this->data_store = GetPaid_Data_Store::load( $this->data_store_name ); |
||
54 | |||
55 | if ( $this->get_id() > 0 ) { |
||
56 | $this->data_store->read( $this ); |
||
57 | } |
||
58 | |||
59 | $this->set_object_read( true ); |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * Sets up default customer data. |
||
64 | */ |
||
65 | private function setup_default_data() { |
||
66 | |||
67 | $this->data = array( |
||
68 | 'user_id' => 0, |
||
69 | 'email' => '', |
||
70 | 'email_cc' => '', |
||
71 | 'status' => 'active', |
||
72 | 'purchase_value' => 0, |
||
73 | 'purchase_count' => 0, |
||
74 | 'date_created' => current_time( 'mysql' ), |
||
75 | 'date_modified' => current_time( 'mysql' ), |
||
76 | 'uuid' => wp_generate_uuid4(), |
||
77 | ); |
||
78 | |||
79 | // Add address fields. |
||
80 | foreach ( array_keys( getpaid_user_address_fields() ) as $field ) { |
||
81 | |||
82 | if ( isset( $this->data[ $field ] ) ) { |
||
83 | continue; |
||
84 | } |
||
85 | |||
86 | // Country. |
||
87 | if ( 'country' === $field ) { |
||
88 | $this->data[ $field ] = wpinv_get_default_country(); |
||
89 | continue; |
||
90 | } |
||
91 | |||
92 | // State. |
||
93 | if ( 'state' === $field ) { |
||
94 | $this->data[ $field ] = wpinv_get_default_state(); |
||
95 | continue; |
||
96 | } |
||
97 | |||
98 | $this->data[ $field ] = ''; |
||
99 | } |
||
100 | |||
101 | $this->default_data = $this->data; |
||
102 | } |
||
103 | |||
104 | /** |
||
105 | * Given a customer email or user id, it returns a customer id. |
||
106 | * |
||
107 | * @static |
||
108 | * @param string $value |
||
109 | * @since 1.0.15 |
||
110 | * @return int |
||
111 | */ |
||
112 | public static function get_customer_id_by( $value, $by = 'email' ) { |
||
113 | global $wpdb; |
||
114 | |||
115 | // Prepare value. |
||
116 | if ( 'email' === $by ) { |
||
117 | $value = sanitize_email( $value ); |
||
118 | } elseif ( 'user_id' === $by ) { |
||
119 | $value = absint( $value ); |
||
120 | } else { |
||
121 | return 0; |
||
122 | } |
||
123 | |||
124 | if ( empty( $value ) ) { |
||
125 | return 0; |
||
126 | } |
||
127 | |||
128 | // Maybe retrieve from the cache. |
||
129 | $cache_key = 'getpaid_customer_ids_by_' . $by; |
||
130 | $customer_id = wp_cache_get( $value, $cache_key ); |
||
131 | if ( false !== $customer_id ) { |
||
132 | return $customer_id; |
||
133 | } |
||
134 | |||
135 | if ( 'email' === $by ) { |
||
136 | $customer_id = (int) $wpdb->get_var( |
||
137 | $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}getpaid_customers WHERE email=%s LIMIT 1", $value ) |
||
138 | ); |
||
139 | } elseif ( 'user_id' === $by ) { |
||
140 | $customer_id = (int) $wpdb->get_var( |
||
141 | $wpdb->prepare( "SELECT id FROM {$wpdb->prefix}getpaid_customers WHERE user_id=%d LIMIT 1", $value ) |
||
142 | ); |
||
143 | } |
||
144 | |||
145 | // Update the cache with our data |
||
146 | wp_cache_set( $value, $customer_id, $cache_key ); |
||
147 | |||
148 | return $customer_id; |
||
0 ignored issues
–
show
The expression
return $customer_id could also return false which is incompatible with the documented return type integer . Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled. ![]() |
|||
149 | |||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Clears the customer's cache. |
||
154 | */ |
||
155 | public function clear_cache() { |
||
156 | wp_cache_delete( $this->get( 'email' ), 'getpaid_customer_ids_by_email' ); |
||
157 | wp_cache_delete( $this->get( 'user_id' ), 'getpaid_customer_ids_by_user_id' ); |
||
158 | wp_cache_delete( $this->get_id(), 'getpaid_customers' ); |
||
159 | } |
||
160 | |||
161 | /* |
||
162 | |-------------------------------------------------------------------------- |
||
163 | | CRUD methods |
||
164 | |-------------------------------------------------------------------------- |
||
165 | | |
||
166 | | Methods which create, read, update and delete discounts from the database. |
||
167 | | |
||
168 | */ |
||
169 | |||
170 | /* |
||
171 | |-------------------------------------------------------------------------- |
||
172 | | Getters |
||
173 | |-------------------------------------------------------------------------- |
||
174 | */ |
||
175 | |||
176 | /** |
||
177 | * Margic method for retrieving a property. |
||
178 | * |
||
179 | * @param string $key The key to fetch. |
||
180 | * @param string $context View or edit context. |
||
181 | */ |
||
182 | public function get( $key, $context = 'view' ) { |
||
183 | |||
184 | // Maybe strip _wpinv_ prefix from key. |
||
185 | $key = str_replace( '_wpinv_', '', $key ); |
||
186 | |||
187 | // Check if we have a helper method for that. |
||
188 | if ( method_exists( $this, 'get_' . $key ) ) { |
||
189 | return call_user_func( array( $this, 'get_' . $key ), $context ); |
||
190 | } |
||
191 | |||
192 | return $this->get_prop( $key, $context ); |
||
193 | |||
194 | } |
||
195 | |||
196 | /* |
||
197 | |-------------------------------------------------------------------------- |
||
198 | | Setters |
||
199 | |-------------------------------------------------------------------------- |
||
200 | | |
||
201 | | Functions for setting customer data. These should not update anything in the |
||
202 | | database itself and should only change what is stored in the class |
||
203 | | object. |
||
204 | */ |
||
205 | |||
206 | /** |
||
207 | * Margic method for setting a property. |
||
208 | * |
||
209 | * @param string $key The key to fetch. |
||
210 | * @param mixed $value The new value. |
||
211 | */ |
||
212 | public function set( $key, $value ) { |
||
213 | |||
214 | // Check if we have a helper method for that. |
||
215 | if ( method_exists( $this, 'set_' . $key ) ) { |
||
216 | return call_user_func( array( $this, 'set_' . $key ), $value ); |
||
0 ignored issues
–
show
array($this, 'set_' . $key) can contain request data and is used in code execution context(s) leading to a potential security vulnerability.
1 path for user data to reach this point
General Strategies to prevent injectionIn general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
throw new \InvalidArgumentException('This input is not allowed.');
}
For numeric data, we recommend to explicitly cast the data: $sanitized = (integer) $tainted;
![]() |
|||
217 | } |
||
218 | |||
219 | return $this->set_prop( $key, $value ); |
||
0 ignored issues
–
show
Are you sure the usage of
$this->set_prop($key, $value) targeting GetPaid_Data::set_prop() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||
220 | |||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Sets customer status. |
||
225 | * |
||
226 | * @since 1.0.0 |
||
227 | * @param string $status New status. |
||
228 | */ |
||
229 | public function set_status( $status ) { |
||
230 | |||
231 | if ( in_array( $status, array( 'active', 'inactive', 'blocked' ), true ) ) { |
||
232 | return $this->set_prop( 'status', $status ); |
||
0 ignored issues
–
show
Are you sure the usage of
$this->set_prop('status', $status) targeting GetPaid_Data::set_prop() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||
233 | } |
||
234 | |||
235 | $this->set_prop( 'status', 'inactive' ); |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Sets the purchase value. |
||
240 | * |
||
241 | * @since 1.0.0 |
||
242 | * @param float $purchase_value. |
||
243 | */ |
||
244 | public function set_purchase_value( $purchase_value ) { |
||
245 | $this->set_prop( 'purchase_value', (float) $purchase_value ); |
||
246 | } |
||
247 | |||
248 | /** |
||
249 | * Sets the purchase count. |
||
250 | * |
||
251 | * @since 1.0.0 |
||
252 | * @param int $purchase_count. |
||
253 | */ |
||
254 | public function set_purchase_count( $purchase_count ) { |
||
255 | $this->set_prop( 'purchase_count', absint( $purchase_count ) ); |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * Sets the user id. |
||
260 | * |
||
261 | * @since 1.0.0 |
||
262 | * @param int $user_id. |
||
263 | */ |
||
264 | public function set_user_id( $user_id ) { |
||
265 | $this->set_prop( 'user_id', absint( $user_id ) ); |
||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Sets the email. |
||
270 | * |
||
271 | * @since 1.0.0 |
||
272 | * @param string $email. |
||
273 | */ |
||
274 | public function set_email( $email ) { |
||
275 | $email = is_string( $email ) ? sanitize_email( $email ) : ''; |
||
276 | $this->set_prop( 'email', $email ); |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * Sets the email cc. |
||
281 | * |
||
282 | * @since 1.0.0 |
||
283 | * @param string $email_cc. |
||
284 | */ |
||
285 | public function set_email_cc( $email_cc ) { |
||
286 | $email_cc = implode( ', ', wp_parse_list( $email_cc ) ); |
||
287 | $this->set_prop( 'email_cc', $email_cc ); |
||
288 | } |
||
289 | |||
290 | /** |
||
291 | * Sets the created date. |
||
292 | * |
||
293 | * @since 1.0.0 |
||
294 | * @param string $date_created date created. |
||
295 | */ |
||
296 | public function set_date_created( $date_created ) { |
||
297 | |||
298 | $date = strtotime( $date_created ); |
||
299 | |||
300 | if ( $date && $date_created !== '0000-00-00 00:00:00' && $date_created !== '0000-00-00 00:00' ) { |
||
301 | $this->set_prop( 'date_created', gmdate( 'Y-m-d H:i:s', $date ) ); |
||
302 | return; |
||
303 | } |
||
304 | |||
305 | $this->set_prop( 'date_created', null ); |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * Sets the created date. |
||
310 | * |
||
311 | * @since 1.0.0 |
||
312 | * @param string $date_modified date created. |
||
313 | */ |
||
314 | public function set_date_modified( $date_modified ) { |
||
315 | |||
316 | $date = strtotime( $date_modified ); |
||
317 | |||
318 | if ( $date && $date_modified !== '0000-00-00 00:00:00' && $date_modified !== '0000-00-00 00:00' ) { |
||
319 | $this->set_prop( 'date_modified', gmdate( 'Y-m-d H:i:s', $date ) ); |
||
320 | return; |
||
321 | } |
||
322 | |||
323 | $this->set_prop( 'date_modified', null ); |
||
324 | } |
||
325 | |||
326 | /* |
||
327 | |-------------------------------------------------------------------------- |
||
328 | | Additional methods |
||
329 | |-------------------------------------------------------------------------- |
||
330 | | |
||
331 | | This method help you manipulate a customer. |
||
332 | | |
||
333 | */ |
||
334 | |||
335 | /** |
||
336 | * Saves the customer. |
||
337 | * |
||
338 | * @since 1.0.0 |
||
339 | */ |
||
340 | public function save() { |
||
341 | |||
342 | $maybe_set = array( |
||
343 | 'uuid' => wp_generate_uuid4(), |
||
344 | 'date_created' => current_time( 'mysql' ), |
||
345 | ); |
||
346 | |||
347 | foreach ( $maybe_set as $key => $value ) { |
||
348 | $current_value = $this->get( $key ); |
||
349 | |||
350 | if ( empty( $current_value ) ) { |
||
351 | $this->set( $key, $value ); |
||
352 | } |
||
353 | } |
||
354 | |||
355 | $this->set( 'date_modified', current_time( 'mysql' ) ); |
||
356 | |||
357 | return parent::save(); |
||
358 | } |
||
359 | |||
360 | /** |
||
361 | * Helper method to clone a customer from a user ID. |
||
362 | * |
||
363 | * @since 1.0.0 |
||
364 | * @param int $user_id. |
||
365 | */ |
||
366 | public function clone_user( $user_id ) { |
||
367 | $user = get_userdata( $user_id ); |
||
368 | |||
369 | if ( empty( $user ) ) { |
||
370 | return; |
||
371 | } |
||
372 | |||
373 | $this->set_user_id( $user->ID ); |
||
374 | $this->set_email( $user->user_email ); |
||
375 | $this->set_purchase_value( getpaid_get_user_total_spend( $user->ID ) ); |
||
376 | $this->set_purchase_count( getpaid_count_user_invoices( $user->ID ) ); |
||
377 | $this->set( 'first_name', $user->first_name ); |
||
378 | $this->set( 'last_name', $user->last_name ); |
||
379 | $this->set_date_created( $user->user_registered ); |
||
380 | |||
381 | // Fetch extra data from WC or old GetPaid. |
||
382 | $prefixes = array( |
||
383 | '_wpinv_', |
||
384 | 'billing_', |
||
385 | '', |
||
386 | ); |
||
387 | |||
388 | foreach ( array_keys( getpaid_user_address_fields() ) as $field ) { |
||
389 | |||
390 | foreach ( $prefixes as $prefix ) { |
||
391 | |||
392 | // Meta table. |
||
393 | $value = get_user_meta( $user_id, $prefix . $field, true ); |
||
394 | |||
395 | // UWP table. |
||
396 | $value = ( empty( $value ) && function_exists( 'uwp_get_usermeta' ) ) ? uwp_get_usermeta( $user_id, $prefix . $field ) : $value; |
||
397 | |||
398 | if ( ! empty( $value ) ) { |
||
399 | $this->set( $field, $value ); |
||
400 | continue; |
||
401 | } |
||
402 | } |
||
403 | } |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * Helper method to migrate an existing user ID to the new customers table. |
||
408 | * |
||
409 | * @since 1.0.0 |
||
410 | * @param int $user_id. |
||
411 | */ |
||
412 | public function migrate_from_user( $user_id ) { |
||
413 | $this->clone_user( $user_id ); |
||
414 | do_action( 'getpaid_customer_migrated_from_user', $this, $user_id ); |
||
415 | $this->save(); |
||
416 | } |
||
417 | } |
||
418 |