Completed
Push — work-fleets ( 874fb8...41da5d )
by SuperNova.WS
06:44
created

eco_queue.php ➔ que_delete()   C

Complexity

Conditions 11
Paths 160

Size

Total Lines 53
Code Lines 32

Duplication

Lines 5
Ratio 9.43 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 11
eloc 32
c 3
b 1
f 0
nc 160
nop 4
dl 5
loc 53
rs 5.8259

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
function que_get_unit_que($unit_id) {
4
  $que_type = false;
5
  foreach(sn_get_groups('ques') as $que_id => $que_data) {
6
    if(in_array($unit_id, $que_data['unit_list'])) {
7
      $que_type = $que_id;
8
      break;
9
    }
10
  }
11
12
  return $que_type;
13
}
14
15
16
function que_get_max_que_length($user, $planet, $que_id, $que_data = null) {
17
  if(empty($que_data)) {
18
    $que_data = sn_get_groups('ques');
19
    $que_data = $que_data[$que_id];
20
  }
21
22
23
  $que_length = 1;
0 ignored issues
show
Unused Code introduced by
$que_length 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...
24
  switch($que_id) {
25
    case QUE_RESEARCH:
26
      $que_length = classSupernova::$config->server_que_length_research + mrc_get_level($user, null, UNIT_PREMIUM); // TODO - вынести в модуль
27
    break;
28
29
    default:
30
      $que_length = isset($que_data['length']) ? $que_data['length'] + mrc_get_level($user, $planet, $que_data['mercenary']) : 1;
31
  }
32
33
  return $que_length;
34
}
35
36
function eco_que_str2arr($que_str) {
37
  $que_arr = explode(';', $que_str);
38
  foreach($que_arr as $que_index => &$que_item) {
39
    if($que_item) {
40
      $que_item = explode(',', $que_item);
41
    } else {
42
      unset($que_arr[$que_index]);
43
    }
44
  }
45
46
  return $que_arr;
47
}
48
49
function eco_que_arr2str($que_arr) {
50
  foreach($que_arr as &$que_item) {
51
    $que_item = implode(',', $que_item);
52
  }
53
54
  return implode(';', $que_arr);
55
}
56
57
58
function que_build($user, $planet, $build_mode = BUILD_CREATE, $redirect = true) {
59
  $classLocale = classLocale::$lang;
60
61
  $is_autoconvert = false;
62
  if($build_mode == BUILD_AUTOCONVERT || sys_get_param_int('auto_convert')) {
63
    $build_mode = BUILD_CREATE;
64
    $is_autoconvert = true;
65
  }
66
67
  $unit_amount_qued = 0;
68
  try {
69
    if(!$user['id']) {
70
      throw new exception('{Нет идентификатора пользователя - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION
71
    }
72
73
    $unit_id = sys_get_param_int('unit_id');
74
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
63% 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...
75
    if(!$unit_id && is_array($unit_list = sys_get_param('fmenge')))
76
    {
77
      foreach($unit_list as $unit_id => $unit_amount) if($unit_amount) break;
78
    }
79
    */
80
    if(!$unit_id) {
81
      throw new exception('{Нет идентификатора юнита - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION
82
    }
83
84
    $que_id = que_get_unit_que($unit_id);
85
    if(!$que_id) {
86
      throw new exception('{Неправильный тип очереди - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION
87
    }
88
89
    if($build_mode == BUILD_DESTROY && $que_id != QUE_STRUCTURES) {
90
      throw new exception('{Уничтожать можно только здания на планете}', ERR_ERROR); // TODO EXCEPTION
91
    }
92
93
    $que_data = sn_get_groups('ques');
94
    $que_data = $que_data[$que_id];
95
96
    // TODO Переделать под подочереди
97
    if($que_id == QUE_STRUCTURES) {
98
      $sn_groups_build_allow = sn_get_groups('build_allow');
99
      $que_data['unit_list'] = $sn_groups_build_allow[$planet['planet_type']];
100
101
      if(!isset($que_data['unit_list'][$unit_id])) {
102
        throw new exception('{Это здание нельзя строить на ' . ($planet['planet_type'] == PT_PLANET ? 'планете' : 'луне'), ERR_ERROR); // TODO EXCEPTION
103
      }
104
    }
105
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
106
    // TODO Разделить очереди для Верфи и Обороны
107
    elseif($que_id == QUE_HANGAR)
108
    {
109
      $que_data['mercenary'] = in_array($unit_id, sn_get_groups('defense')) ? MRC_FORTIFIER : MRC_ENGINEER;
110
    }
111
    elseif($que_id == QUE_HANGAR)
112
    {
113
      $que_data['mercenary'] = in_array($unit_id, sn_get_groups('defense')) ? MRC_FORTIFIER : MRC_ENGINEER;
114
    }
115
    */
116
117
118
    sn_db_transaction_start();
119
    // Это нужно, что бы заблокировать пользователя и работу с очередями
120
    $user = DBStaticUser::db_user_by_id($user['id']);
121
    // Это нужно, что бы заблокировать планету от списания ресурсов
122
    if(isset($planet['id']) && $planet['id']) {
123
      $planet = DBStaticPlanet::db_planet_by_id($planet['id'], true);
124
    } else {
125
      $planet['id'] = 0;
126
    }
127
128
    $planet_id = $que_id == QUE_RESEARCH ? 0 : intval($planet['id']);
129
130
    $que = que_get($user['id'], $planet['id'], $que_id, true);
0 ignored issues
show
Documentation introduced by
$que_id is of type integer|string, but the function expects a boolean.

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...
131
    $in_que = &$que['in_que'][$que_id][$user['id']][$planet_id];
132
    $que_max_length = que_get_max_que_length($user, $planet, $que_id, $que_data);
133
    // TODO Добавить вызовы функций проверок текущей и максимальной длин очередей
134
    if(count($in_que) >= $que_max_length) {
135
      throw new exception('{Все слоты очереди заняты}', ERR_ERROR); // TODO EXCEPTION
136
    }
137
138
    // TODO Отдельно посмотреть на уничтожение зданий - что бы можно было уничтожать их без планов
139
    switch(eco_can_build_unit($user, $planet, $unit_id)) {
140
      case BUILD_ALLOWED:
141
      break;
142
      case BUILD_UNIT_BUSY:
143
        throw new exception('{Строение занято}', ERR_ERROR);
144
      break; // TODO EXCEPTION eco_bld_msg_err_laboratory_upgrading
0 ignored issues
show
Unused Code introduced by
break; 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...
145
      // case BUILD_REQUIRE_NOT_MEET:
146
      default:
147
        if($build_mode == BUILD_CREATE) {
148
          throw new exception('{Требования не удовлетворены}', ERR_ERROR);
149
        }
150
      break; // TODO EXCEPTION eco_bld_msg_err_requirements_not_meet
151
    }
152
153
    $unit_amount = floor(sys_get_param_float('unit_amount', 1));
154
    $unit_amount_qued = $unit_amount;
155
    $units_qued = isset($in_que[$unit_id]) ? $in_que[$unit_id] : 0;
156
    $unit_level = mrc_get_level($user, $planet, $unit_id, true, true) + $units_qued;
157
    if($unit_max = get_unit_param($unit_id, P_MAX_STACK)) {
158
      if($unit_level >= $unit_max) {
159
        throw new exception('{Максимальное количество юнитов данного типа уже достигнуто или будет достигнуто по окончанию очереди}', ERR_ERROR); // TODO EXCEPTION
160
      }
161
      $unit_amount = max(0, min($unit_amount, $unit_max - $unit_level));
162
    }
163
164
    if($unit_amount < 1) {
165
      throw new exception('{Неправильное количество юнитов - сообщите Администрации}', ERR_ERROR); // TODO EXCEPTION
166
    }
167
168
    // TODO Переделать eco_unit_busy для всех типов зданий
169
    //  if(eco_unit_busy($user, $planet, $que, $unit_id))
0 ignored issues
show
Unused Code Comprehensibility introduced by
71% 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...
170
    //  {
171
    //    die('Unit busy'); // TODO EXCEPTION
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
172
    //  }
173
    if(get_unit_param($unit_id, P_STACKABLE)) {
174
      // TODO Поле 'max_Lot_size' для ограничения размера стэка в очереди - то ли в юниты, то ли в очередь
175
      if(in_array($unit_id, $group_missile = sn_get_groups(GROUP_STR_MISSILES))) {
176
        // TODO Поле 'container' - указывает на родительску структуру, в которой хранится данный юнит и по вместительности которой нужно применять размер юнита
177
        $used_silo = 0;
178 View Code Duplication
        foreach($group_missile as $missile_id) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
          $missile_qued = isset($in_que[$missile_id]) ? $in_que[$missile_id] : 0;
180
          $used_silo += (mrc_get_level($user, $planet, $missile_id, true, true) + $missile_qued) * get_unit_param($missile_id, P_UNIT_SIZE);
181
        }
182
        $free_silo = mrc_get_level($user, $planet, STRUC_SILO) * get_unit_param(STRUC_SILO, P_CAPACITY) - $used_silo;
183
        if($free_silo <= 0) {
184
          throw new exception('{Ракетная шахта уже заполнена или будет заполнена по окончанию очереди}', ERR_ERROR); // TODO EXCEPTION
185
        }
186
        $unit_size = get_unit_param($unit_id, P_UNIT_SIZE);
187
        if($free_silo < $unit_size) {
188
          throw new exception("{В ракетной шахте нет места для {$classLocale['tech'][$unit_id]}}", ERR_ERROR); // TODO EXCEPTION
189
        }
190
        $unit_amount = max(0, min($unit_amount, floor($free_silo / $unit_size)));
191
      }
192
      $unit_level = $new_unit_level = 0;
193
    } else {
194
      $unit_amount = 1;
195
      if($que_id == QUE_STRUCTURES) {
196
        // if($build_mode == BUILD_CREATE && eco_planet_fields_max($planet) - $planet['field_current'] - $que['sectors'][$planet['id']] <= 0)
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
197
        $sectors_qued = is_array($in_que) ? array_sum($in_que) : 0;
198
        if($build_mode == BUILD_CREATE && eco_planet_fields_max($planet) - $planet['field_current'] - $sectors_qued <= 0) {
199
          throw new exception('{Не хватает секторов на планете}', ERR_ERROR); // TODO EXCEPTION
200
        }
201
        // И что это я такое написал? Зачем?
202
        //if($build_mode == BUILD_DESTROY && $planet['field_current'] <= $que['amounts'][$que_id])
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% 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...
203
        //{
204
        //  die('Too much buildings'); // TODO EXCEPTION
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% 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...
205
        //}
206
      }
207
      $build_multiplier = $build_mode == BUILD_CREATE ? 1 : -1;
208
      $new_unit_level = $unit_level + $unit_amount * $build_multiplier;
209
    }
210
211
    $build_data = eco_get_build_data($user, $planet, $unit_id, $unit_level);
212
213
    $exchange = array();
214
    $market_get_autoconvert_cost = market_get_autoconvert_cost();
215
    if($is_autoconvert && $build_data[BUILD_AUTOCONVERT]) {
216
      $dark_matter = mrc_get_level($user, null, RES_DARK_MATTER);
217
      if(mrc_get_level($user, null, RES_DARK_MATTER) < $market_get_autoconvert_cost) {
218
        throw new exception("{Нет хватает " . ($market_get_autoconvert_cost - $dark_matter) . "ТМ на постройки с автоконвертацией ресурсов}", ERR_ERROR); // TODO EXCEPTION
219
      }
220
221
      !get_unit_param($unit_id, P_STACKABLE) ? $unit_amount = 1 : false;
222
      $resources_loot = sn_get_groups('resources_loot');
223
      $resource_got = array();
224
      $resource_exchange_rates = array();
225
      $resource_diff = array();
226
      $all_positive = true;
227
      foreach($resources_loot as $resource_id) {
228
        $resource_db_name = pname_resource_name($resource_id);
229
        $resource_got[$resource_id] = floor(mrc_get_level($user, $planet, $resource_id));
230
        $resource_exchange_rates[$resource_id] = classSupernova::$config->__get("rpg_exchange_{$resource_db_name}");
231
        $resource_diff[$resource_id] = $resource_got[$resource_id] - $build_data[BUILD_CREATE][$resource_id] * $unit_amount;
232
        $all_positive = $all_positive && ($resource_diff[$resource_id] > 0);
233
      }
234
      // Нужна автоконвертация
235
      if($all_positive) {
236
        $is_autoconvert = false;
237
      } else {
238
        foreach($resource_diff as $resource_diff_id => &$resource_diff_amount) {
239
          if($resource_diff_amount >= 0) {
240
            continue;
241
          }
242
          foreach($resource_diff as $resource_got_id => &$resource_got_amount) {
243
            if($resource_got_amount <= 0) {
244
              continue;
245
            }
246
            $current_exchange = $resource_exchange_rates[$resource_got_id] / $resource_exchange_rates[$resource_diff_id];
247
248
            $will_exchage_to = min(-$resource_diff_amount, floor($resource_got_amount * $current_exchange));
249
            $will_exchage_from = $will_exchage_to / $current_exchange;
250
251
            $resource_diff_amount += $will_exchage_to;
252
            $resource_got_amount -= $will_exchage_from;
253
            $exchange[$resource_diff_id] += $will_exchage_to;
254
            $exchange[$resource_got_id] -= $will_exchage_from;
255
          }
256
        }
257
258
        $is_autoconvert_ok = true;
259
        foreach($resource_diff as $resource_diff_amount2) {
260
          if($resource_diff_amount2 < 0) {
261
            $is_autoconvert_ok = false;
262
            break;
263
          }
264
        }
265
266
        if($is_autoconvert_ok) {
267
          $build_data['RESULT'][$build_mode] = BUILD_ALLOWED;
268
          $build_data['CAN'][$build_mode] = $unit_amount;
269
        } else {
270
          $unit_amount = 0;
271
        }
272
      }
273
    }
274
    $unit_amount = min($build_data['CAN'][$build_mode], $unit_amount);
275
    if($unit_amount <= 0) {
276
      throw new exception('{Не хватает ресурсов}', ERR_ERROR); // TODO EXCEPTION
277
    }
278
279
    if($new_unit_level < 0) {
280
      throw new exception('{Нельзя уничтожить больше юнитов, чем есть}', ERR_ERROR); // TODO EXCEPTION
281
    }
282
283
    if($build_data['RESULT'][$build_mode] != BUILD_ALLOWED) {
284
      throw new exception('{Строительство блокировано}', ERR_ERROR); // TODO EXCEPTION
285
    }
286
287
    if($is_autoconvert) {
288
      ksort($exchange);
289
      ksort($resource_got);
290
      db_change_units(
291
        $user,
292
        $planet,
293
        array(
294
          RES_METAL     => !empty($exchange[RES_METAL]) ? $exchange[RES_METAL] : 0,
295
          RES_CRYSTAL   => !empty($exchange[RES_CRYSTAL]) ? $exchange[RES_CRYSTAL] : 0,
296
          RES_DEUTERIUM => !empty($exchange[RES_DEUTERIUM]) ? $exchange[RES_DEUTERIUM] : 0,
297
        )
298
      );
299
      rpg_points_change($user['id'], RPG_BUILD_AUTOCONVERT, -$market_get_autoconvert_cost, sprintf(
300
        classLocale::$lang['bld_autoconvert'], $unit_id, $unit_amount, uni_render_planet_full($planet, '', false, true), classLocale::$lang['tech'][$unit_id],
301
        sys_unit_arr2str($build_data[BUILD_CREATE]), sys_unit_arr2str($resource_got), sys_unit_arr2str($exchange)
302
      ));
303
    }
304
305
    $unit_amount_qued = 0;
306
    while($unit_amount > 0 && count($que['ques'][$que_id][$user['id']][$planet_id]) < $que_max_length) {
307
      $place = min($unit_amount, MAX_FLEET_OR_DEFS_PER_ROW);
308
      que_add_unit($unit_id, $user, $planet, $build_data, $new_unit_level, $place, $build_mode);
309
      $unit_amount -= $place;
310
      $que = que_get($user['id'], $planet['id'], $que_id, true);
0 ignored issues
show
Documentation introduced by
$que_id is of type integer|string, but the function expects a boolean.

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...
311
      $unit_amount_qued += $place;
312
    }
313
314
    sn_db_transaction_commit();
315
316
    if($redirect) {
317
      sys_redirect("{$_SERVER['PHP_SELF']}?mode=" . sys_get_param_str('mode') . "&ally_id=" . sys_get_param_id('ally_id'));
318
      die();
319
    }
320
321
    $operation_result = array(
322
      'STATUS'  => ERR_NONE,
323
      'MESSAGE' => '{Строительство начато}',
324
    );
325
  } catch(exception $e) {
326
    sn_db_transaction_rollback();
327
    $operation_result = array(
328
      'STATUS'  => in_array($e->getCode(), array(ERR_NONE, ERR_WARNING, ERR_ERROR)) ? $e->getCode() : ERR_ERROR,
329
      'MESSAGE' => $e->getMessage()
330
    );
331
  }
332
333
  if(!empty($operation_result['MESSAGE'])) {
334
    $operation_result['MESSAGE'] .= ' ' . ($unit_amount_qued ? $unit_amount_qued : $unit_amount) . 'x[' . classLocale::$lang['tech'][$unit_id] . ']';
0 ignored issues
show
Bug introduced by
The variable $unit_amount does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $unit_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
335
  }
336
337
  return $operation_result;
338
}
339
340
341
function que_recalculate($old_que) {
342
  $new_que = array();
343
344
  if(!is_array($old_que['items'])) {
345
    return $new_que;
346
  }
347
  foreach($old_que['items'] as $row) {
348
    if(!isset($row) || !$row || $row['que_unit_amount'] <= 0) {
349
      continue;
350
    }
351
352
    $new_que['items'][] = $row;
353
354
    $new_que['in_que'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][$row['que_unit_id']] += $row['que_unit_amount'] * ($row['que_unit_mode'] == BUILD_CREATE ? 1 : -1);
355
    $new_que['in_que_abs'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][$row['que_unit_id']] += $row['que_unit_amount'];
356
357
    $last_id = count($new_que['items']) - 1;
358
359
    if($row['que_planet_id']) {
360
      $new_que['planets'][$row['que_planet_id']][$row['que_type']][] = &$new_que['items'][$last_id];
361
    } elseif($row['que_type'] == QUE_RESEARCH) {
362
      $new_que['players'][$row['que_player_id']][$row['que_type']][] = &$new_que['items'][$last_id];
363
    }
364
    $new_que['ques'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][] = &$new_que['items'][$last_id];
365
366
    // Это мы можем посчитать по длине очереди в players и planets
367
    //$ques['used_slots'][$row['que_type']][$row['que_player_id']][intval($row['que_planet_id'])][$row['que_unit_id']]++;
0 ignored issues
show
Unused Code Comprehensibility introduced by
97% 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...
368
  }
369
370
  return $new_que;
371
}
372
373
function que_get($user_id, $planet_id = null, $que_type = false, $for_update = false) {
374
  return DBStaticQue::db_que_list_by_type_location($user_id, $planet_id, $que_type, $for_update);
375
}
376
377
function que_add_unit($unit_id, $user = array(), $planet = array(), $build_data, $unit_level = 0, $unit_amount = 1, $build_mode = BUILD_CREATE) {
378
  // TODO Унифицировать проверки
379
380
  // TODO que_process() тут
381
382
  sn_db_transaction_check(true);
383
384
  $build_mode = $build_mode == BUILD_CREATE ? BUILD_CREATE : BUILD_DESTROY;
385
386
  // TODO: Some checks
387
  db_change_units(
388
    $user,
389
    $planet,
390
    array(
391
      RES_METAL     => -$build_data[$build_mode][RES_METAL] * $unit_amount,
392
      RES_CRYSTAL   => -$build_data[$build_mode][RES_CRYSTAL] * $unit_amount,
393
      RES_DEUTERIUM => -$build_data[$build_mode][RES_DEUTERIUM] * $unit_amount,
394
    )
395
  );
396
397
  $que_type = que_get_unit_que($unit_id);
398
  $planet_id_origin = $planet['id'] ? $planet['id'] : 'NULL';
399
  $planet_id = $que_type == QUE_RESEARCH ? 'NULL' : $planet_id_origin;
400 View Code Duplication
  if(is_numeric($planet_id)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
401
    DBStaticPlanet::db_planet_update_set_by_id($planet_id, "`que_processed` = UNIX_TIMESTAMP(NOW())");
402
  } elseif(is_numeric($user['id'])) {
403
    DBStaticUser::db_user_set_by_id($user['id'], '`que_processed` = UNIX_TIMESTAMP(NOW())');
404
  }
405
406
  $resource_list = sys_unit_arr2str($build_data[$build_mode]);
407
408
  DBStaticQue::db_que_set_insert(array(
409
    'que_player_id'        => $user['id'],
410
    'que_planet_id'        => $planet_id,
411
    'que_planet_id_origin' => $planet_id_origin,
412
    'que_type'             => $que_type,
413
    'que_time_left'        => $build_data[RES_TIME][$build_mode],
414
    'que_unit_id'          => $unit_id,
415
    'que_unit_amount'      => $unit_amount,
416
    'que_unit_mode'        => $build_mode,
417
    'que_unit_level'       => $unit_level,
418
    'que_unit_time'        => $build_data[RES_TIME][$build_mode],
419
    'que_unit_price'       => $resource_list,
420
  ));
421
}
422
423
function que_delete($que_type, $user = array(), $planet = array(), $clear = false) {
424
  $planets_locked = array();
425
426
  // TODO: Some checks
427
  sn_db_transaction_start();
428
  $user = DBStaticUser::db_user_by_id($user['id'], true);
429
  $planet['id'] = $planet['id'] && $que_type !== QUE_RESEARCH ? $planet['id'] : 0;
430
  $global_que = que_get($user['id'], $planet['id'], $que_type, true);
431
432
  if(!empty($global_que['ques'][$que_type][$user['id']][$planet['id']])) {
433
    $que = array_reverse($global_que['ques'][$que_type][$user['id']][$planet['id']]);
434
435
    foreach($que as $que_item) {
436
      DBStaticQue::db_que_delete_by_id($que_item['que_id']);
437
438
      if($que_item['que_planet_id_origin']) {
439
        $planet['id'] = $que_item['que_planet_id_origin'];
440
      }
441
442
      if(!isset($planets_locked[$planet['id']])) {
443
        $planets_locked[$planet['id']] = $planet['id'] ? DBStaticPlanet::db_planet_by_id($planet['id'], true) : $planet;
444
      }
445
446
      $build_data = sys_unit_str2arr($que_item['que_unit_price']);
447
448
      db_change_units(
449
        $user,
450
        $planets_locked[$planet['id']],
451
        array(
452
          RES_METAL     => $build_data[RES_METAL] * $que_item['que_unit_amount'],
453
          RES_CRYSTAL   => $build_data[RES_CRYSTAL] * $que_item['que_unit_amount'],
454
          RES_DEUTERIUM => $build_data[RES_DEUTERIUM] * $que_item['que_unit_amount'],
455
        )
456
      );
457
458
      if(!$clear) {
459
        break;
460
      }
461
    }
462
463 View Code Duplication
    if(is_numeric($planet['id'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
464
      DBStaticPlanet::db_planet_update_set_by_id($planet['id'], "`que_processed` = UNIX_TIMESTAMP(NOW())");
465
    } elseif(is_numeric($user['id'])) {
466
      DBStaticUser::db_user_set_by_id($user['id'], '`que_processed` = UNIX_TIMESTAMP(NOW())');
467
    }
468
469
    sn_db_transaction_commit();
470
  } else {
471
    sn_db_transaction_rollback();
472
  }
473
//die();
474
  header("Location: {$_SERVER['PHP_SELF']}?mode={$que_type}" . "&ally_id=" . sys_get_param_id('ally_id'));
0 ignored issues
show
Security Response Splitting introduced by
"Location: {$_SERVER['PH...get_param_id('ally_id') can contain request data and is used in response header context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST
    in includes/general.php on line 258
  2. sys_get_param() returns tainted data, and $value is assigned
    in includes/general.php on line 266
  3. sys_get_param_id() returns tainted data
    in includes/functions/eco_queue.php on line 474

Response Splitting Attacks

Allowing an attacker to set a response header, opens your application to response splitting attacks; effectively allowing an attacker to send any response, he would like.

General Strategies to prevent injection

In 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;
Loading history...
475
}
476
477
478
function que_tpl_parse_element($que_element, $short_names = false) {
479
  return
480
    array(
481
      'ID'        => $que_element['que_unit_id'],
482
      'QUE'       => $que_element['que_type'],
483
      'NAME'      => $short_names && !empty(classLocale::$lang['tech_short'][$que_element['que_unit_id']])
484
        ? classLocale::$lang['tech_short'][$que_element['que_unit_id']]
485
        : classLocale::$lang['tech'][$que_element['que_unit_id']],
486
      'TIME'      => $que_element['que_time_left'],
487
      'TIME_FULL' => $que_element['que_unit_time'],
488
      'AMOUNT'    => $que_element['que_unit_amount'],
489
      'LEVEL'     => $que_element['que_unit_level'],
490
    );
491
}
492
493
/**
494
 * Процедура парсит очереди текущего игрока в темплейт
495
 *
496
 * TODO: Переместить в хелперы темплейтов
497
 * TODO: Сделать поддержку нескольких очередей
498
 *
499
 * $que_type - тип очереди ОБЯЗАТЕЛЬНО
500
 * $que - либо результат $que_get(), либо конкретная очередь
501
 *
502
 * @param template $template
503
 * @param          $que_type
504
 * @param          $user
505
 * @param array    $planet
506
 * @param null     $que
507
 * @param bool     $short_names
508
 */
509
function que_tpl_parse(&$template, $que_type, $user, $planet = array(), $que = null, $short_names = false) {
510
  // TODO: Переделать для $que_type === false
511
  $planet['id'] = $planet['id'] ? $planet['id'] : 0;
512
513
  if(!is_array($que)) {
514
    $que = que_get($user['id'], $planet['id'], $que_type);
515
  }
516
517
  if(is_array($que) && isset($que['items'])) {
518
    $que = $que['ques'][$que_type][$user['id']][$planet['id']];
519
  }
520
521
  if($que) {
522
    foreach($que as $que_element) {
523
      $template->assign_block_vars('que', que_tpl_parse_element($que_element, $short_names));
524
    }
525
  }
526
527
  if($que_type == QUE_RESEARCH) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
528
  }
529
}
530
531
532
/*
533
 *
534
 * Эта процедура должна вызываться исключительно в транзакции!!!
535
 *
536
 * $user_id
537
 *   (integer) - обрабатываются глообальные очереди пользователя
538
 * $planet_id
539
 *   null - обработка очередей планет не производится
540
 * // TODO    false/0 - обрабатываются очереди всех планет по $user_id
541
 *   (integer) - обрабатываются локальные очереди для планеты. Нужно, например, в обработчике флотов
542
 *
543
 */
544
function que_process(&$user, $planet = null, $on_time = SN_TIME_NOW) {
545
  sn_db_transaction_check(true);
546
547
  $que = array();
548
549
  // Блокируем пользователя. Собственно, запись о нём нам не нужна - будем использовать старую
550
  $user = DBStaticUser::db_user_by_id($user['id'], true);
551
552
  $time_left[$user['id']][0] = max(0, $on_time - $user['que_processed']);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$time_left was never initialized. Although not strictly required by PHP, it is generally a good practice to add $time_left = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
553
  if($planet === null && !$time_left[$user['id']][0]) {
554
    // TODO
555
    return $que;
556
  }
557
558
  // Определяем, какие очереди нам нужны и получаем их
559
  $que_type_id = $planet === null ? QUE_RESEARCH : false;
560
  $planet = intval(is_array($planet) ? $planet['id'] : $planet); // В $planet у нас теперь только её ID или шаблон null/0/false
561
  $que = que_get($user['id'], $planet, $que_type_id, true);
0 ignored issues
show
Bug introduced by
It seems like $que_type_id defined by $planet === null ? QUE_RESEARCH : false on line 559 can also be of type integer; however, que_get() does only seem to accept boolean, 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...
562
  if(empty($que['items'])) {
563
    return $que;
564
  }
565
566
  $planet_list = array();
567
  if($planet !== null) {
568
    // Если нужно изменять данные на планетах - блокируем планеты и получаем данные о них
569
    // TODO - от них не надо ничего, кроме ID и que_processed
570
    $planet_row = DBStaticPlanet::db_planet_list_by_user_or_planet($user['id'], $planet);
571
    $planet_list[$planet_row['id']] = $planet_row;
572
    $time_left[$planet_row['id_owner']][$planet_row['id']] = max(0, $on_time - $planet_row['que_processed']);
573
  }
574
575
  // Теперь в $time_left лежит время обсчета всех очередей по каждой из планеты
576
  if(array_sum($time_left[$user['id']]) == 0) {
577
    return $que;
578
  }
579
580
  $unit_changes = array();
581
  foreach($que['items'] as &$que_item) {
582
    $que_player_id = &$que_item['que_player_id'];
583
    $que_planet_id = intval($que_item['que_planet_id']);
584
585
    $que_time_left = &$que['time_left'][$que_player_id][$que_planet_id][$que_item['que_type']];
586
    if(!isset($que_time_left)) {
587
      $que_time_left = $time_left[$que_player_id][$que_planet_id];
588
    }
589
    if($que_time_left <= 0 || $que_item['que_unit_amount'] <= 0) {
590
      continue;
591
    }
592
    // Дальше мы идем, если только осталось время в очереди И юниты к постройке
593
594
    // Вычисляем, сколько целых юнитов будет построено - от 0 до количества юнитов в очереди
595
    $unit_processed = min($que_item['que_unit_amount'] - 1, floor($que_time_left / $que_item['que_unit_time']));
596
    // Вычитаем это время из остатков
597
    $que_time_left -= $unit_processed * $que_item['que_unit_time'];
598
599
    // Теперь работаем с остатком времени на юните. Оно не может быть равно или меньше нуля
600
601
    // Если времени в очереди осталось не меньше, чем время текущего юнита - значит мы достроили юнит
602
    if($que_time_left >= $que_item['que_time_left']) {
603
      // Увеличиваем количество отстроенных юнитов
604
      $unit_processed++;
605
      // Вычитаем из времени очереди потраченное на постройку время
606
      $que_time_left -= $que_item['que_time_left'];
607
      // Полное время юнита равно времени нового юнита
608
      $que_item['que_time_left'] = $que_item['que_unit_time'];
609
      // Тут у нас может остатся время очереди - если постройка была не последняя
610
    }
611
    // Изменяем количество оставшихся юнитов
612
    $que_item['que_unit_amount'] -= $unit_processed;
613
614
    // Если еще остались юниты - значит ВСЁ оставшееся время приходится на достройку следующего юнита
615
    if($que_item['que_unit_amount'] > 0) {
616
      $que_item['que_time_left'] = $que_item['que_time_left'] - $que_time_left;
617
      $que_time_left = 0;
618
    }
619
620
    if($que_item['que_unit_amount'] <= 0) {
621
      DBStaticQue::db_que_delete_by_id($que_item['que_id']);
622
    } else {
623
      classSupernova::$gc->cacheOperator->db_upd_record_list(
0 ignored issues
show
Bug introduced by
The method db_upd_record_list does only exist in SnDbCachedOperator, but not in Closure.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
624
        LOC_QUE,
625
        "`que_unit_amount` = `que_unit_amount` - ({$unit_processed}), `que_time_left` = {$que_item['que_time_left']}",
626
        "`que_id` = {$que_item['que_id']}"
627
      );
628
    }
629
630
    if($unit_processed) {
631
      $unit_processed_delta = $unit_processed * ($que_item['que_unit_mode'] == BUILD_CREATE ? 1 : -1);
632
      $unit_changes[$que_player_id][$que_planet_id][$que_item['que_unit_id']] += $unit_processed_delta;
633
    }
634
  }
635
636
  foreach($time_left as $player_id => $planet_data) {
637
    foreach($planet_data as $planet_id => $time_on_planet) {
638
      if($planet_id) {
639
        // update planet
640
        classSupernova::$gc->cacheOperator->db_upd_record_list(LOC_PLANET, "`que_processed` = {$on_time}", "id = {$planet_id}");
641
      } else {
642
        // update user
643
        classSupernova::$gc->cacheOperator->db_upd_record_list(LOC_USER, "`que_processed` = {$on_time}", "id = {$player_id}");
644
      }
645
646
      if(is_array($unit_changes[$player_id][$planet_id])) {
647
        foreach($unit_changes[$player_id][$planet_id] as $unit_id => $unit_amount) {
648
          DBStaticUnit::dbUpdateOrInsertUnit($unit_id, $unit_amount, $user, $planet_id ? $planet_id : null);
0 ignored issues
show
Bug introduced by
It seems like $planet_id ? $planet_id : null can also be of type integer or string; however, DBStaticUnit::dbUpdateOrInsertUnit() does only seem to accept 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...
649
        }
650
      }
651
    }
652
  }
653
654
  $que = que_recalculate($que);
655
656
  // TODO: Re-enable quests for Alliances
657
  if(!empty($unit_changes) && !$user['user_as_ally']) {
658
    $quest_list = qst_get_quests($user['id']);
659
    $quest_triggers = qst_active_triggers($quest_list);
660
    $quest_rewards = array();
661
662
663
    $xp_incoming = array();
664
    foreach($unit_changes as $user_id => $planet_changes) {
665
      foreach($planet_changes as $planet_id => $changes) {
666
        $planet_this = $planet_id ? classSupernova::$gc->cacheOperator->db_get_record_by_id(LOC_PLANET, $planet_id) : array();
0 ignored issues
show
Bug introduced by
The method db_get_record_by_id does only exist in SnDbCachedOperator, but not in Closure.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
667
        foreach($changes as $unit_id => $unit_value) {
668
          $que_id = que_get_unit_que($unit_id);
669
          $unit_level_new = mrc_get_level($user, $planet_this, $unit_id, false, true) + $unit_value;
670
          if($que_id == QUE_STRUCTURES || $que_id == QUE_RESEARCH) {
671
            $build_data = eco_get_build_data($user, $planet_this, $unit_id, $unit_level_new - 1);
672
            $build_data = $build_data[BUILD_CREATE];
673
            foreach(sn_get_groups('resources_loot') as $resource_id) {
674
              $xp_incoming[$que_id] += $build_data[$resource_id]; // TODO - добавить конверсию рейтов обмена
675
            }
676
          }
677
678
          if(is_array($quest_triggers)) {
679
            // TODO: Check mutiply condition quests
680
            $quest_trigger_list = array_keys($quest_triggers, $unit_id);
681
            if(is_array($quest_trigger_list)) {
682
              foreach($quest_trigger_list as $quest_id) {
683
                $quest_unit_level = $unit_level_new;
684
                if(get_unit_param($unit_id, P_UNIT_TYPE) == UNIT_SHIPS) {
685
                  $quest_unit_level = DBStaticUnit::db_unit_count_by_user_and_type_and_snid($user_id, 0, $unit_id);
686
                  $quest_unit_level = $quest_unit_level[$unit_id]['qty'];
687
                }
688
                if($quest_list[$quest_id]['quest_status_status'] != QUEST_STATUS_COMPLETE && $quest_list[$quest_id]['quest_unit_amount'] <= $quest_unit_level) {
689
                  $quest_rewards[$quest_id][$user_id][$planet_id] = $quest_list[$quest_id]['quest_rewards_list'];
690
                  $quest_list[$quest_id]['quest_status_status'] = QUEST_STATUS_COMPLETE;
691
                }
692
              }
693
            }
694
          }
695
        }
696
      }
697
    }
698
    // TODO: Изменить начисление награды за квесты на ту планету, на которой происходил ресеч
699
    qst_reward($user, $quest_rewards, $quest_list);
700
701
    foreach($xp_incoming as $que_id => $xp) {
702
      rpg_level_up($user, $que_id == QUE_RESEARCH ? RPG_TECH : RPG_STRUCTURE, $xp / 1000);
703
    }
704
  }
705
706
  // TODO Сообщения о постройке
707
708
  return $que;
709
}
710