1<?php 2/** 3 * pfccontainer.class.php 4 * 5 * Copyright � 2006 Stephane Gully <stephane.gully@gmail.com> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the 19 * Free Software Foundation, 51 Franklin St, Fifth Floor, 20 * Boston, MA 02110-1301 USA 21 */ 22 23 require_once dirname(__FILE__)."/pfccontainerinterface.class.php"; 24 require_once dirname(__FILE__)."/pfcurlprocessing.php"; 25 26/** 27 * pfcContainer is an abstract class which define interface 28 * to be implemented by concrete container (example: File) 29 * 30 * @author Stephane Gully <stephane.gully@gmail.com> 31 * @abstract 32 */ 33class pfcContainer extends pfcContainerInterface 34{ 35 var $_container = null; // contains the concrete container instance 36 var $_usememorycache = true; 37 38 39 function &Instance($type = 'File', $usememorycache = true) 40 { 41 static $i; 42 if (!isset($i)) 43 $i = new pfcContainer($type, $usememorycache); 44 return $i; 45 } 46 47 function pfcContainer($type = 'File', $usememorycache = true) 48 { 49 pfcContainerInterface::pfcContainerInterface(); 50 51 $this->_usememorycache = $usememorycache; 52 $type = strtolower($type); 53 54 // create the concrete container instance 55 require_once dirname(__FILE__)."/containers/".$type.".class.php"; 56 $container_classname = "pfcContainer_".$type; 57 $this->_container = new $container_classname(); 58 } 59 function getDefaultConfig() 60 { 61 if ($this->_container) 62 return $this->_container->getDefaultConfig(); 63 else 64 return array(); 65 } 66 function init(&$c) 67 { 68 if ($this->_container) 69 return $this->_container->init($c); 70 } 71 72 /** 73 * Create (connect/join) the nickname into the server or the channel locations 74 * Notice: the caller must take care to update all channels the users joined (use stored channel list into metadata) 75 * @param $chan if NULL then create the user on the server (connect), otherwise create the user on the given channel (join) 76 * @param $nick the nickname to create 77 * @param $nickid is the corresponding nickname id (taken from session) 78 */ 79 /* 80 function createNick($chan, $nick, $nickid) 81 { 82 $c =& pfcGlobalConfig::Instance(); 83 84 if ($nick == '') 85 user_error('pfcContainer::createNick nick is empty', E_USER_ERROR); 86 if ($nickid == '') 87 user_error('pfcContainer::createNick nickid is empty', E_USER_ERROR); 88 89 if ($chan == NULL) $chan = 'SERVER'; 90 91 $this->setMeta("nickid-to-metadata", $nickid, 'nick', $nick); 92 $this->setMeta("metadata-to-nickid", 'nick', $this->encode($nick), $nickid); 93 94 $this->setMeta("nickid-to-channelid", $nickid, $this->encode($chan)); 95 $this->setMeta("channelid-to-nickid", $this->encode($chan), $nickid); 96 97 // update the SERVER channel 98 if ($chan != 'SERVER') $this->updateNick($nickid); 99 100 return true; 101 } 102 */ 103 104 function createNick($nickid, $nick) 105 { 106 $c =& pfcGlobalConfig::Instance(); 107 108 if ($nick == '') 109 user_error('pfcContainer::createNick nick is empty', E_USER_ERROR); 110 if ($nickid == '') 111 user_error('pfcContainer::createNick nickid is empty', E_USER_ERROR); 112 113 $this->setMeta("nickid-to-metadata", $nickid, 'nick', $nick); 114 $this->setMeta("metadata-to-nickid", 'nick', $this->encode($nick), $nickid); 115 116 return true; 117 } 118 119 120 function joinChan($nickid, $chan) 121 { 122 $c =& pfcGlobalConfig::Instance(); 123 124 if ($nickid == '') 125 user_error('pfcContainer::joinChan nickid is empty', E_USER_ERROR); 126 127 if ($chan == NULL) $chan = 'SERVER'; 128 129 $this->setMeta("nickid-to-channelid", $nickid, $this->encode($chan)); 130 $this->setMeta("channelid-to-nickid", $this->encode($chan), $nickid); 131 132 // update the SERVER channel 133 if ($chan == 'SERVER') $this->updateNick($nickid); 134 135 return true; 136 } 137 138 139 140 /** 141 * Remove (disconnect/quit) the nickname from the server or from a channel 142 * Notice: when a user quit, the caller must take care removeNick from each channels ('SERVER' included) 143 * This function takes care to remove all users metadata when he his disconnected from all channels 144 * @param $chan if NULL then remove the user from the 'SERVER' channel, otherwise just remove the user from the given channel (quit) 145 * @param $nickid the nickname id to remove 146 * @return array which contains removed user infos ('nickid', 'nick', 'timestamp') 147 */ 148 function removeNick($chan, $nickid) 149 { 150 $c =& pfcGlobalConfig::Instance(); 151 152 if ($chan == NULL) $chan = 'SERVER'; 153 154 $deleted_user = array(); 155 $deleted_user["nick"] = array(); 156 $deleted_user["nickid"] = array(); 157 $deleted_user["timestamp"] = array(); 158 159 if (!$nickid) return $deleted_user; 160 161 $timestamp = $this->getMeta("channelid-to-nickid", $this->encode('SERVER'), $nickid); 162 if (count($timestamp["timestamp"]) == 0) return $deleted_user; 163 $timestamp = $timestamp["timestamp"][0]; 164 165 $deleted_user["nick"][] = $this->getNickname($nickid); 166 $deleted_user["nickid"][] = $nickid; 167 $deleted_user["timestamp"][] = $timestamp; 168 169 // remove the nickid from the channel list 170 $this->rmMeta('channelid-to-nickid', $this->encode($chan), $nickid); 171 $this->rmMeta('nickid-to-channelid', $nickid, $this->encode($chan)); 172 173 // if the user is the last one to quit this room, 174 // and this room is not a default room, 175 // then clean the room history 176 $channels = array(); 177 foreach($c->channels as $cc) 178 $channels[] = 'ch_'.$cc; // @todo clean this piece of code when the chan and chanid will be refactored 179 if (!in_array($chan, $channels)) 180 { 181 $ret = $this->getOnlineNick($chan); 182 if (count($ret['nickid']) == 0) 183 { 184 $this->rmMeta('channelid-to-msg', $this->encode($chan)); 185 $this->rmMeta('channelid-to-msgid', $this->encode($chan)); 186 } 187 } 188 189 // get the current user's channels list 190 $channels = $this->getMeta("nickid-to-channelid",$nickid); 191 $channels = $channels["value"]; 192 // no more joined channel, just remove the user's metadata 193 if (count($channels) == 0) 194 { 195 // remove the nickname to nickid correspondance 196 $this->rmMeta('metadata-to-nickid', 'nick', $this->encode($this->getNickname($nickid))); 197 // remove disconnected nickname metadata 198 $this->rmMeta('nickid-to-metadata', $nickid); 199 // remove users commands in queue 200 $this->rmMeta("nickid-to-cmdtoplay", $nickid); 201 $this->rmMeta("nickid-to-cmdtoplayid", $nickid); 202 } 203 204 return $deleted_user; 205 } 206 207 /** 208 * Store/update the alive user status on the 'SERVER' channel 209 * The default File container will just touch (update the date) of the nickname file in the 'SERVER' channel. 210 * @param $nickid the nickname id to keep alive 211 */ 212 function updateNick($nickid) 213 { 214 $c =& pfcGlobalConfig::Instance(); 215 216 $chan = 'SERVER'; 217 218 $this->setMeta("nickid-to-channelid", $nickid, $this->encode($chan)); 219 $this->setMeta("channelid-to-nickid", $this->encode($chan), $nickid); 220 return true; 221 } 222 223 /** 224 * Change the user's nickname 225 * As nickname value are stored in user's metadata, this function just update the 'nick' metadata 226 * @param $newnick 227 * @param $oldnick 228 * @return true on success, false on failure (if the oldnick doesn't exists) 229 */ 230 function changeNick($newnick, $oldnick) 231 { 232 $c =& pfcGlobalConfig::Instance(); 233 234 $oldnickid = $this->getNickId($oldnick); 235 $newnickid = $this->getNickId($newnick); 236 if ($oldnickid == "") return false; // the oldnick must be connected 237 if ($newnickid != "") return false; // the newnick must not be inuse 238 239 // remove the oldnick to oldnickid correspondance 240 $this->rmMeta("metadata-to-nickid", 'nick', $this->encode($oldnick)); 241 242 // update the nickname 243 $this->setMeta("nickid-to-metadata", $oldnickid, 'nick', $newnick); 244 $this->setMeta("metadata-to-nickid", 'nick', $this->encode($newnick), $oldnickid); 245 return true; 246 } 247 248 /** 249 * Returns the nickid corresponding to the given nickname 250 * The nickid is a unique id used to identify a user (generated from the browser sessionid) 251 * The nickid is stored in the container when createNick is called. 252 * @param $nick 253 * @return string the nick id 254 */ 255 function getNickId($nick) 256 { 257 $nickid = $this->getMeta("metadata-to-nickid", 'nick', $this->encode($nick), true); 258 $nickid = isset($nickid["value"][0]) ? $nickid["value"][0] : ""; 259 return $nickid; 260 } 261 262 /** 263 * Returns the nickname corresponding the the given nickid 264 * @param $nickid 265 * @return string the corresponding nickname 266 */ 267 function getNickname($nickid) 268 { 269 $nick = $this->getMeta("nickid-to-metadata", $nickid, 'nick', true); 270 $nick = isset($nick["value"][0]) ? $this->decode($nick["value"][0]) : ""; 271 return $nick; 272 } 273 274 /** 275 * Remove (disconnect/quit) the timeouted nicknames 276 * Notice: this function will remove all nicknames which are not uptodate from all his joined channels 277 * @param $timeout 278 * @return array("nickid"=>array("nickid1", ...),"timestamp"=>array(timestamp1, ...)) contains all disconnected nickids and there timestamp 279 */ 280 function removeObsoleteNick($timeout) 281 { 282 $c =& pfcGlobalConfig::Instance(); 283 284 $deleted_user = array('nick'=>array(), 285 'nickid'=>array(), 286 'timestamp'=>array(), 287 'channels'=>array()); 288 $ret = $this->getMeta("channelid-to-nickid", $this->encode('SERVER')); 289 for($i = 0; $i<count($ret['timestamp']); $i++) 290 { 291 $timestamp = $ret['timestamp'][$i]; 292 $nickid = $ret['value'][$i]; 293 if (time() > ($timestamp+$timeout/1000) && $nickid) // user will be disconnected after 'timeout' secondes of inactivity 294 { 295 // get the current user's channels list 296 $channels = array(); 297 $ret2 = $this->getMeta("nickid-to-channelid",$nickid); 298 foreach($ret2["value"] as $userchan) 299 { 300 $userchan = $this->decode($userchan); 301 if ($userchan != 'SERVER') 302 { 303 // disconnect the user from each joined channels 304 $this->removeNick($userchan, $nickid); 305 $channels[] = $userchan; 306 } 307 } 308 // now disconnect the user from the server 309 // (order is important because the SERVER channel has timestamp informations) 310 $du = $this->removeNick('SERVER', $nickid); 311 $channels[] = 'SERVER'; 312 313 $deleted_user["nick"] = array_merge($deleted_user["nick"], $du["nick"]); 314 $deleted_user["nickid"] = array_merge($deleted_user["nickid"], $du["nickid"]); 315 $deleted_user["timestamp"] = array_merge($deleted_user["timestamp"], $du["timestamp"]); 316 $deleted_user["channels"] = array_merge($deleted_user["channels"], array($channels)); 317 } 318 } 319 320 return $deleted_user; 321 } 322 323 /** 324 * Returns the nickname list on the given channel or on the whole server 325 * @param $chan if NULL then returns all connected user, otherwise just returns the channel nicknames 326 * @return array("nickid"=>array("nickid1", ...),"timestamp"=>array(timestamp1, ...)) contains the nickid list with the associated timestamp (laste update time) 327 */ 328 function getOnlineNick($chan) 329 { 330 $c =& pfcGlobalConfig::Instance(); 331 332 if ($chan == NULL) $chan = 'SERVER'; 333 334 $online_user = array('nick'=>array(),'nickid'=>array(),'timestamp'=>array()); 335 $ret = $this->getMeta("channelid-to-nickid", $this->encode($chan)); 336 for($i = 0; $i<count($ret['timestamp']); $i++) 337 { 338 $nickid = $ret['value'][$i]; 339 340 // get timestamp from the SERVER channel 341 $timestamp = $this->getMeta("channelid-to-nickid", $this->encode('SERVER'), $nickid); 342 if (count($timestamp['timestamp']) == 0) continue; 343 $timestamp = $timestamp['timestamp'][0]; 344 345 $online_user["nick"][] = $this->getNickname($nickid); 346 $online_user["nickid"][] = $nickid; 347 $online_user["timestamp"][] = $timestamp; 348 } 349 return $online_user; 350 } 351 352 /** 353 * Returns returns a positive number if the nick is online in the given channel 354 * @param $chan if NULL then check if the user is online on the server, otherwise check if the user has joined the channel 355 * @return false if the user is off line, true if the user is online 356 */ 357 function isNickOnline($chan, $nickid) 358 { 359 if (!$nickid) return false; 360 if ($chan == NULL) $chan = 'SERVER'; 361 362 $ret = $this->getMeta("channelid-to-nickid", 363 $this->encode($chan), 364 $nickid); 365 366 return (count($ret['timestamp']) > 0); 367 } 368 369 /** 370 * Write a command to the given channel or to the server 371 * Notice: a message is very generic, it can be a misc command (notice, me, ...) 372 * @param $chan if NULL then write the message on the server, otherwise just write the message on the channel message pool 373 * @param $nick is the sender nickname 374 * @param $cmd is the command name (ex: "send", "nick", "kick" ...) 375 * @param $param is the command' parameters (ex: param of the "send" command is the message) 376 * @return $msg_id the created message identifier 377 */ 378 function write($chan, $nick, $cmd, $param) 379 { 380 $c =& pfcGlobalConfig::Instance(); 381 if ($chan == NULL) $chan = 'SERVER'; 382 383 $msgid = $this->_requestMsgId($chan); 384 385 // format message 386 $data = "\n"; 387 $data .= $msgid."\t"; 388 $data .= time()."\t"; 389 $data .= $nick."\t"; 390 $data .= $cmd."\t"; 391 $data .= $param; 392 393 // write message 394 $this->setMeta("channelid-to-msg", $this->encode($chan), $msgid, $data); 395 396 // delete the obsolete message 397 $old_msgid = $msgid - $c->max_msg - 20; 398 if ($old_msgid > 0) 399 $this->rmMeta("channelid-to-msg", $this->encode($chan), $old_msgid); 400 401 return $msgid; 402 } 403 404 /** 405 * Read the last posted commands from a channel or from the server 406 * Notice: the returned array is ordered by id 407 * @param $chan if NULL then read from the server, otherwise read from the given channel 408 * @param $from_id read all message with a greater id 409 * @return array() contains the formated command list 410 */ 411 function read($chan, $from_id) 412 { 413 $c =& pfcGlobalConfig::Instance(); 414 if ($chan == NULL) $chan = 'SERVER'; 415 416 // read new messages content + parse content 417 $new_from_id = $this->getLastId($chan); 418 $datalist = array(); 419 for ( $mid = $from_id; $mid <= $new_from_id; $mid++ ) 420 { 421 $line = $this->getMeta("channelid-to-msg", $this->encode($chan), $mid, true); 422 $line = $line["value"][0]; 423 if ($line != "" && $line != "\n") 424 { 425 $formated_line = explode( "\t", $line ); 426 $data = array(); 427 $data["id"] = trim($formated_line[0]); 428 $data["timestamp"] = $formated_line[1]; 429 $data["sender"] = $formated_line[2]; 430 $data["cmd"] = $formated_line[3]; 431 // convert URLs to html 432 $data["param"] = $formated_line[4]; 433 $datalist[$data["id"]] = $data; 434 } 435 } 436 return array("data" => $datalist, 437 "new_from_id" => $new_from_id+1 ); 438 } 439 440 /** 441 * Returns the last message id 442 * Notice: the default file container just returns the messages.index file content 443 * @param $chan if NULL then read if from the server, otherwise read if from the given channel 444 * @return int is the last posted message id 445 */ 446 function getLastId($chan) 447 { 448 if ($chan == NULL) $chan = 'SERVER'; 449 450 $lastmsgid = $this->getMeta("channelid-to-msgid", $this->encode($chan), 'lastmsgid', true); 451 if (count($lastmsgid["value"]) == 0) 452 $lastmsgid = 0; 453 else 454 $lastmsgid = $lastmsgid["value"][0]; 455 return $lastmsgid; 456 } 457 458 459 /** 460 * Return a unique id. Each time this function is called, the last id is incremented. 461 * used internaly 462 * @private 463 */ 464 function _requestMsgId($chan) 465 { 466 if ($chan == NULL) $chan = 'SERVER'; 467 468 $lastmsgid = $this->incMeta("channelid-to-msgid", $this->encode($chan), 'lastmsgid'); 469 470 if (count($lastmsgid["value"]) == 0) 471 $lastmsgid = 0; 472 else 473 $lastmsgid = $lastmsgid["value"][0]; 474 return $lastmsgid; 475 } 476 477 /** 478 * Remove all created data for this server (identified by serverid) 479 * Notice: for the default File container, it's just a recursive directory remove 480 */ 481 function clear() 482 { 483 $this->rmMeta(NULL); 484 } 485 486 function getAllUserMeta($nickid) 487 { 488 $result = array(); 489 $ret = $this->getMeta("nickid-to-metadata", $nickid); 490 foreach($ret["value"] as $k) 491 $result[$k] = $this->getUserMeta($nickid, $k); 492 // $result['chanid'] = $this->getMeta("nickid-to-channelid", $nickid); 493 // $result['chanid'] = $result['chanid']['value']; 494 return $result; 495 } 496 497 function getUserMeta($nickid, $key = NULL) 498 { 499 $ret = $this->getMeta("nickid-to-metadata", $nickid, $key, true); 500 return isset($ret['value'][0]) ? $ret['value'][0] : NULL; 501 } 502 503 function setUserMeta($nickid, $key, $value) 504 { 505 $ret = $this->setMeta("nickid-to-metadata", $nickid, $key, $value); 506 return $ret; 507 } 508 509 function getCmdMeta($nickid, $key = NULL) 510 { 511 $ret = $this->getMeta("nickid-to-cmdtoplay", $nickid, $key, true); 512 return $ret['value']; 513 } 514 515 function setCmdMeta($nickid, $key, $value) 516 { 517 $ret = $this->setMeta("nickid-to-cmdtoplay", $nickid, $key, $value); 518 return $ret; 519 } 520 521 function getAllChanMeta($chan) 522 { 523 $result = array(); 524 $ret = $this->getMeta("channelid-to-metadata", $this->encode($chan)); 525 foreach($ret["value"] as $k) 526 $result[$k] = $this->getChanMeta($chan, $k); 527 return $result; 528 } 529 530 function getChanMeta($chan, $key = NULL) 531 { 532 $ret = $this->getMeta("channelid-to-metadata", $this->encode($chan), $key, true); 533 return isset($ret['value'][0]) ? $ret['value'][0] : NULL; 534 } 535 536 function setChanMeta($chan, $key, $value) 537 { 538 $ret = $this->setMeta("channelid-to-metadata", $this->encode($chan), $key, $value); 539 return $ret; 540 } 541 542 var $_cache = array(); 543 544 /** 545 * Write a meta data value identified by a group / subgroup / leaf [with a value] 546 * As an example in the default file container this arborescent structure is modelised by simple directories 547 * group1/subgroup1/leaf1 548 * /leaf2 549 * /subgroup2/... 550 * Each leaf can contain or not a value. 551 * However each leaf and each group/subgroup must store the lastmodified time (timestamp). 552 * @param $group root arborescent element 553 * @param $subgroup is the root first child which contains leafs 554 * @param $leaf is the only element which can contain values 555 * @param $leafvalue NULL means the leaf will not contain any values 556 * @return 1 if the old leaf has been overwritten, 0 if a new leaf has been created 557 */ 558 function setMeta($group, $subgroup, $leaf, $leafvalue = NULL) 559 { 560 $ret = $this->_container->setMeta($group, $subgroup, $leaf, $leafvalue); 561 562 if ($this->_usememorycache) 563 { 564 // store the modifications in the cache 565 if (isset($this->_cache[$group]['value']) && 566 !in_array($subgroup, $this->_cache[$group]['value'])) 567 { 568 $this->_cache[$group]['value'][] = $subgroup; 569 $this->_cache[$group]['timestamp'][] = time(); 570 } 571 if (isset($this->_cache[$group]['childs'][$subgroup]['value']) && 572 !in_array($leaf, $this->_cache[$group]['childs'][$subgroup]['value'])) 573 { 574 $this->_cache[$group]['childs'][$subgroup]['value'][] = $leaf; 575 $this->_cache[$group]['childs'][$subgroup]['timestamp'][] = time(); 576 } 577 $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['value'] = array($leafvalue); 578 $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['timestamp'] = array(time()); 579 } 580 581 return $ret; 582 } 583 584 585 /** 586 * Read meta data identified by a group [/ subgroup [/ leaf]] 587 * @param $group is mandatory, it's the arborescence's root 588 * @param $subgroup if null then the subgroup list names are returned 589 * @param $leaf if null then the leaf names are returned 590 * @param $withleafvalue if set to true the leaf value will be returned 591 * @return array which contains two subarray 'timestamp' and 'value' 592 */ 593 function getMeta($group, $subgroup = null, $leaf = null, $withleafvalue = false) 594 { 595 $ret = array('timestamp' => array(), 596 'value' => array()); 597 598 if ($this->_usememorycache) 599 { 600 // check if the data exists in the cache 601 $incache = false; 602 if ($subgroup == null && 603 isset($this->_cache[$group]['value'])) 604 { 605 $incache = true; 606 $ret = $this->_cache[$group]; 607 } 608 else if ($leaf == null && 609 isset($this->_cache[$group]['childs'][$subgroup]['value'])) 610 { 611 $incache = true; 612 $ret = $this->_cache[$group]['childs'][$subgroup]; 613 } 614 else 615 { 616 if ($withleafvalue) 617 { 618 if (isset($this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['value'])) 619 { 620 $incache = true; 621 $ret = $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]; 622 } 623 } 624 else 625 { 626 if (isset($this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['timestamp'])) 627 { 628 $incache = true; 629 $ret = $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]; 630 } 631 } 632 } 633 634 if ($incache) 635 { 636 $ret2 = array(); 637 if (isset($ret['timestamp'])) $ret2['timestamp'] = $ret['timestamp']; 638 if (isset($ret['value'])) $ret2['value'] = $ret['value']; 639 return $ret2; 640 } 641 } 642 643 // get the fresh data 644 $ret = $this->_container->getMeta($group, $subgroup, $leaf, $withleafvalue); 645 646 if ($this->_usememorycache) 647 { 648 // store in the cache 649 if ($subgroup == null) 650 { 651 $this->_cache[$group]['value'] = $ret['value']; 652 $this->_cache[$group]['timestamp'] = $ret['timestamp']; 653 } 654 else if ($leaf == null) 655 { 656 $this->_cache[$group]['childs'][$subgroup]['value'] = $ret['value']; 657 $this->_cache[$group]['childs'][$subgroup]['timestamp'] = $ret['timestamp']; 658 } 659 else 660 { 661 if ($withleafvalue) 662 $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['value'] = $ret['value']; 663 else 664 unset($this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['value']); 665 $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['timestamp'] = $ret['timestamp']; 666 } 667 } 668 669 return $ret; 670 } 671 672 /** 673 * Increment a counter identified by the following path : group / subgroup / leaf 674 * Notice: this step must be atomic in order to avoid multithread problem (don't forget to use locking features) 675 * @param $group is mandatory 676 * @param $subgroup is mandatory 677 * @param $leaf is mandatory, it's the counter name 678 * @return array which contains two subarray 'timestamp' and 'value' (value contains the incremented numeric value) 679 */ 680 function incMeta($group, $subgroup, $leaf) 681 { 682 $ret = $this->_container->incMeta($group, $subgroup, $leaf); 683 684 if ($this->_usememorycache) 685 { 686 // store the modifications in the cache 687 if (isset($this->_cache[$group]['value']) && 688 !in_array($subgroup, $this->_cache[$group]['value'])) 689 { 690 $this->_cache[$group]['value'][] = $subgroup; 691 $this->_cache[$group]['timestamp'][] = time(); 692 } 693 if (isset($this->_cache[$group]['childs'][$subgroup]['value']) && 694 !in_array($leaf, $this->_cache[$group]['childs'][$subgroup]['value'])) 695 { 696 $this->_cache[$group]['childs'][$subgroup]['value'][] = $leaf; 697 $this->_cache[$group]['childs'][$subgroup]['timestamp'][] = time(); 698 } 699 $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['value'] = $ret['value']; 700 $this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]['timestamp'] = array(time()); 701 } 702 703 return $ret; 704 } 705 706 /** 707 * Remove a meta data or a group of metadata 708 * @param $group if null then it will remove all the possible groups (all the created metadata) 709 * @param $subgroup if null then it will remove the $group's childs (all the subgroup contained by $group) 710 * @param $leaf if null then it will remove all the $subgroup's childs (all the leafs contained by $subgroup) 711 * @return true on success, false on error 712 */ 713 function rmMeta($group, $subgroup = null, $leaf = null) 714 { 715 if ($this->_usememorycache) 716 { 717 // remove from the cache 718 if ($group == null) 719 $this->_cache = array(); 720 else if ($subgroup == null) 721 unset($this->_cache[$group]); 722 else if ($leaf == null) 723 { 724 if (isset($this->_cache[$group]['value'])) 725 { 726 $i = array_search($subgroup,$this->_cache[$group]['value']); 727 if ($i !== FALSE) 728 { 729 unset($this->_cache[$group]['value'][$i]); 730 unset($this->_cache[$group]['timestamp'][$i]); 731 } 732 } 733 unset($this->_cache[$group]['childs'][$subgroup]); 734 } 735 else 736 { 737 if (isset($this->_cache[$group]['childs'][$subgroup]['value'])) 738 { 739 $i = array_search($leaf,$this->_cache[$group]['childs'][$subgroup]['value']); 740 if ($i !== FALSE) 741 { 742 unset($this->_cache[$group]['childs'][$subgroup]['value'][$i]); 743 unset($this->_cache[$group]['childs'][$subgroup]['timestamp'][$i]); 744 } 745 } 746 unset($this->_cache[$group]['childs'][$subgroup]['childs'][$leaf]); 747 } 748 } 749 750 return $this->_container->rmMeta($group, $subgroup, $leaf); 751 } 752 753 /** 754 * In the default File container: used to encode UTF8 strings to ASCII filenames 755 * This method can be overridden by the concrete container 756 */ 757 function encode($str) 758 { 759 return $this->_container->encode($str); 760 } 761 762 /** 763 * In the default File container: used to decode ASCII filenames to UTF8 strings 764 * This method can be overridden by the concrete container 765 */ 766 function decode($str) 767 { 768 return $this->_container->decode($str); 769 } 770} 771 772?> 773