Issues (575)

classes/class-frontend.php (5 issues)

1
<?php
2
/**
3
 * LSX Currency Frontend Class
4
 *
5
 * @package   LSX Currencies
6
 * @author    LightSpeed
7
 * @license   GPL3
8
 * @link
9
 * @copyright 2019 LightSpeed
10
 */
11
12
namespace lsx\currencies\classes;
13
14
/**
15
 * The frontend classes
16
 */
17
class Frontend {
18
19
	/**
20
	 * Holds instance of the class
21
	 *
22
	 * @var object \lsx\currencies\classes\Admin()
23
	 */
24
	private static $instance;
25
26
	/**
27
	 * This will hold the rates with a base currency of USD.
28
	 *
29
	 * @var boolean
30
	 */
31
	public $rates = false;
32
33
	/**
34
	 * This will hold the rates error message.
35
	 *
36
	 * @var boolean
37
	 */
38
	public $rates_message = false;
39
40
	/**
41
	 * This is the current currency selected, default to the base currency.
42
	 *
43
	 * @var boolean
44
	 */
45
	public $current_currency = false;
46
47
	/**
48
	 * Constructor
49
	 */
50
	public function __construct() {
51
		if ( ! is_admin() ) {
52
			add_action( 'plugins_loaded', array( $this, 'set_defaults' ), 11, 1 );
53
			add_filter( 'lsx_to_custom_field_query', array( $this, 'price_filter' ), 20, 5 );
54
			add_action( 'wp_enqueue_scripts', array( $this, 'assets' ), 5 );
55
			add_filter( 'wp_nav_menu_items', array( $this, 'wp_nav_menu_items_filter' ), 10, 2 );
56
			add_filter( 'wp_kses_allowed_html', array( $this, 'wp_kses_allowed_html' ), 10, 2 );
57
			add_filter( 'get_post_metadata', array( $this, 'filter_post_meta' ), 100, 4 );
58
		}
59
	}
60
61
	/**
62
	 * Return an instance of this class.
63
	 *
64
	 * @return  object
65
	 */
66
	public static function init() {
67
		// If the single instance hasn't been set, set it now.
68
		if ( ! isset( self::$instance ) ) {
69
			self::$instance = new self();
70
		}
71
		return self::$instance;
72
	}
73
74
	/**
75
	 * Constructor
76
	 */
77
	public function set_defaults() {
78
		$this->rates_message = esc_html__( 'Error: API key isn\'t set.', 'lsx-currencies' );
0 ignored issues
show
Documentation Bug introduced by
The property $rates_message was declared of type boolean, but esc_html__('Error: API k...et.', 'lsx-currencies') is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
79
		$this->rates = get_transient( 'lsx_currencies_rates' );
80
		if ( false === $this->rates ) {
81
			$rates         = wp_remote_retrieve_body( wp_safe_remote_get( lsx_currencies()->api_url ) );
82
			$decoded_rates = json_decode( $rates );
83
84
			if ( is_wp_error( $rates ) ) {
85
				$this->rates_message = $rates->get_error_message();
86
			} elseif ( ! empty( $decoded_rates->error ) ) {
87
				$this->rates_message = $decoded_rates->description;
88
			} elseif ( empty( $rates ) ) {
89
				$this->rates_message = esc_html__( 'Error: API response is empty.', 'lsx-currencies' );
90
			} else {
91
				$this->rates_message = esc_html__( 'Success (new request).', 'lsx-currencies' );
92
				set_transient( 'lsx_currencies_rates', $decoded_rates->rates, 60 * 60 * 12 );
93
				do_action( 'lsx_currencies_rates_refreshed' );
94
				$this->rates = $decoded_rates->rates;
95
			}
96
		} else {
97
			$this->rates_message = esc_html__( 'Success (from cache).', 'lsx-currencies' );
98
		}
99
		$this->current_currency = isset( $_COOKIE['lsx_currencies_choice'] ) ? sanitize_key( $_COOKIE['lsx_currencies_choice'] ) : lsx_currencies()->base_currency;
0 ignored issues
show
Documentation Bug introduced by
It seems like IssetNode ? sanitize_key...encies()->base_currency can also be of type string. However, the property $current_currency is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
100
		$this->current_currency = strtoupper( $this->current_currency );
101
	}
102
103
	/**
104
	 * Enques the assets
105
	 */
106
	public function assets() {
107
		wp_enqueue_script( 'lsx-moneyjs', LSX_CURRENCIES_URL . 'assets/js/vendor/money.min.js', array( 'jquery' ), LSX_CURRENCIES_VER, true );
108
		wp_enqueue_script( 'lsx-accountingjs', LSX_CURRENCIES_URL . 'assets/js/vendor/accounting.min.js', array( 'jquery' ), LSX_CURRENCIES_VER, true );
109
		wp_enqueue_script( 'lsx-jquery-cookie', LSX_CURRENCIES_URL . 'assets/js/vendor/cookie.min.js', array( 'jquery' ), LSX_CURRENCIES_VER, true );
110
111
		$prefix = '.min';
112
		$src = '';
113
		$script_debug = false;
114
		if ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) {
115
			$prefix = '';
116
			$src = 'src/';
117
			$script_debug = true;
118
		}
119
		wp_enqueue_script( 'lsx-currencies', LSX_CURRENCIES_URL . 'assets/js/' . $src . 'lsx-currencies' . $prefix . '.js', array( 'jquery', 'lsx-moneyjs', 'lsx-accountingjs', 'lsx-jquery-cookie' ), LSX_CURRENCIES_VER, true );
120
121
		$base_currency = lsx_currencies()->base_currency;
122
		$current_currency = $this->current_currency;
123
		if ( true === lsx_currencies()->convert_to_single ) {
124
			$current_currency = $base_currency;
125
		}
126
127
		$params = apply_filters( 'lsx_currencies_js_params', array(
128
			'current_currency'  => $current_currency,
129
			'currency_symbols'  => $this->get_available_symbols(),
130
			'rates'             => $this->rates,
131
			'rates_message'     => $this->rates_message,
132
			'base'              => $base_currency,
133
			'flags'             => lsx_currencies()->display_flags,
134
			'convert_to_single' => lsx_currencies()->convert_to_single,
135
			'script_debug'      => $script_debug,
136
			'remove_decimals'   => lsx_currencies()->remove_decimals,
137
		));
138
139
		wp_localize_script( 'lsx-currencies', 'lsx_currencies_params', $params );
140
141
		wp_enqueue_style( 'lsx-currencies', LSX_CURRENCIES_URL . 'assets/css/lsx-currencies.css', array(), LSX_CURRENCIES_VER );
142
		wp_style_add_data( 'lsx-currencies', 'rtl', 'replace' );
143
	}
