more flexible API
[curl-de-sac.git] / web / Obj / curl-de-sac.phh
1 <?php
2 /*
3  *  curl-de-sac - curl-de-sac.phh
4  *
5  *  Copyright (C)      2014 Matteo Nastasi
6  *                          mailto: nastasi@alternativeoutput.it
7  *                                  matteo.nastasi@gmail.com
8  *                          web: http://www.alternativeoutput.it
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details. You should have received a
19  * copy of the GNU General Public License along with this program; if
20  * not, write to the Free Software Foundation, Inc, 59 Temple Place -
21  * Suite 330, Boston, MA 02111-1307, USA.
22  *
23  */
24
25 define('CURL_DE_SAC_VERS', '0.1');
26 $G_curl_de_sac_vers = CURL_DE_SAC_VERS;
27
28 class CDS_cmd {
29     var $cmd_cls;
30     var $ch;
31     var $tlimit;
32
33     function CDS_cmd($cmd_cls, $ch)
34     {
35         $this->cmd_cls = $cmd_cls;
36         $this->ch = $ch;
37         $this->tlimit = time() + $cmd_cls->tout;
38     }
39
40     function ch_get()
41     {
42         return ($this->ch);
43     }
44
45     function dbg_get()
46     {
47         // NOTE: cmd_cls must be valid by definition
48         if ($this->cmd_cls->cds == NULL)
49             return -1;
50         return $this->cmd_cls->cds->dbg_get();
51     }
52 }
53
54 class CDS_cmd_cls {
55     var $cds;
56     var $name;
57     var $tout;
58
59     function CDS_cmd_cls($name, $tout)
60     {
61         $this->cds = NULL;
62         $this->name = $name;
63         $this->tout = $tout;
64     }
65
66     function cds_set($cds)
67     {
68         $this->cds = $cds;
69     }
70
71     static function pre_create($cds, $url, $opts=NULL)
72     {
73         if ($cds->dbg_get() > 2) { printf("CURL: curl_init\n"); }
74         if (($ch = curl_init()) == FALSE)
75             return FALSE;
76         curl_setopt($ch, CURLOPT_URL, $url);
77         if ($opts == NULL) {
78             curl_setopt($ch, CURLOPT_HEADER, 0);
79             curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
80             curl_setopt($ch, CURLOPT_FORBID_REUSE, true);
81             curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: close'));
82         }
83         else {
84             foreach($opts as $opt => $value) {
85                 curl_setopt($ch, $opt, $value);
86             }
87         }
88         return ($ch);
89     }
90
91     function create($cds, $ch)
92     {
93         if ($cds->dbg > 2) {
94             printf("CDS_cmd_cls::create - begin\n");
95             printf("CURL: curl_multi_add_handle\n");
96         }
97         if (($ret = curl_multi_add_handle($cds->mh, $ch)) != 0) {
98             // INFO: $ret is a CURLM_XXX errors code
99             return (FALSE);
100         }
101         if ($cds->dbg > 2) { printf("CDS_cmd_cls::create - end\n"); }
102         return (TRUE);
103     }
104
105     function process($cmd, $ret)
106     {
107         
108         fprintf(STDERR, "process MUST BE IMPLEMENTED");
109         exit(123);
110     }
111
112     function timeout($cmd)
113     {
114         fprintf(STDERR, "timeout MUST BE IMPLEMENTED");
115         exit(123);
116     }
117
118     function dbg_get()
119     {
120         return $this->cds->dbg;
121     }
122 }
123
124 class Curl_de_sac {
125     var $mh;
126     var $cmd_cls;
127     var $cmd;
128     var $dbg;
129
130     function Curl_de_sac($dbg=0) {
131         if ($dbg > 2) { printf("CURL: curl_multi_init\n"); }
132         $this->mh = curl_multi_init();
133         $this->cmd_cls = array();
134         $this->cmd = array();
135         $this->dbg = $dbg;
136     }
137
138     function dbg_set($dbg)
139     {
140         $this->dbg = $dbg;
141     }
142
143     function dbg_get()
144     {
145         return($this->dbg);
146     }
147
148     function cmd_cls_register($cmd_cls)
149     {
150         if (get_class($cmd_cls) != 'CDS_cmd_cls' && is_subclass_of($cmd_cls, 'CDS_cmd_cls') == FALSE)
151             return FALSE;
152
153         if (isset($this->cmd_cls[$cmd_cls->name]))
154             return FALSE;
155
156         $this->cmd_cls[$cmd_cls->name] = $cmd_cls;
157         $cmd_cls->cds_set($this);
158
159         return TRUE;
160     }
161
162     function cmd_cls_deregister($cmd_cls)
163     {
164         if (get_class($cmd_cls) != 'CDS_cmd_cls' && is_subclass_of($cmd_cls, 'CDS_cmd_cls') == FALSE)
165             return FALSE;
166         if (!isset($this->cmd_cls[$cmd_cls->name]))
167             return FALSE;
168
169         $this->cmd_cls[$cmd_cls->name]->cds_set(NULL);
170
171         unset($this->cmd_cls[$cmd_cls->name]);
172         return TRUE;
173     }
174
175     function cmd_cls_deregister_all()
176     {
177         foreach($this->cmd_cls as $cmd_cls) {
178             $cmd_cls->cds_set(NULL);
179         }
180
181         $this->cmd_cls = array();
182     }
183
184
185     function cleanup($key)
186     {
187         $cmd = $this->cmd[$key];
188
189         if ($this->dbg > 2) {
190             printf("cleanup\n");
191             printf("CURL: curl_multi_remove_handle:\n");
192             print_r($cmd->ch_get());
193             printf("\n");
194         }
195         // return 0 on SUCCESS or CURLM_XXX in other cases
196         if (($ret = curl_multi_remove_handle($this->mh, $cmd->ch_get())) != 0) {
197             fprintf(STDERR, "CURL: curl_multi_remove_handle FAILED (%d)\n", $ret);
198         }
199         if ($this->dbg > 2) { printf("CURL: curl_close\n"); }
200         curl_close($cmd->ch_get());
201         unset($this->cmd[$key]);
202     }
203
204     function execute()
205     {
206         $args = func_get_args();
207
208         if ($this->dbg > 1) {
209              printf("CDS_cmd_cls::execute  ARGS:\n");
210              print_r($args);
211         }
212         do {
213             if (($name = array_shift($args)) === NULL)
214                 break;
215             array_unshift($args, $this);
216             
217             if (!isset($this->cmd_cls[$name]))
218                 break;
219             
220             $cmd_cls = $this->cmd_cls[$name];
221             
222             // custom create now can return synchronously returning true instead of a
223             // "command instance class" instance or false if any error occurs
224             $inst = call_user_func_array(array($cmd_cls, "create"), $args);
225             if (is_bool($inst))
226                 break;
227             array_push($this->cmd, $inst);
228             if ($this->dbg > 1) { printf("CDS_cmd_cls::process - execute  push cmd\n"); }
229             if (($this->dbg & 1) == 1) { print_r($this); }
230
231             return TRUE;
232         } while (FALSE);
233
234         return $inst;
235     }
236
237     function process($curtime=0)
238     {
239         if ($curtime  == 0) {
240             $curtime = time();
241         }
242         if ($this->dbg > 1) { printf("CDS_cmd_cls::process - begin\n"); }
243         $running = NULL;
244
245         if ($this->dbg > 2) { printf("CURL: curl_multi_exec\n"); }
246         $ret = curl_multi_exec($this->mh, $running);
247         $msgs_in_queue = NULL;
248
249         do {
250             if ($this->dbg > 2) { printf("CURL: curl_multi_info_read\n"); }
251
252             if ($ret = curl_multi_info_read ($this->mh, $msgs_in_queue)) {
253                 if ($this->dbg > 1) { printf("Info_read miq: %d\n", $msgs_in_queue); }
254                 if ($this->dbg > 2) { printf("CURL: curl_getinfo\n"); }
255
256                 $info = curl_getinfo($ret['handle']);
257                 if ($this->dbg > 1) {
258                     printf("Getinfo:\n");
259                     print_r($info);
260                 }
261
262                 foreach($this->cmd as $key => $cmd) {
263                     if ($cmd->ch == $ret['handle']) {
264                         if ($cmd->cmd_cls->process($cmd, $ret) == TRUE) {
265                             $this->cleanup($key);
266                         }
267                         break;
268                     }
269                 }
270             }
271         } while ($msgs_in_queue > 0);
272         foreach ($this->cmd as $key => $cmd) {
273             if ($this->dbg > 2) { printf("Check tout, curr: %d tlimit %d\n", $curtime, $cmd->tlimit); }
274             if ($curtime > $cmd->tlimit) {
275                 if ($this->dbg > 2) { printf("TIMEOUT REACHED!\n"); }
276                 $cmd->cmd_cls->timeout($cmd);
277                 $this->cleanup($key);
278             }
279         }
280         if ($this->dbg > 1) { printf("CDS_cmd_cls::process - end (queue: %d)\n", $msgs_in_queue); }
281     }
282
283 }