Passed
Branch trunk (3f392c)
by SuperNova.WS
04:59
created

getValueFromStorage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 29 and the first side effect is on line 4.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
4
require_once('general/math.php');
5
require_once('general/compatibility.php');
6
require_once('general/params.php');
7
require_once('general/nickRender.php');
8
require_once('general/formatters.php');
9
require_once('general/validators.php');
10
require_once('general/unitFunctions.php');
11
require_once('general/playerFunctions.php');
12
require_once('general/planetFunctions.php');
13
require_once('general/urlAndHttp.php');
14
require_once('general_pname.php');
15
16
17
// HOOKS AND HANDLERS ----------------------------------------------------------------------------------------------------------------
18
/**
19
 * Function wrapping
20
 *
21
 * Due glitch in PHP 5.3.1 SuperNova is incompatible with this version
22
 * Reference: https://bugs.php.net/bug.php?id=50394
23
 *
24
 * @param string $func_name
25
 * @param array  $func_arg
26
 *
27
 * @return mixed
28
 */
29
function sn_function_call($func_name, $func_arg = array()) {
30
  global $functions; // All data in $functions should be normalized to valid 'callable' state: '<function_name>'|array('<object_name>', '<method_name>')
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
31
32
  if (is_array($functions[$func_name]) && !is_callable($functions[$func_name])) {
33
    // Chain-callable functions should be made as following:
34
    // 1. Never use incomplete calls with parameters "by default"
35
    // 2. Reserve last parameter for cumulative result
36
    // 3. Use same format for original value and cumulative result (if there is original value)
37
    // 4. Honor cumulative result
38
    // 5. Return cumulative result
39
    foreach ($functions[$func_name] as $func_chain_name) {
40
      // По идее - это уже тут не нужно, потому что оно все должно быть callable к этому моменту
41
      // Но для старых модулей...
42
      if (is_callable($func_chain_name)) {
43
        $result = call_user_func_array($func_chain_name, $func_arg);
44
      }
45
    }
46
  } else {
47
    // TODO: This is left for backward compatibility. Appropriate code should be rewrote!
48
    $func_name = isset($functions[$func_name]) && is_callable($functions[$func_name]) ? $functions[$func_name] : ('sn_' . $func_name);
49
    if (is_callable($func_name)) {
50
      $result = call_user_func_array($func_name, $func_arg);
51
    }
52
  }
53
54
  return $result;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
55
}
56
57
/**
58
 * @param        $hook_list
59
 * @param        $template
60
 * @param string $hook_type - тип хука 'model' или 'view'
61
 * @param string $page_name - имя страницы, для которого должен был быть выполнен хук
62
 */
63
function execute_hooks(&$hook_list, &$template, $hook_type = null, $page_name = null) {
64
  if (!empty($hook_list)) {
65
    foreach ($hook_list as $hook) {
66
      if (is_callable($hook_call = (is_string($hook) ? $hook : (is_array($hook) ? $hook['callable'] : $hook->callable)))) {
67
        $template = call_user_func($hook_call, $template, $hook_type, $page_name);
68
      }
69
    }
70
  }
71
}
72
73
function sn_sys_handler_add(&$functions, $handler_list, $class_module_name = '', $sub_type = '') {
74
  if (isset($handler_list) && is_array($handler_list) && !empty($handler_list)) {
75
    foreach ($handler_list as $function_name => $function_data) {
76
      sys_handler_add_one($functions, $function_name, $function_data, $class_module_name, $sub_type);
77
    }
78
  }
79
}
80
81
/**
82
 * Adding one handler for specific function name
83
 *
84
 * @param callable[]   $functions
85
 * @param string       $function_name
86
 * @param string|array $function_data
87
 * @param string       $class_module_name
88
 * @param string       $sub_type
89
 */