144
145
	/**
146
	 * Returns all of the available symbols.
147
	 *
148
	 * @return array
149
	 */
150
	public function get_available_symbols() {
151
		$symbols = array();
152
		if ( false !== lsx_currencies()->additional_currencies && ! empty( lsx_currencies()->additional_currencies ) ) {
153
			foreach ( lsx_currencies()->additional_currencies as $key => $currency ) {
154
				$symbols[ $key ] = lsx_currencies()->currency_symbols[ $key ];
155
			}
156
		}
157
		return $symbols;
158
	}
159
160
	/**
161
	 * Adds in the required currency conversion tags
162
	 */
163
	public function price_filter( $return_html, $meta_key, $value, $before, $after ) {
164
		if ( 'price' === $meta_key ) {
165
			$additional_html = '';
166
			$additional_prices = get_post_meta( get_the_ID(), 'additional_prices', false );
0 ignored issues
show
It seems like get_the_ID() can also be of type false; however, parameter $post_id of get_post_meta() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

166
			$additional_prices = get_post_meta( /** @scrutinizer ignore-type */ get_the_ID(), 'additional_prices', false );
Loading history...
167
			$prefix = '<span class="amount lsx-currencies" ';
168
169
			if ( true === lsx_currencies()->multi_prices && ! empty( $additional_prices ) ) {
170
				foreach ( $additional_prices as $a_price ) {
171
					$additional_html .= ' data-price-' . $a_price['currency'] . '="' . $a_price['amount'] . '"';
172
				}
173
			}
174
175
			$value = preg_replace( '/[^0-9.]+/', '', $value );
176
			$decimals = substr_count( $value, '.' );
177
178
			if ( false !== $decimals && $decimals > 1 ) {
179
				$decimals--;
180
				$decimals = (int) $decimals;
181
				$value = preg_replace( '/' . preg_quote( '.', '/' ) . '/', '', $value, $decimals );
182
			}
183
184
			$money_format = 2;
185
			if ( false !== lsx_currencies()->remove_decimals ) {
186
				$money_format = 0;
187
			}
188
189
			$prefix .= '>';
190
			$suffix = '</span>';
191
192
			setlocale( LC_MONETARY, 'en_US' );
193
194
			// Work out the other tags.
195
			$currency = '<span class="currency-icon ' . mb_strtolower( lsx_currencies()->base_currency ) . '">' . lsx_currencies()->base_currency . '</span>';
196
197
			$value = ltrim( rtrim( $value ) );
198
			
199
			$for_value = number_format( (float) $value, $money_format );
200
			$for_value = str_replace( array( '$', 'USD' ), '', $for_value );
201
			
202
			$amount = '<span class="value" data-price-' . lsx_currencies()->base_currency . '="' . trim( str_replace( array( '$', 'USD' ), '', $value ) ) . '" ' . $additional_html . '>' . str_replace( array( '$', 'USD' ), '', $for_value ) . '</span>';
203
204
			// Check for a price type and add that in.
205
			$price_type = get_post_meta( get_the_ID(), 'price_type', true );
206
207
			switch ( $price_type ) {
208
				case 'per_person_per_night':
209
				case 'per_person_sharing':
210
				case 'per_person_sharing_per_night':
211
					$amount = $currency . $amount . ' ' . ucwords( str_replace( '_', ' ', $price_type ) );
212
				    break;
213
214
				case 'total_percentage':
215
					$amount .= '% ' . esc_html__( 'Off', 'lsx-currencies' );
216
					$before = str_replace( 'from', '', $before );
217
				    break;
218
219
				case 'none':
220
				default:
221
					$amount = $currency . $amount;
222
				    break;
223
			}
224
225
			$return_html = $before . $prefix . $amount . $suffix . $after;
226
		}
227
228
		return $return_html;
229
	}
