req_to_xml()   F
last analyzed

Complexity

Conditions 22
Paths > 20000

Size

Total Lines 84
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 50
nc 49216
nop 2
dl 0
loc 84
rs 0
c 0
b 0
f 0

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
// This file is part of BOINC.
3
// http://boinc.berkeley.edu
4
// Copyright (C) 2020 University of California
5
//
6
// BOINC is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License
8
// as published by the Free Software Foundation,
9
// either version 3 of the License, or (at your option) any later version.
10
//
11
// BOINC is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU Lesser General Public License for more details.
15
//
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
// This file contains a PHP binding of a web-service interface
20
// for submitting jobs to a BOINC server.
21
//
22
// Functions:
23
//      boinc_abort_batch(): abort a batch
24
//      boinc_estimate_batch(); estimate completion time of a batch
25
//      boinc_get_output_file(): get the URL for an output file
26
//      boinc_get_output_files(): get the URL for zipped batch output
27
//      boinc_query_batch(): get details of a batch
28
//      boinc_query_batches(): get list of batches
29
//      boinc_query_job(): get details of a job
30
//      boinc_retire_batch(): retire a batch; delete output files
31
//      boinc_submit_batch(): submit a batch
32
//      boinc_set_timeout($x): set RPC timeout to X seconds
33
//
34
//  See https://github.com/BOINC/boinc/wiki/RemoteJobs#php-interface
35
36
//// Implementation stuff follows
37
38
// Convert a request message from PHP object to XML string
39
//
40
function req_to_xml($req, $op) {
41
    if (!isset($req->batch_name)) {
42
        $req->batch_name = "batch_".time();
43
    }
44
    $x = "<$op>
45
    <authenticator>$req->authenticator</authenticator>
46
    <batch>
47
    <app_name>$req->app_name</app_name>
48
    <batch_name>$req->batch_name</batch_name>
49
";
50
    if ((isset($req->output_template_filename)) && ($req->output_template_filename)) {
51
        $x .= "    <output_template_filename>$req->output_template_filename</output_template_filename>
52
";
53
    }
54
    if ((isset($req->input_template_filename)) && ($req->input_template_filename)) {
55
        $x .= "    <input_template_filename>$req->input_template_filename</input_template_filename>
56
";
57
    }
58
    if ((isset($req->app_version_num)) && ($req->app_version_num)) {
59
        $x .= "    <app_version_num>$req->app_version_num</app_version_num>
60
";
61
    }
62
    if (!empty($req->allocation_priority)) {
63
        $x .= "    <allocation_priority/>
64
";
65
    }
66
    if (isset($req->priority)) {
67
        $x .= "    <priority>$req->priority</priority>
68
";
69
    }
70
    foreach ($req->jobs as $job) {
71
        $x .= "    <job>
72
";
73
        if (!empty($job->name)) {
74
            $x .= "    <name>$job->name</name>
75
";
76
        }
77
        if (!empty($job->rsc_fpops_est)) {
78
            $x .= "    <rsc_fpops_est>$job->rsc_fpops_est</rsc_fpops_est>
79
";
80
        }
81
        if (!empty($job->command_line)) {
82
            $x .= "    <command_line>$job->command_line</command_line>
83
";
84
        }
85
        if (!empty($job->target_team)) {
86
            $x .= "        <target_team>$job->target_team</target_team>
87
";
88
        } elseif (!empty($job->target_user)) {
89
            $x .= "        <target_user>$job->target_user</target_user>
90
";
91
        } elseif (!empty($job->target_host)) {
92
            $x .= "        <target_host>$job->target_host</target_host>
93
";
94
        }
95
        if (isset($job->priority)) {
96
            $x .= "        <priority>$job->priority</priority>
97
";
98
        }
99
        foreach ($job->input_files as $file) {
100
            $x .= "        <input_file>\n";
101
            $x .= "            <mode>$file->mode</mode>\n";
102
            if ($file->mode == "remote") {
103
                $x .= "            <url>$file->url</url>\n";
104
                $x .= "            <nbytes>$file->nbytes</nbytes>\n";
105
                $x .= "            <md5>$file->md5</md5>\n";
106
            } else {
107
                $x .= "            <source>$file->source</source>\n";
108
            }
109
            $x .= "        </input_file>\n";
110
        }
111
        if (!empty($job->input_template)) {
112
            $x .= "    $job->input_template\n";
113
        }
114
        if (!empty($job->output_template)) {
115
            $x .= "    $job->output_template\n";
116
        }
117
        $x .= "    </job>
118
";
119
    }
120
    $x .= "    </batch>
121
</$op>
122
";
123
    return $x;
124
}
125
126
// check whether the PHP structure looks like a batch request object
127
//
128
function validate_request($req) {
129
    if (!is_object($req)) return "req is not an object";
130
    if (!property_exists($req, 'project')) return "missing req->project";
131
    if (!property_exists($req, 'authenticator')) return "missing req->authenticator";
132
    if (!property_exists($req, 'app_name')) return "missing req->app_name";
133
    if (!property_exists($req, 'jobs')) return "missing req->jobs";
134
    if (!is_array($req->jobs)) return "req->jobs is not an array";
135
    foreach ($req->jobs as $job) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops 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.

Consider removing the loop.

Loading history...
136
        // other checks
137
    }
