| 1 | <?php | 
| 2 | /** | 
| 3 | * This file contains the GenericDataSource child class | 
| 4 | * that may use arbitrary code modules as database handlers. | 
| 5 | * It's aims are to get together: | 
| 6 | *   o phpHtmlLibs "source-handlers" | 
| 7 | *   o PEAR's database handlers | 
| 8 | *   o custom ones (e.g. Data::Driver::Proxy object, which talks to a remote rpc server) | 
| 9 | * | 
| 10 | * @author Andreas Motl <andreas.motl@ilo.de> | 
| 11 | * @package phpHtmlLib | 
| 12 | * @module GenericDataSource | 
| 13 | * | 
| 14 | */ | 
| 15 |  | 
| 16 | /** | 
| 17 | * $Id: GenericDataSource.inc,v 1.1 2003/03/01 21:47:15 joko Exp $ | 
| 18 | * | 
| 19 | * $Log: GenericDataSource.inc,v $ | 
| 20 | * Revision 1.1  2003/03/01 21:47:15  joko | 
| 21 | * renamed from AbstractDataSource.inc | 
| 22 | * | 
| 23 | * Revision 1.1  2003/03/01 20:17:15  joko | 
| 24 | * renamed from GenericDataSource.inc | 
| 25 | * replaced '_proxy' through '_handler' to better associate the ability to act as a generic dispatcher handler | 
| 26 | * | 
| 27 | * Revision 1.3  2003/03/01 15:36:11  joko | 
| 28 | * + debugging | 
| 29 | * + renamed some methods regarding new proposal (build_handler_options, fetch_result, etc.) | 
| 30 | * | 
| 31 | * Revision 1.2  2003/03/01 04:50:27  joko | 
| 32 | * encapsulated all output into tracing mode | 
| 33 | * disabled tracing | 
| 34 | * | 
| 35 | * Revision 1.1  2003/03/01 03:10:40  joko | 
| 36 | * + initial commit | 
| 37 | * | 
| 38 | * | 
| 39 | */ | 
| 40 |  | 
| 41 | /** | 
| 42 | * This requires the MemoryDataSource base class | 
| 43 | * | 
| 44 | */ | 
| 45 | // V1: compile-time-include, stable since years  ;-) | 
| 46 | include_once($phphtmllib."/widgets/data_source/MemoryDataSource.inc"); | 
| 47 | // V2: runtime-load proposal, more flexible | 
| 48 | // loadModule('MemoryDataSource', 'inc');   // doesn't work (would do if basepath of module is in libpath) | 
| 49 | // loadModule($phphtmllib . '::widgets::data_source::MemoryDataSource', 'inc');   // works  (by accident) | 
| 50 | // loadModule($phphtmllib . "/widgets/data_source/MemoryDataSource", "inc");   // works  (should be stable) | 
| 51 | // loadModule($phphtmllib . "/widgets/data_source/MemoryDataSource.inc", null);   // works  (should be stable) | 
| 52 |  | 
| 53 |  | 
| 54 |  | 
| 55 | /** | 
| 56 | * Have to have PEAR and DB included | 
| 57 | * the pear dir must be in the | 
| 58 | * include path. | 
| 59 | */ | 
| 60 | // require_once("PEAR.php");   // FIXME: what about PEAR::XML::RPC? | 
| 61 | // require_once("DB.php"); | 
| 62 |  | 
| 63 | /** | 
| 64 | * This GenericDataSource child class is *completely* independent | 
| 65 | * of a specific storage implementation and intended to be a wrapper | 
| 66 | * class on the way from phpHtmlLib to Xyz. | 
| 67 | *    ("Yyz" gets represented by Data::Storage by now.....) | 
| 68 | * | 
| 69 | * GenericDataSource interacts with an intermediate "proxy" object | 
| 70 | * (e.g. Data::Driver::Proxy) when doing queries. | 
| 71 | *    Don't mix this up with a persistent database handle which gets | 
| 72 | *    reused each and every time for different queries. | 
| 73 | *    Here *is* a new instance of Data::Driver::Proxy for *each* query. | 
| 74 | * | 
| 75 | * But the point is: Caching! The actual *data* isn't read redundant! | 
| 76 | * | 
| 77 | * | 
| 78 | * !!!!!!  refactor this to Data::Driver::Proxy  !!!!!!     <----------------- | 
| 79 | * | 
| 80 | * Data::Driver::Proxy instantiates "handlers" below itself | 
| 81 | * which act as encapsulated workers to actually do the stuff to do. | 
| 82 | * It can cache data for "disconnected mode" using the | 
| 83 | * php PEAR DB abstraction objects. | 
| 84 | * One worker already implemented is Data::Driver::RPC::Remote, which | 
| 85 | * talks to a RPC::XML server (todo: talk SOAP!) using PEAR::XML::RPC. | 
| 86 | * | 
| 87 | * ---  refactored here, but: redundant somehow  --- | 
| 88 | * Data::Driver::Proxy uses a PEAR XML::RPC object to actually | 
| 89 | * talk HTTP and serialize data chunks to and from XML, | 
| 90 | * but adds some caching to this "process of fetching data from a remote side". | 
| 91 | * It "proxies" arbitrary data chunks a) inside a native php4 session, | 
| 92 | * b) by talking to a rdbms (single proxy-table) or c) [TODO] using PEAR::Cache. | 
| 93 | * ---  refactored here, but: redundant somehow  --- | 
| 94 | * | 
| 95 | * !!!!!!  refactor this to Data::Driver::Proxy  !!!!!!     <----------------- | 
| 96 | * | 
| 97 | * | 
| 98 | * How to use? | 
| 99 | * | 
| 100 | * Pass an array holding "locator metadata" to the constructor. | 
| 101 | * GenericDataSource takes care of the rest. | 
| 102 | * | 
| 103 | * Pass an array to the constructor: (e.g.) | 
| 104 | * | 
| 105 | *  1. doing rpc-calls.... | 
| 106 | *    $locator = array( | 
| 107 | *      type => 'rpc', | 
| 108 | *      metadata => array( Host => 'localhost', Port => '8765' ), | 
| 109 | *    ); | 
| 110 | *    $source = new GenericDataSource($locator); | 
| 111 | *    $this->set_data_source( &$source ); | 
| 112 | * | 
| 113 | *  2. [proposal] common datahandles.... | 
| 114 | *    $locator = array( | 
| 115 | *      type => 'mysql', | 
| 116 | *      dsn => 'known dsn markup', | 
| 117 | *    ); | 
| 118 | *    $source = new GenericDataSource($locator); | 
| 119 | *    $this->set_data_source( &$source ); | 
| 120 | * | 
| 121 | * @author Andreas Motl <andreas.motl@ilo.de> | 
| 122 | * @author Sebastian Utz <seut@tunemedia.de> | 
| 123 | * @copyright (c) 2003 - All Rights reserved. | 
| 124 | * @license GNU LGPL (GNU Lesser General Public License) | 
| 125 | * | 
| 126 | * @author-url http://www.netfrag.org/~joko/ | 
| 127 | * @author-url http://www.netfrag.org/~jonen/ | 
| 128 | * @license-url http://www.gnu.org/licenses/lgpl.txt | 
| 129 | * | 
| 130 | * @package phpHtmlLib | 
| 131 | * @module GenericDataSource | 
| 132 | * | 
| 133 | */ | 
| 134 |  | 
| 135 | /** | 
| 136 | * Todo: | 
| 137 | * | 
| 138 | *  o mungle this to be able to be wrapped around phpHtmlLib's own storage-handles | 
| 139 | *  o implement another Data::Driver::Proxy container | 
| 140 | * | 
| 141 | */ | 
| 142 |  | 
| 143 |  | 
| 144 | class GenericDataSource extends MemoryDataSource { | 
| 145 |  | 
| 146 | /** | 
| 147 | * This var holds the locator metadata hash | 
| 148 | * that is used to feed metadata to a per-query-instance | 
| 149 | * of a Data::Driver::Proxy object. | 
| 150 | * | 
| 151 | */ | 
| 152 | var $_locator = NULL; | 
| 153 |  | 
| 154 |  | 
| 155 | /** | 
| 156 | * This var holds the query hash | 
| 157 | * that is used to feed as query to a per-query-instance | 
| 158 | * of a Data::Driver::Proxy object. | 
| 159 | * | 
| 160 | */ | 
| 161 | var $_query = NULL; | 
| 162 |  | 
| 163 |  | 
| 164 | /** | 
| 165 | * This var holds the Handler object | 
| 166 | * that is used to do the work. | 
| 167 | * It acts as a dispatcher combining result caching. | 
| 168 | * It is assumed that this provides 4 methods: | 
| 169 | * queryData() - execute a query against a data storage | 
| 170 | * querySchema() - execute a query against underlying storage metadata | 
| 171 | * sendCommand() - send a command against an arbitrary execution engine | 
| 172 | * ... or others! (these are just proposals for convenience) | 
| 173 | * | 
| 174 | */ | 
| 175 | var $_handler = NULL; | 
| 176 |  | 
| 177 |  | 
| 178 | /** | 
| 179 | * This var holds options fed to the Proxy object | 
| 180 | * These are built from locator metadata (_locator) and query arguments (_query) | 
| 181 | * It's a simple structured hash: | 
| 182 | *  $proxy_options = array( | 
| 183 | *    method => '<remote-method-name>', | 
| 184 | *    args => array('list', 'of', 'arguments') | 
| 185 | *  ); | 
| 186 | * | 
| 187 | */ | 
| 188 | var $_handler_options = NULL; | 
| 189 |  | 
| 190 |  | 
| 191 | /** | 
| 192 | * this holds the query result from the | 
| 193 | * Data::Driver::Proxy->queryXyz() call | 
| 194 | * | 
| 195 | */ | 
| 196 | var $_result = NULL; | 
| 197 |  | 
| 198 | /** | 
| 199 | * This holds some information about the tracing level. | 
| 200 | * | 
| 201 | */ | 
| 202 | var $_debug = array( | 
| 203 | notice => 0, | 
| 204 | trace => 0, | 
| 205 | payload => 0, | 
| 206 | ); | 
| 207 |  | 
| 208 |  | 
| 209 | /** | 
| 210 | * The constructor is used to pass in the | 
| 211 | * locator metadata hash. | 
| 212 | * | 
| 213 | * @param LocatorMetadataHash array - layout: array( type => '', metadata => '', dsn => '' ) | 
| 214 | * @param Query array - layout: array( type => '', metadata => '', dsn => '' ) | 
| 215 | */ | 
| 216 | function GenericDataSource( &$locator, $query ) { | 
| 217 |  | 
| 218 | $this->set_locator( $locator ); | 
| 219 | $this->set_query( $query ); | 
| 220 | } | 
| 221 |  | 
| 222 | /** | 
| 223 | * Set the locator metadata hash we will feed *partly* | 
| 224 | * to an encapsulated Data::Driver::Proxy instance. | 
| 225 | * | 
| 226 | * @param LocatorMetadataHash array - | 
| 227 | * | 
| 228 | */ | 
| 229 | function set_locator( &$locator ) { | 
| 230 | $this->_locator = &$locator; | 
| 231 | } | 
| 232 |  | 
| 233 | /** | 
| 234 | * Set the query metadata hash we will feed *completely* | 
| 235 | * to an encapsulated Data::Driver::Proxy instance. | 
| 236 | * | 
| 237 | * @param QueryDeclaration array - | 
| 238 | * | 
| 239 | */ | 
| 240 | function set_query( $query ) { | 
| 241 | $this->_query = $query; | 
| 242 | } | 
| 243 |  | 
| 244 | /** | 
| 245 | * Directly inject a Data::Driver::Proxy instance to use. | 
| 246 | * | 
| 247 | * @param Data::Driver::Proxy object - &$proxy | 
| 248 | * | 
| 249 | */ | 
| 250 | function set_handler( &$proxy ) { | 
| 251 | $this->_handler = &$proxy; | 
| 252 | } | 
| 253 |  | 
| 254 | /** | 
| 255 | * Issue remote/proxy call | 
| 256 | * Stolen from Application_AbstractBackend::_remote_method (RefactoringProposal?) | 
| 257 | * Tweaked a bit: proxy package now taken from $this->_handler_name | 
| 258 | * | 
| 259 | * Create a new instance of a proxy and store it for later reuse. | 
| 260 | * Read the locator metadata hash and create | 
| 261 | * the proxy handler instance using passed-in name. | 
| 262 | * | 
| 263 | * @param string - $proxy_name (namespaced classname - perl syntax - e.g.: Data::Driver::Proxy) | 
| 264 | * | 
| 265 | */ | 
| 266 | function do_handler_call() { | 
| 267 |  | 
| 268 |  | 
| 269 | // 1. read args | 
| 270 |  | 
| 271 | $arg_list = func_get_args(); | 
| 272 | $command = array_shift($arg_list); | 
| 273 | $cache_key = join("-", array(session_id(), $command, join('_', $arg_list) )); | 
| 274 | //print "cache_key: $cache_key<br>"; | 
| 275 |  | 
| 276 | // FIXME: what if arg_list still contains more elements after doing this? | 
| 277 | $query = array_shift($arg_list); | 
| 278 |  | 
| 279 |  | 
| 280 | // 2. prepare proxy-options | 
| 281 |  | 
| 282 | // read from locator metadata | 
| 283 | $proxy_name = $this->_locator[metadata][package]; | 
| 284 | $rpcinfo = array( Host => $this->_locator[metadata][Host], Port => $this->_locator[metadata][Port] ); | 
| 285 |  | 
| 286 | // FIXME! implement this into Data::Driver::RPC::Remote! | 
| 287 | //$rpcinfo[to_latin] = 1; | 
| 288 |  | 
| 289 | // FIXME! patch query | 
| 290 | if (sizeof($query) == 1) { | 
| 291 | $query = $query[0]; | 
| 292 | } | 
| 293 |  | 
| 294 | // --------------------   clone this & modify  ---------- | 
| 295 | $proxy = mkObject($proxy_name, $cache_key, array( key => 1, command => $command, query => $query, remote => 1, rpcinfo => $rpcinfo, cache => array( db => 0, session => 1 ) ) ); | 
| 296 | $this->set_handler( $proxy ); | 
| 297 | //$result = $resultHandle->getAttributes(); | 
| 298 | //return $result; | 
| 299 | //$this->_result = $resultHandle->getAttributes(); | 
| 300 | //$this->_result = $this->_handler->getAttributes(); | 
| 301 | // --------------------   clone this & modify  ---------- | 
| 302 |  | 
| 303 | } | 
| 304 |  | 
| 305 |  | 
| 306 | function build_handler_options() { | 
| 307 |  | 
| 308 | //print Dumper($this->_query); | 
| 309 | //exit; | 
| 310 |  | 
| 311 | // make up a command from metadata | 
| 312 | // FIXME: abstract this some more (e.g. via a CommandMapper|Registry) | 
| 313 | switch ($this->_query[metatype]) { | 
| 314 | case 'data': | 
| 315 | //$command = 'queryData'; | 
| 316 | $command = 'getObjects';   // FIXME!!! | 
| 317 | $args = array(); | 
| 318 | switch ($this->_query[vartype]) { | 
| 319 | case 'object': | 
| 320 | if (!$this->_query[classname]) { | 
| 321 | $msg = "_query[vartype] == 'object' requires _query[classname]"; | 
| 322 | user_error("GenericDataSource::do_query() - failed: " . $msg); | 
| 323 | } | 
| 324 | array_push($args, $this->_query[classname]); | 
| 325 | break; | 
| 326 | } | 
| 327 | break; | 
| 328 | case 'schema': | 
| 329 | $command = 'querySchema'; | 
| 330 | break; | 
| 331 | } | 
| 332 |  | 
| 333 | // trace | 
| 334 | $this->debug(null, '<b>= = = = = = = = = = = = = = = = = = = = = =</b>'); | 
| 335 | $this->trace('build_handler_options', array( | 
| 336 | "_locator" => $this->_locator, | 
| 337 | "_query" => $this->_query, | 
| 338 | "command" => '<b>' . $command . "</b>", | 
| 339 | "args" => $args | 
| 340 | )); | 
| 341 |  | 
| 342 | $this->_handler_options = array( | 
| 343 | method => $command, | 
| 344 | args => $args, | 
| 345 | ); | 
| 346 |  | 
| 347 | } | 
| 348 |  | 
| 349 | function fetch_result() { | 
| 350 |  | 
| 351 | $this->build_handler_options(); | 
| 352 | $method = $this->_handler_options[method]; | 
| 353 | $args = $this->_handler_options[args]; | 
| 354 |  | 
| 355 | // pre-flight checks | 
| 356 | if (!$method) { | 
| 357 | $msg = "Remote command could not be resolved, please pass in or check configuration."; | 
| 358 | user_error("GenericDataSource::do_query() - failed: " . $msg); | 
| 359 | return; | 
| 360 | } | 
| 361 |  | 
| 362 | // do remote call here and get result | 
| 363 | // FIXME: handle synchronous/asynchronous mode here!!! | 
| 364 | $this->do_handler_call($method, $args); | 
| 365 | // TODO: ... = $this->poll_handler_result  and  $this->get_handler_result | 
| 366 | $this->_result = $this->_handler->getAttributes(); | 
| 367 |  | 
| 368 | // trace | 
| 369 | if ($this->_debug[notice]) { | 
| 370 | //print "_result = " . Dumper($this->_result); | 
| 371 | print "_result - count = " . sizeof($this->_result) . "<br/>"; | 
| 372 | } | 
| 373 |  | 
| 374 | } | 
| 375 |  | 
| 376 |  | 
| 377 | function do_query() { | 
| 378 | //print "query!<br/>"; | 
| 379 | $this->fetch_result(); | 
| 380 | $this->handle_result(); | 
| 381 | } | 
| 382 |  | 
| 383 | function do_query_schema() { | 
| 384 | user_error("FIXME: do_query_schema"); | 
| 385 | // $this->do_handler_call( ... ); | 
| 386 | } | 
| 387 |  | 
| 388 | function get_header() { | 
| 389 | $this->fetch_result(); | 
| 390 | $this->read_labels_from_result(); | 
| 391 | return $this->get_labels(); | 
| 392 | } | 
| 393 |  | 
| 394 |  | 
| 395 |  | 
| 396 |  | 
| 397 | } | 
| 398 |  | 
| 399 | ?> |