230
231
	/**
232
	 * Filter on the 'wp_nav_menu_items' hook, that potentially adds a currency switcher to the item of some menus.
233
	 *
234
	 * @param $items string
235
	 * @param $args object
236
	 *
237
	 * @return string
238
	 */
239
	public function wp_nav_menu_items_filter( $items, $args ) {
240
		if ( '' !== lsx_currencies()->menus && lsx_currencies()->menus === $args->theme_location ) {
241
			if ( 'top-menu' === $args->theme_location ) {
242
				$items = $this->get_menu_html( $args ) . $items;
243
			} else {
244
				$items = $items . $this->get_menu_html( $args );
245
			}
246
		}
247
		return $items;
248
	}
249
250
	/**
251
	 * Returns the HTML string of the language switcher for a given menu.
252
	 *
253
	 * @param $args object
254
	 *
255
	 * @return string
256
	 */
257
	private function get_menu_html( $args ) {
258
		if ( empty( lsx_currencies()->additional_currencies ) ) {
259
			return '';
260
		}
261
262
		$items = '';
263
		$items .= '<li class="menu-item menu-item-currency menu-item-currency-current menu-item-has-children dropdown">';
264
		$items .= isset( $args->before ) ? $args->before : '';
265
		$items .= '<a class="current symbol-' . lsx_currencies()->switcher_symbol_position . '" href="#' . strtolower( $this->current_currency ) . '">';
0 ignored issues
show
$this->current_currency of type boolean is incompatible with the type string expected by parameter $string of strtolower(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

265
		$items .= '<a class="current symbol-' . lsx_currencies()->switcher_symbol_position . '" href="#' . strtolower( /** @scrutinizer ignore-type */ $this->current_currency ) . '">';
Loading history...
266
		$items .= isset( $args->link_before ) ? $args->link_before : '';
267
268
		if ( ! empty( lsx_currencies()->display_flags ) && 'left' === lsx_currencies()->flag_position ) {
269
			$items .= lsx_currencies()->get_currency_flag( $this->current_currency );
270
		}
271
272
		if ( 'left' === lsx_currencies()->switcher_symbol_position ) {
273
			$items .= '<span class="currency-icon ' . strtolower( $this->current_currency ) . '"></span>';
274
		}
275
276
		$items .= $this->current_currency;
277
278
		if ( 'right' === lsx_currencies()->switcher_symbol_position ) {
279
			$items .= '<span class="currency-icon ' . strtolower( $this->current_currency ) . '"></span>';
280
		}
281
282
		if ( ! empty( lsx_currencies()->display_flags ) && 'right' === lsx_currencies()->flag_position ) {
283
			$items .= lsx_currencies()->get_currency_flag( $this->current_currency );
284
		}
285
286
		$items .= isset( $args->link_after ) ? $args->link_after : '';
287
		$items .= '<span class="caret"></span></a>';
288
		$items .= isset( $args->after ) ? $args->after : '';
289
		$items .= $this->render_sub_items();
290
		$items .= '</li>';
291
		return $items;
292
	}
293
	/**
294
	 * Returns the HTML string of the language switcher for a given menu.
295
	 *
296
	 * @param object $args
297
	 *
298
	 * @return string
299
	 */
300
	private function render_sub_items() {
301
		$sub_items = '';
302
		foreach ( lsx_currencies()->additional_currencies as $key => $currency ) {
303
			$hidden = '';
304
			$class = '';
305
306
			if ( $this->current_currency === $key ) {
307
				$hidden = 'style="display:none";';
308
				$class = 'hidden';
309
			}
310
311
			$sub_items .= '<li ' . $hidden . ' class="' . $class . ' menu-item menu-item-currency ' . lsx_currencies()->switcher_symbol_position . '">';
312
			$sub_items .= '<a class=" symbol-' . lsx_currencies()->switcher_symbol_position . '" href="#' . strtolower( $key ) . '">';
313
314
			if ( ! empty( lsx_currencies()->display_flags ) && 'left' === lsx_currencies()->flag_position ) {
315
				$sub_items .= lsx_currencies()->get_currency_flag( $key );
316
			}
317
318
			if ( 'left' === lsx_currencies()->switcher_symbol_position ) {
319
				$sub_items .= '<span class="currency-icon ' . strtolower( $key ) . '"></span>';
320
			}
321
322
			$sub_items .= ucwords( $key );
323
324
			if ( 'right' === lsx_currencies()->switcher_symbol_position ) {
325
				$sub_items .= '<span class="currency-icon ' . strtolower( $key ) . '"></span>';
326
			}
327
328
			if ( ! empty( lsx_currencies()->display_flags ) && 'right' === lsx_currencies()->flag_position ) {
329
				$sub_items .= lsx_currencies()->get_currency_flag( $key );
330
			}
331
332
			$sub_items .= '</a></li>';
333
		}
334
335
		$sub_items = '<ul class="sub-menu submenu-currency dropdown-menu">' . $sub_items . '</ul>';
336
		return $sub_items;
337
	}
338
339
	/**
340
	 * Allow data params for Slick slider addon.
341
	 */
342
	public function wp_kses_allowed_html( $allowedtags, $context ) {
343
		if ( ! isset( $allowedtags['span'] ) ) {
344
			$allowedtags['span'] = array();
345
		}
346
347
		$allowedtags['span']['data-price-AUD'] = true;
348
		$allowedtags['span']['data-price-BRL'] = true;
349
		$allowedtags['span']['data-price-GBP'] = true;
350
		$allowedtags['span']['data-price-BWP'] = true;
351
		$allowedtags['span']['data-price-CAD'] = true;
352
		$allowedtags['span']['data-price-CNY'] = true;
353
		$allowedtags['span']['data-price-EUR'] = true;
354
		$allowedtags['span']['data-price-HKD'] = true;
355
		$allowedtags['span']['data-price-INR'] = true;
356
		$allowedtags['span']['data-price-IDR'] = true;
357
		$allowedtags['span']['data-price-ILS'] = true;
358
		$allowedtags['span']['data-price-JPY'] = true;
359
		$allowedtags['span']['data-price-KES'] = true;
360
		$allowedtags['span']['data-price-LAK'] = true;
361
		$allowedtags['span']['data-price-MWK'] = true;
362
		$allowedtags['span']['data-price-MYR'] = true;
363
		$allowedtags['span']['data-price-MZN'] = true;
364
		$allowedtags['span']['data-price-NAD'] = true;
365
		$allowedtags['span']['data-price-NZD'] = true;
366
		$allowedtags['span']['data-price-NOK'] = true;
367
		$allowedtags['span']['data-price-RUB'] = true;
368
		$allowedtags['span']['data-price-SGD'] = true;
369
		$allowedtags['span']['data-price-ZAR'] = true;
370
		$allowedtags['span']['data-price-SEK'] = true;
371
		$allowedtags['span']['data-price-CHF'] = true;
372
		$allowedtags['span']['data-price-TZS'] = true;
373
		$allowedtags['span']['data-price-USD'] = true;
374
		$allowedtags['span']['data-price-AED'] = true;
375
		$allowedtags['span']['data-price-ZMW'] = true;
376
		$allowedtags['span']['data-price-ZWL'] = true;
377
378
		return $allowedtags;
379
	}
380
381
	/**
382
	 * Allow empty prices if the convert to single currency is active.
383
	 *
384
	 * @param null $metadata
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $metadata is correct as it would always require null to be passed?
Loading history...
385
	 * @param string $object_id
386
	 * @param string $meta_key
387
	 * @param boolean $single
388
	 * @return void
389
	 */
390
	public function filter_post_meta( $metadata = null, $object_id, $meta_key, $single ) {
391
		if ( true === lsx_currencies()->convert_to_single && 'price' === $meta_key ) {
392
			$meta_cache = wp_cache_get( $object_id, 'post_meta' );
393
394
			if ( ! isset( $meta_cache[ $meta_key ] ) || '' === $meta_cache[ $meta_key ] ) {
395
				$metadata = '0';
396
			}
397
		}
398
		return $metadata;
399
	}
400
}
401