90
function sys_handler_add_one(&$functions, $function_name, $function_data, $class_module_name, $sub_type) {
91
  if (is_string($function_data)) {
92
    $override_with = &$function_data;
93
  } elseif (isset($function_data['callable'])) {
94
    $override_with = &$function_data['callable'];
95
  }
96
97
  $overwrite = $override_with[0] == '*';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $override_with does not seem to be defined for all execution paths leading up to this point.
Loading history...
98
  if ($overwrite) {
99
    $override_with = substr($override_with, 1);
100
  }
101
102
  if (($point_position = strpos($override_with, '.')) === false && $class_module_name) {
103
    $override_with = array($class_module_name, $override_with);
104
  } elseif ($point_position == 0) {
105
    $override_with = substr($override_with, 1);
106
  } elseif ($point_position > 0) {
107
    $override_with = array(substr($override_with, 0, $point_position), substr($override_with, $point_position + 1));
108
  }
109
110
  if ($overwrite) {
111
    $functions[$function_name] = array();
112
  } elseif (!isset($functions[$function_name])) {
113
    $functions[$function_name] = array();
114
    $sn_function_name = 'sn_' . $function_name . ($sub_type ? '_' . $sub_type : '');
115
    //if(is_callable($sn_function_name))
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
116
    {
117
      $functions[$function_name][] = $sn_function_name;
118
    }
119
  }
120
121
  $functions[$function_name][] = $function_data;
122
}
123
124
125
// FLEET FUNCTIONS -----------------------------------------------------------------------------------------------------
126
/**
127
 * @param \Fleet\MissionExplore $result
128
 *
129
 * @return \Fleet\MissionExplore
130
 */
131
function flt_mission_explore_addon_object($result) { return sn_function_call('flt_mission_explore_addon_object', [$result]); }
132
133
/**
134
 * @param \Fleet\MissionExplore $result
135
 *
136
 * @return \Fleet\MissionExplore
137
 */