138
    return null;
139
}
140
141
$rpc_timeout = 0;
142
143
// Given a request object and XML string, issue the HTTP POST request
144
//
145
function do_http_op($req, $xml, $op) {
146
    global $rpc_timeout;
147
148
    $ch = curl_init("$req->project/submit_rpc_handler.php");
149
    curl_setopt($ch, CURLOPT_POST, 1);
150
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
151
    if ($rpc_timeout) {
152
        curl_setopt($ch, CURLOPT_TIMEOUT, $rpc_timeout);
153
    }
154
155
// see if we need to send any files
156
//
157
$nfiles = 0;
158
$post = array();
159
$post["request"] = $xml;
160
$cwd = getcwd();
161
if ($op == "submit_batch") {
162
    foreach ($req->jobs as $job) {
163
        foreach ($job->input_files as $file) {
164
            if ($file->mode == "inline") {
165
                $path = realpath("$cwd/$file->path");
166
                $post["file$nfiles"] = $path;
167
                $nfiles++;
168
            }
169
        }
170
    }
171
}
172
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
173
    $reply = curl_exec($ch);
174
    curl_close($ch);
175
    if (!$reply) return array(null, "HTTP error");
176
    $r = @simplexml_load_string($reply);
0 ignored issues
show
Bug introduced by
It seems like $reply can also be of type true; however, parameter $data of simplexml_load_string() does only seem to accept string, 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

176
    $r = @simplexml_load_string(/** @scrutinizer ignore-type */ $reply);
Loading history...
177
    if (!$r) {
178
        return array(null, "Can't parse reply XML:\n$reply");
179
    }
180
    $e = (string)$r->error_msg;
181
    if ($e) {
182
        return array(null, "BOINC server: $e");
183
    } else {
184
        return array($r, null);
185
    }
186
}
187
188
// do a batch op (estimate or submit)
189
//
190
function do_batch_op($req, $op) {
191
    $retval = validate_request($req);
192
    if ($retval) return array(null, $retval);
193
    $xml = req_to_xml($req, $op);
194
    return do_http_op($req, $xml, $op);
195
}
196
197
// convert a batch description from XML string to object
198
//
199
function batch_xml_to_object($batch) {
200
    $b = new StdClass;
201
    $b->id = (int)($batch->id);
202
    $b->create_time = (double)($batch->create_time);
203
    $b->est_completion_time = (double)($batch->est_completion_time);
204
    $b->njobs = (int)($batch->njobs);
205
    $b->fraction_done = (double) $batch->fraction_done;
206
    $b->nerror_jobs = (int)($batch->nerror_jobs);
207
    $b->state = (int)($batch->state);
208
    $b->completion_time = (double)($batch->completion_time);
209
    $b->credit_estimate = (double)($batch->credit_estimate);
210
    $b->credit_canonical = (double)($batch->credit_canonical);
211
    $b->name = (string)($batch->name);
212
    $b->app_name = (string)($batch->app_name);
213
    $b->total_cpu_time = (double)($batch->total_cpu_time);
214
    return $b;
215
}
216
217
// if RPC had a fatal error, return the message
218
//
219
function get_error($reply, $outer_tag) {
220
    $name = $reply->getName();
221
    if ($name != $outer_tag) {
222
        return "Bad reply outer tag";
223
    }
224
    foreach ($reply->error as $error) {
225
        if (isset($error->error_num)) {
226
            return (string)$error->error_msg;
227
        }
228
    }
229
    return null;
230
}
231
232
//// API functions follow
233
234
function boinc_set_timeout($x) {
235
    global $rpc_timeout;
236
    $rpc_timeout = $x;
237
}
238
239
function boinc_ping($req) {
240
    $req_xml = "<ping>
241
    <authenticator>$req->authenticator</authenticator>
242
</ping>
243
";
244
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
245
    if ($errmsg) return array(null, $errmsg);
246
    return array($reply, null);
247
}
248
249
function boinc_estimate_batch($req) {
250
    list($reply, $errmsg) = do_batch_op($req, "estimate_batch");
251
    if ($errmsg) return array(0, $errmsg);
252
    if ($x = get_error($reply, "estimate_batch")) {
253
        return array(null, $x);
254
    }
255
    return array((string)$reply->seconds, null);
256
}
257
258
function boinc_submit_batch($req) {
259
    list($reply, $errmsg) = do_batch_op($req, "submit_batch");
260
    if ($errmsg) return array(0, $errmsg);
261
    if ($x = get_error($reply, "submit_batch")) {
262
        return array(null, $x);
263
    }
264
    return array((int)$reply->batch_id, null);
265
}
266
267
function boinc_query_batches($req) {
268
    $req_xml = "<query_batches>
269
    <authenticator>$req->authenticator</authenticator>
270
";
271
    if (!empty($req->get_cpu_time)) {
272
        $req_xml .= "   <get_cpu_time>1</get_cpu_time>\n";
273
    }
274
    $req_xml .= "</query_batches>\n";
275
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
276
    if ($errmsg) return array(null, $errmsg);
277
    if ($x = get_error($reply, "query_batches")) {
278
        return array(null, $x);
279
    }
280
    $batches = array();
281
    foreach ($reply->batch as $batch) {
282
        $b = batch_xml_to_object($batch);
283
        $batches[] = $b;
284
    }
285
    return array($batches, null);
286
}
287
288
function boinc_query_batch($req) {
289
    $req_xml = "<query_batch>
290
    <authenticator>$req->authenticator</authenticator>
291
    <batch_id>$req->batch_id</batch_id>
292
";
293
    if (!empty($req->get_cpu_time)) {
294
        $req_xml .= "   <get_cpu_time>1</get_cpu_time>\n";
295
    }
296
    if (!empty($req->get_job_details)) {
297
        $req_xml .= "   <get_job_details>1</get_job_details>\n";
298
    }
299
    $req_xml .= "</query_batch>\n";
300
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
301
    if ($errmsg) return array(null, $errmsg);
302
    if ($x = get_error($reply, "query_batch")) {
303
        return array(null, $x);
304
    }
305
    $jobs = array();
306
    foreach ($reply->job as $job) {
307
        $j = new StdClass;
308
        $j->id = (int)($job->id);
309
        $j->canonical_instance_id = (int)($job->canonical_instance_id);
310
        $jobs[] = $j;
311
    }
312
    $r = batch_xml_to_object($reply);
313
    $r->jobs = $jobs;
314
    return array($r, null);
315
}
316
317
function boinc_query_job($req) {
318
    $req_xml = "<query_job>
319
    <authenticator>$req->authenticator</authenticator>
320
    <job_id>$req->job_id</job_id>
321
</query_job>
322
";
323
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
324
    if ($errmsg) return array(null, $errmsg);
325
    if ($x = get_error($reply, "query_job")) {
326
        return array(null, $x);
327
    }
328
    $instances = array();
329
    foreach ($reply->instance as $instance) {
330
        $i = new StdClass;
331
        $i->name = (string)($instance->name);
332
        $i->id = (int)($instance->id);
333
        $i->state = (string)($instance->state);
334
        $i->outfiles = array();
335
        foreach ($instance->outfile as $outfile) {
336
            $f = new StdClass;
337
            $f->size = (double)$outfile->size;
338
            $i->outfiles[] = $f;
339
        }
340
        $instances[] = $i;
341
    }
342
    $r = new StdClass;
343
    $r->instances = $instances;
344
    return array($r, null);
345
}
346
347
function boinc_abort_batch($req) {
348
    $req_xml = "<abort_batch>
349
    <authenticator>$req->authenticator</authenticator>
350
    <batch_id>$req->batch_id</batch_id>
351
</abort_batch>
352
";
353
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
354
    if ($errmsg) return $errmsg;
355
    if ($x = get_error($reply, "abort_batch")) {
356
        return array(null, $x);
357
    }
358
    return array(true, null);
359
}
360
361
function boinc_get_output_file($req) {
362
    $auth_str = md5($req->authenticator.$req->instance_name);
363
    $name = $req->instance_name;
364
    $file_num = $req->file_num;
365
    return $req->project."/get_output.php?cmd=result_file&result_name=$name&file_num=$file_num&auth_str=$auth_str";
366
}
367
368
function boinc_get_output_files($req) {
369
    $auth_str = md5($req->authenticator.$req->batch_id);
370
    $batch_id = $req->batch_id;
371
    return $req->project."/get_output.php?cmd=batch_files&batch_id=$batch_id&auth_str=$auth_str";
372
}
373
374
function boinc_retire_batch($req) {
375
    $req_xml = "<retire_batch>
376
    <authenticator>$req->authenticator</authenticator>
377
    <batch_id>$req->batch_id</batch_id>
378
</retire_batch>
379
";
380
    list($reply, $errmsg) = do_http_op($req, $req_xml, "");
381
    if ($errmsg) return $errmsg;
382
    if ($x = get_error($reply, "retire_batch")) {
383
        return array(null, $x);
384
    }
385
    return array(true, null);
386
}
387
388
//// example usage follows
389
if (0) {
390
    $req = new StdClass;
391
    $req->project = "http://isaac.ssl.berkeley.edu/test/";
392
    $req->authenticator = trim(file_get_contents("test_auth"));
393
    $req->app_name = "uppercase";
394
    $req->batch_name = "batch_name_12";
395
    $req->app_version_num = 710;
396
    $req->jobs = array();
397
398
399
    $f = new StdClass;
400
    $f->mode = "remote";
401
    $f->url = "http://isaac.ssl.berkeley.edu/validate_logic.txt";
402
    $f->md5 = "eec5a142cea5202c9ab2e4575a8aaaa7";
403
    $f->nbytes = 4250;
404
405
if (0) {
406
    $f = new StdClass;
407
    $f->mode = "local";
408
    $f->source = "foobar";
409
    //$job->input_files[] = $f;
410
}
411
412
    $it = "
413
<input_template>
414
    <file_info>
415
    </file_info>
416
    <workunit>
417
        <file_ref>
418
            <open_name>in</open_name>
419
        </file_ref>
420
        <target_nresults>1</target_nresults>
421
        <min_quorum>1</min_quorum>
422
        <rsc_fpops_est>   60e9  </rsc_fpops_est>
423
        <rsc_fpops_bound> 60e12 </rsc_fpops_bound>
424
        <rsc_disk_bound>2e6</rsc_disk_bound>
425
        <rsc_memory_bound>1e6</rsc_memory_bound>
426
        <delay_bound>3600</delay_bound>
427
        <credit>1</credit>
428
    </workunit>
429
</input_template>
430
";
431
    $ot = "
432
<output_template>
433
    <file_info>
434
        <name><OUTFILE_0/></name>
435
        <generated_locally/>
436
        <upload_when_present/>
437
        <max_nbytes>6000000</max_nbytes>
438
        <url><UPLOAD_URL/></url>
439
    </file_info>
440
    <result>
441
        <file_ref>
442
            <file_name><OUTFILE_0/></file_name>
443
            <open_name>out</open_name>
444
        </file_ref>
445
    </result>
446
</output_template>
447
";
448
    for ($i=0; $i<2; $i++) {
449
        $job = new StdClass;
450
        $job->input_files = array();
451
        $job->input_files[] = $f;
452
        $job->name = $req->batch_name."_$i";
453
        //$job->rsc_fpops_est = $i*1e9;
454
        $job->command_line = "--t $i";
455
        $job->input_template = $it;
456
        $job->output_template = $ot;
457
        $req->jobs[] = $job;
458
    }
459
460
    if (0) {
461
        list($e, $errmsg) = boinc_estimate_batch($req);
462
        if ($errmsg) {
463
            echo "Error from server: $errmsg\n";
464
        } else {
465
            echo "Batch completion estimate: $e seconds\n";
466
        }
467
    } else {
468
        list($id, $errmsg) = boinc_submit_batch($req);
469
        if ($errmsg) {
470
            echo "Error from server: $errmsg\n";
471
        } else {
472
            echo "Batch ID: $id\n";
473
        }
474
    }
475
}
476
477
if (0) {
478
    list($batches, $errmsg) = boinc_query_batches($req);
479
    if ($errmsg) {
480
        echo "Error: $errmsg\n";
481
    } else {
482
        print_r($batches);
483
    }
484
}
485
486
if (0) {
487
    $req->batch_id = 20;
488
    list($jobs, $errmsg) = boinc_query_batch($req);
489
    if ($errmsg) {
490
        echo "Error: $errmsg\n";
491
    } else {
492
        print_r($jobs);
493
    }
494
}
495
?>
496