cosmetic error msg - somehow debug_backtrace seems to lack context sometimes
[plcapi.git] / php / plc_api.php
1 <?php
2 //
3 // PlanetLab Central Slice API (PLCAPI) PHP interface
4 //
5 // DO NOT EDIT. This file was automatically generated at
6 // @DATE@.
7 //
8 // Mark Huang <mlhuang@cs.princeton.edu>
9 // Copyright (C) 2005-2006 The Trustees of Princeton University
10 //
11 // $Id$
12 // $URL$
13 //
14 //
15
16 require_once 'plc_config.php';
17
18 class PLCAPI
19 {
20   var $auth;
21   var $server;
22   var $port;
23   var $path;
24   var $errors;
25   var $trace;
26   var $calls;
27   var $multicall;
28
29   function PLCAPI($auth = NULL,
30                   $server = PLC_API_HOST,
31                   $port = PLC_API_PORT,
32                   $path = PLC_API_PATH,
33                   $cainfo = NULL)
34   {
35     $this->auth = $auth;
36     $this->server = $server;
37     $this->port = $port;
38     $this->path = $path;
39     $this->cainfo = $cainfo;
40     $this->errors = array();
41     $this->trace = array();
42     $this->calls = array();
43     $this->multicall = false;
44   }
45
46   function error_log($error_msg, $backtrace_level = 1)
47   {
48     $backtrace = debug_backtrace();
49     $file = $backtrace[$backtrace_level]['file'];
50     $line = $backtrace[$backtrace_level]['line'];
51
52     $error_line='PLCAPI error:  ' . $error_msg ;
53     if ($file) $error_line .= ' in file ' . $file;
54     if ($line) $error_line .= ' on line ' . $line;
55     $this->errors[] = $error_line;
56     error_log($error_line);
57   }
58
59   function error()
60   {
61     if (empty($this->trace)) {
62       return NULL;
63     } else {
64       $last_trace = end($this->trace);
65       return implode("\\n", $last_trace['errors']);
66     }
67   }
68
69   function trace()
70   {
71     return $this->trace;
72   }
73
74   function microtime_float()
75   {
76     list($usec, $sec) = explode(" ", microtime());
77     return ((float) $usec + (float) $sec);
78   }
79
80   function call($method, $args = NULL)
81   {
82     if ($this->multicall) {
83       $this->calls[] = array ('methodName' => $method,
84                                 'params' => $args);
85       return NULL;
86     } else {
87       return $this->internal_call ($method, $args, 3);
88     }
89   }
90
91   function internal_call($method, $args = NULL, $backtrace_level = 2)
92   {
93     $curl = curl_init();
94
95     // Verify peer certificate if talking over SSL
96     if ($this->port == 443) {
97       curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 2);
98       if (!empty($this->cainfo)) {
99         curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
100       } elseif (defined('PLC_API_CA_SSL_CRT')) {
101         curl_setopt($curl, CURLOPT_CAINFO, PLC_API_CA_SSL_CRT);
102       }
103       $url = 'https://';
104     } else {
105       $url = 'http://';
106     }
107
108     // Set the URL for the request
109     $url .= $this->server . ':' . $this->port . '/' . $this->path;
110     curl_setopt($curl, CURLOPT_URL, $url);
111
112     // Marshal the XML-RPC request as a POST variable. <nil/> is an
113     // extension to the XML-RPC spec that is supported in our custom
114     // version of xmlrpc.so via the 'allow_null' output_encoding key.
115     $request = xmlrpc_encode_request($method, $args, array('allow_null' => TRUE));
116     curl_setopt($curl, CURLOPT_POSTFIELDS, $request);
117
118     // Construct the HTTP header
119     $header[] = 'Content-type: text/xml';
120     $header[] = 'Content-length: ' . strlen($request);
121     curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
122
123     // Set some miscellaneous options
124     curl_setopt($curl, CURLOPT_TIMEOUT, 180);
125
126     // Get the output of the request
127     curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
128     $t0 = $this->microtime_float();
129     $output = curl_exec($curl);
130     $t1 = $this->microtime_float();
131
132     if (curl_errno($curl)) {
133       $this->error_log('curl: ' . curl_error($curl), true);
134       $ret = NULL;
135     } else {
136       $ret = xmlrpc_decode($output);
137       if (is_array($ret) && xmlrpc_is_fault($ret)) {
138         $this->error_log('Fault Code ' . $ret['faultCode'] . ': ' .
139                          $ret['faultString'], $backtrace_level, true);
140         $ret = NULL;
141       }
142     }
143
144     curl_close($curl);
145
146     $this->trace[] = array('method' => $method,
147                            'args' => $args,
148                            'runtime' => $t1 - $t0,
149                            'return' => $ret,
150                            'errors' => $this->errors);
151     $this->errors = array();
152
153     return $ret;
154   }
155
156   function begin()
157   {
158     if (!empty($this->calls)) {
159       $this->error_log ('Warning: multicall already in progress');
160     }
161
162     $this->multicall = true;
163   }
164
165   function commit()
166   {
167     if (!empty ($this->calls)) {
168       $ret = array();
169       $results = $this->internal_call ('system.multicall', array ($this->calls));
170       foreach ($results as $result) {
171         if (is_array($result)) {
172           if (xmlrpc_is_fault($result)) {
173             $this->error_log('Fault Code ' . $result['faultCode'] . ': ' .
174                              $result['faultString'], 1, true);
175             $ret[] = NULL;
176             // Thierry - march 30 2007 
177             // using $adm->error() is broken with begin/commit style 
178             // this is because error() uses last item in trace and checks for ['errors']
179             // when using begin/commit we do run internal_call BUT internal_call checks for 
180             // multicall's result globally, not individual results, so ['errors'] comes empty
181             // I considered hacking internal_call 
182             // to *NOT* maintain this->trace at all when invoked with multicall
183             // but it is too complex to get all values right
184             // so let's go for the hacky way, and just record individual errors at the right place
185             $this->trace[count($this->trace)-1]['errors'][] = end($this->errors);
186           } else {
187             $ret[] = $result[0];
188           }
189         } else {
190           $ret[] = $result;
191         }
192       }
193     } else {
194       $ret = NULL;
195     }
196
197     $this->calls = array();
198     $this->multicall = false;
199
200     return $ret;
201   }
202
203   //
204   // PLCAPI Methods
205   //
206
207   function __call($name, $args)
208   {
209      array_unshift($args, $this->auth);
210      return $this->call($name, $args);
211   }
212 }
213
214 global $adm;
215
216 $adm = new PLCAPI(array('AuthMethod' => "capability",
217                         'Username' => PLC_API_MAINTENANCE_USER,
218                         'AuthString' => PLC_API_MAINTENANCE_PASSWORD));
219
220 ?>