138
function sn_flt_mission_explore_addon_object($result) {
139
  return $result;
140
}
141
142
// FILE FUNCTIONS ----------------------------------------------------------------------------------------------------------------
143
function sys_file_read($filename) {
144
  return @file_get_contents($filename);
145
}
146
147
function sys_file_write($filename, $content) {
148
  return @file_put_contents($filename, $content, FILE_APPEND);
149
}
150
151
function sn_sys_load_php_files($dir_name, $load_extension = 'php', $modules = false) {
152
  if (file_exists($dir_name)) {
153
    $dir = opendir($dir_name);
154
    while (($file = readdir($dir)) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $dir can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, 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

154
    while (($file = readdir(/** @scrutinizer ignore-type */ $dir)) !== false) {
Loading history...
155
      if ($file == '..' || $file == '.') {
156
        continue;
157
      }
158
159
      $full_filename = $dir_name . $file;
160
      if ($modules && is_dir($full_filename)) {
161
        if (file_exists($full_filename = "{$full_filename}/{$file}.{$load_extension}")) {
162
          require_once($full_filename);
163
          // Registering module
164
          if (class_exists($file)) {
165
            new $file($full_filename);
166
          }
167
        }
168
      } else {
169
        $extension = substr($full_filename, -strlen($load_extension));
170
        if ($extension == $load_extension) {
171
          require_once($full_filename);
172
        }
173
      }
174
    }
175
  }
176
}
177
178
179
// GLOBAL DATA FUNCTIONS -----------------------------------------------------------------------------------------------
180
/**
181
 * Simple wrapper to get base or calculated value for supplied unitSnId
182
 *
183
 * @param int  $unitSnId
184
 * @param bool $plain
185
 *
186
 * @return float|int
187
 */
188
function getValueFromStorage($unitSnId, $plain = false) {
189
  $valueObject = classSupernova::$gc->valueStorage->getValueObject($unitSnId);
190
191
  return $plain ? $valueObject->base : $valueObject->getValue();
192
}
193
194
/**
195
 * Get game resource multiplier aka mining speed
196
 *
197
 * @param bool $plain
198
 *
199
 * @return float|int
200
 */
201
function game_resource_multiplier($plain = false) {
202
  return getValueFromStorage(UNIT_SERVER_SPEED_MINING, $plain);
0 ignored issues
show
Bug introduced by
UNIT_SERVER_SPEED_MINING of type string is incompatible with the type integer expected by parameter $unitSnId of getValueFromStorage(). ( Ignorable by Annotation )

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

202
  return getValueFromStorage(/** @scrutinizer ignore-type */ UNIT_SERVER_SPEED_MINING, $plain);
Loading history...
203
}
204
205
/**
206
 * Get game speed aka manufacturing speed
207
 *
208
 * @param bool $plain
209
 *
210
 * @return float|int
211
 */
212
function get_game_speed($plain = false) {
213
  return getValueFromStorage(UNIT_SERVER_SPEED_BUILDING, $plain);
0 ignored issues
show
Bug introduced by
UNIT_SERVER_SPEED_BUILDING of type string is incompatible with the type integer expected by parameter $unitSnId of getValueFromStorage(). ( Ignorable by Annotation )

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

213
  return getValueFromStorage(/** @scrutinizer ignore-type */ UNIT_SERVER_SPEED_BUILDING, $plain);
Loading history...
214
}
215
216
/**
217
 * Get fleet flying speed aka... hmph... fleet flying speed
218
 *
219
 * @param bool $plain
220
 *
221
 * @return float|int
222
 */
223
function flt_server_flight_speed_multiplier($plain = false) {
224
  return getValueFromStorage(UNIT_SERVER_SPEED_FLEET, $plain);
0 ignored issues
show
Bug introduced by
UNIT_SERVER_SPEED_FLEET of type string is incompatible with the type integer expected by parameter $unitSnId of getValueFromStorage(). ( Ignorable by Annotation )

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

224
  return getValueFromStorage(/** @scrutinizer ignore-type */ UNIT_SERVER_SPEED_FLEET, $plain);
Loading history...
225
}
226
227
228
/**
229
 * Получение стоимости ММ в валюте сервера
230
 *
231
 * @param bool|false $plain
232
 *
233
 * @return mixed
234
 */
235
function get_mm_cost($plain = false) {
236
  $result = null;
237
238
  return sn_function_call('get_mm_cost', array($plain, &$result));
239
}
240
241
function sn_get_mm_cost($plain = false, &$result) {
242
  return $result = classSupernova::$config->payment_currency_exchange_mm_ ? classSupernova::$config->payment_currency_exchange_mm_ : 20000;
243
}
244
245
/**
246
 * Получение курса обмены валюты в серверную валюту
247
 *
248
 * @param $currency_symbol
249
 *
250
 * @return float
251
 */
252
function get_exchange_rate($currency_symbol) {
253
  $currency_symbol = strtolower($currency_symbol);
254
  $config_field = 'payment_currency_exchange_' . $currency_symbol;
255
256
  // Заворачиваем получение стоимости ММ через перекрываемую процедуру
257
  $exchange_rate = floatval($currency_symbol == 'mm_' ? get_mm_cost() : classSupernova::$config->$config_field);
258
259
  return $exchange_rate;
260
}
261
262
function sys_stat_get_user_skip_list() {
263
  $result = array();
264
265
  $user_skip_list = array();
266
267
  if (classSupernova::$config->stats_hide_admins) {
0 ignored issues
show
Bug Best Practice introduced by
The property stats_hide_admins does not exist on classConfig. Since you implemented __get, consider adding a @property annotation.
Loading history...
268
    $user_skip_list[] = '`authlevel` > 0';
269
  }
270
271
  if (classSupernova::$config->stats_hide_player_list) {
0 ignored issues
show
Bug Best Practice introduced by
The property stats_hide_player_list does not exist on classConfig. Since you implemented __get, consider adding a @property annotation.
Loading history...
272
    $temp = explode(',', classSupernova::$config->stats_hide_player_list);
273
    foreach ($temp as $user_id) {
274
      $user_id = floatval($user_id);
275
      if ($user_id) {
276
        $user_skip_list[] = '`id` = ' . $user_id;
277
      }
278
    }
279
  }
280
281
  if (!empty($user_skip_list)) {
282
    $user_skip_list = implode(' OR ', $user_skip_list);
283
    $user_skip_query = db_user_list($user_skip_list);
0 ignored issues
show
Deprecated Code introduced by
The function db_user_list() has been deprecated. ( Ignorable by Annotation )

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

283
    $user_skip_query = /** @scrutinizer ignore-deprecated */ db_user_list($user_skip_list);
Loading history...
284
    if (!empty($user_skip_query)) {
285
      foreach ($user_skip_query as $user_skip_row) {
286
        $result[$user_skip_row['id']] = $user_skip_row['id'];
287
      }
288
    }
289
  }
290
291
  return $result;
292
}
293
294
function market_get_autoconvert_cost() {
295
  return classSupernova::$config->rpg_cost_exchange ? classSupernova::$config->rpg_cost_exchange * 3 : 3000;
0 ignored issues
show
Bug Best Practice introduced by
The property rpg_cost_exchange does not exist on classConfig. Since you implemented __get, consider adding a @property annotation.
Loading history...
296
}
297
298
function sn_powerup_get_price_matrix($powerup_id, $powerup_unit = false, $level_max = null, $plain = false) {
299
  $result = null;
300
301
  return sn_function_call('sn_powerup_get_price_matrix', array($powerup_id, $powerup_unit, $level_max, $plain, &$result));
302
}
303
304
function sn_sn_powerup_get_price_matrix($powerup_id, $powerup_unit = false, $level_max = null, $plain = false, &$result) {
305
  global $sn_powerup_buy_discounts;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
306
307
  $result = array();
308
309
  $powerup_data = get_unit_param($powerup_id);
310
  $is_upgrade = !empty($powerup_unit) && $powerup_unit;
311
312
  $level_current = $term_original = $time_left = 0;
313
  if ($is_upgrade) {
314
    $time_finish = strtotime($powerup_unit['unit_time_finish']);
315
    $time_left = max(0, $time_finish - SN_TIME_NOW);
316
    if ($time_left > 0) {
317
      $term_original = $time_finish - strtotime($powerup_unit['unit_time_start']);
318
      $level_current = $powerup_unit['unit_level'];
319
    }
320
  }
321
322
  $level_max = $level_max > $powerup_data[P_MAX_STACK] ? $level_max : $powerup_data[P_MAX_STACK];
323
  $original_cost = 0;
324
  for ($i = 1; $i <= $level_max; $i++) {
325
    $base_cost = eco_get_total_cost($powerup_id, $i);
326
    $base_cost = $base_cost[BUILD_CREATE][RES_DARK_MATTER];
327
    foreach ($sn_powerup_buy_discounts as $period => $discount) {
328
      $upgrade_price = floor($base_cost * $discount * $period / PERIOD_MONTH);
329
      $result[$i][$period] = $upgrade_price;
330
      $original_cost = $is_upgrade && $i == $level_current && $period <= $term_original ? $upgrade_price : $original_cost;
331
    }
332
  }
333
334
  if ($is_upgrade && $time_left) {
335
    $term_original = round($term_original / PERIOD_DAY);
336
    $time_left = min(floor($time_left / PERIOD_DAY), $term_original);
337
    $cost_left = $term_original > 0 ? ceil($time_left / $term_original * $original_cost) : 0;
338
339
    array_walk_recursive($result, function (&$value) use ($cost_left) {
340
      $value -= $cost_left;
341
    });
342
  }
343
344
  return $result;
345
}
346
347
/**
348
 * @param $price_matrix_plain
349
 * @param $price_matrix_original
350
 * @param $price_matrix_upgrade
351
 * @param $user_dark_matter
352
 *
353
 * @return array
354
 *
355
 * Used in player_premium and interface_batch_operation modules
356
 */
357
function price_matrix_templatize(&$price_matrix_plain, &$price_matrix_original, &$price_matrix_upgrade, $user_dark_matter) {
358
  $prices = array();
359
  foreach ($price_matrix_original as $level_num => $level_data) {
360
    $price_per_period = array();
361
    foreach ($level_data as $period => $price) {
362
      $price_per_period[$period] = array(
363
        'PERIOD'             => $period,
364
        'PRICE_ORIGIN'       => $price,
365
        'PRICE_ORIGIN_TEXT'  => HelperString::numberFloorAndFormat($price),
366
        'PRICE_ORIGIN_CLASS' => prettyNumberGetClass($price, $user_dark_matter),
367
        'PRICE_UPGRADE'      => $price_matrix_upgrade[$level_num][$period],
368
        'PRICE_UPGRADE_TEXT' => HelperString::numberFloorAndFormat($price_matrix_upgrade[$level_num][$period]),
369
      );
370
      if (isset($price_matrix_plain[$level_num][$period])) {
371
        $price_per_period[$period] += array(
372
          'PRICE_PLAIN_PERCENT' => ceil(100 - ($price / $price_matrix_plain[$level_num][$period]) * 100),
373
          'PRICE_PLAIN'         => $price_matrix_plain[$level_num][$period],
374
          'PRICE_PLAIN_TEXT'    => HelperString::numberFloorAndFormat($price_matrix_plain[$level_num][$period]),
375
        );
376
      }
377
    }
378
379
    $prices[$level_num] = array(
380
      '.'     => array('period' => $price_per_period),
381
      'LEVEL' => $level_num,
382
    );
383
  }
384
385
  return $prices;
386
}
387
388
389
// TOOLS & UTILITIES ----------------------------------------------------------------------------------------------------------------
390
/**
391
 * Generates random string of $length symbols from $allowed_chars charset
392
 *
393
 * @param int    $length
394
 * @param string $allowed_chars
395
 *
396
 * @return string
397
 */
398
function sys_random_string($length = 16, $allowed_chars = SN_SYS_SEC_CHARS_ALLOWED) {
399
  $allowed_length = strlen($allowed_chars);
400
401
  $random_string = '';
402
  for ($i = 0; $i < $length; $i++) {
403
    $random_string .= $allowed_chars[mt_rand(0, $allowed_length - 1)];
404
  }
405
406
  return $random_string;
407
}
408
409
function array_merge_recursive_numeric($array1, $array2) {
410
  if (!empty($array2) && is_array($array2)) {
411
    foreach ($array2 as $key => $value) {
412
      $array1[$key] = !isset($array1[$key]) || !is_array($array1[$key]) ? $value : array_merge_recursive_numeric($array1[$key], $value);
413
    }
414
  }
415
416
  return $array1;
417
}
418
419
function sn_sys_array_cumulative_sum(&$array) {
420
  $accum = 0;
421
  foreach ($array as &$value) {
422
    $accum += $value;
423
    $value = $accum;
424
  }
425
}
426
427
function print_rr($var, $capture = false) {
428
  $print = '<pre>' . htmlspecialchars(print_r($var, true)) . '</pre>';
429
  if ($capture) {
430
    return $print;
431
  } else {
432
    print($print);
433
  }
434
}
435
436
/**
437
 * Returns unique string ID for total fleets on planet
438
 *
439
 * @param array $planetTemplatized
440
 *
441
 * @return int|string
442
 */
443
function getUniqueFleetId($planetTemplatized) {
444
  return empty($planetTemplatized['id']) ? 0 : sprintf(FLEET_ID_TEMPLATE, $planetTemplatized['id']);
445
}
446
447
/**
448
 * @param array $context
449
 *
450
 * @return array
451
 */
452
function getLocationFromContext($context = []) {
453
  if (!empty($context[LOC_FLEET])) {
454
    return [LOC_FLEET, $context[LOC_FLEET]['fleet_id']];
455
  } elseif (!empty($context[LOC_PLANET])) {
456
    return [LOC_PLANET, $context[LOC_PLANET]['id']];
457
  } elseif (!empty($context[LOC_USER])) {
458
    return [LOC_USER, $context[LOC_USER]['id']];
459
  } else {
460
    return [LOC_SERVER, 0];
461
  }
462
463
}
464
465
466
//
467
468
469
// MAIL ----------------------------------------------------------------------------------------------------------------
470
function mymail($email_unsafe, $title, $body, $from = '', $html = false) {
471
  $from = trim($from ? $from : classSupernova::$config->game_adminEmail);
472
473
  $head = '';
474
  $head .= "Content-Type: text/" . ($html ? 'html' : 'plain') . "; charset=utf-8 \r\n";
475
  $head .= "Date: " . date('r') . " \r\n";
476
  $head .= "Return-Path: " . classSupernova::$config->game_adminEmail . " \r\n";
477
  $head .= "From: {$from} \r\n";
478
  $head .= "Sender: {$from} \r\n";
479
  $head .= "Reply-To: {$from} \r\n";
480
//  $head .= "Organization: {$org} \r\n";
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
481
  $head .= "X-Sender: {$from} \r\n";
482
  $head .= "X-Priority: 3 \r\n";
483
484
  $body = str_replace("\r\n", "\n", $body);
485
  $body = str_replace("\n", "\r\n", $body);
486
487
  if ($html) {
488
    $body = '<html><head><base href="' . SN_ROOT_VIRTUAL . '"></head><body>' . nl2br($body) . '</body></html>';
489
  }
490
491
  $title = '=?UTF-8?B?' . base64_encode($title) . '?=';
492
493
  return @mail($email_unsafe, $title, $body, $head);
494
}
495
496
497
//
498
499
500
// VERSION FUNCTIONS ----------------------------------------------------------------------------------------------------------------
501
function sn_version_compare_extra($version) {
502
  static $version_regexp = '#(\d+)([a-f])(\d+)(?:\.(\d+))*#';
503
  preg_match($version_regexp, $version, $version);
504
  unset($version[0]);
505
  $version[2] = ord($version[2]) - ord('a');
506
507
  return implode('.', $version);
508
}
509
510
function sn_version_compare($ver1, $ver2) {
511
  return version_compare(sn_version_compare_extra($ver1), sn_version_compare_extra($ver2));
512
}
513