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) {
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