/Class/Comet/Storage.php

https://github.com/jmarien/phpBayeux · PHP · 316 lines · 239 code · 32 blank · 45 comment · 33 complexity · 0ba2d9a13d47f64e36a3aef962065a55 MD5 · raw file

  1. <?php
  2. /**
  3. * @todo refactor storage backend out to an abstract driver interface
  4. * @todo implement/try a redis backend
  5. * @todo test memcached again, on 64bit Ubuntu laptop everything was ok, on 32 Ubuntu PC not.
  6. */
  7. class Comet_Storage {
  8. protected $_channel = null;
  9. protected $_memcacheServer = '127.0.0.1';
  10. protected $_memcachePort = 11211;
  11. protected $_cacheBackend = null;
  12. protected $_cacheType = 'memcache';
  13. public function __construct() {
  14. $this->_loadBackend();
  15. }
  16. public function setChannel($channel) {
  17. //_log(' '.__FILE__.' '.__LINE__.' | set channel to '.$this->_normalizeName($channel));
  18. $this->_channel = $this->_normalizeName($channel);
  19. }
  20. /**
  21. *
  22. * @return string channel name
  23. */
  24. public function getChannel() {
  25. return $this->_channel;
  26. }
  27. /**
  28. *
  29. * @param string $clientid
  30. * @return array
  31. */
  32. public function getClientData($clientid) {
  33. $channel = $this->getChannel();
  34. $lastid = 0;
  35. switch ($this->_cacheType) {
  36. case 'file':
  37. $clientPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'client_'.$clientid;
  38. //_log(' '.__FILE__.' '.__LINE__.' | get client info from '.$clientPath);
  39. $clientinfo = unserialize(file_get_contents($clientPath));
  40. if (isset($clientinfo[$channel])) {
  41. $lastid = $clientinfo[$channel];
  42. }
  43. break;
  44. case 'memcache':
  45. //_log(' '.__FILE__.' '.__LINE__.' | get client info from client_'.$clientid);
  46. $clientinfo = $this->_cacheBackend->get('client_'.$clientid);
  47. if (isset($clientinfo[$channel])) {
  48. $lastid = $clientinfo[$channel];
  49. }
  50. break;
  51. }
  52. $data = $this->_getChannelData();
  53. //_log(' '.__FILE__.' '.__LINE__.' | channel data restored from storage '.print_r($data,1));
  54. //_log(' '.__FILE__.' '.__LINE__.' | checking for position '.$lastid);
  55. if (isset($data[$lastid])) {
  56. //_log(' '.__FILE__.' '.__LINE__.' | channel data found for client on position '.$lastid);
  57. $length = count($data) - $lastid;
  58. $result = array_slice($data,$lastid, $length, true);
  59. $keys = array_keys($result);
  60. $lastid = array_pop($keys);
  61. if ($lastid > 0) {
  62. $this->_setClientInfo($clientid, $lastid + 1);
  63. }
  64. return array_values($result);
  65. } else {
  66. //_log(' '.__FILE__.' '.__LINE__.' | no channel data found for client '.$lastid);
  67. return array();
  68. }
  69. }
  70. public function getClientChannels($clientid) {
  71. switch ($this->_cacheType) {
  72. case 'file':
  73. $clientPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'client_'.$clientid;
  74. //_log(' '.__FILE__.' '.__LINE__.' | get client info from '.$clientPath);
  75. if (file_exists($clientPath)) {
  76. $clientinfo = unserialize(file_get_contents($clientPath));
  77. return($clientinfo);
  78. }
  79. break;
  80. case 'memcache':
  81. //_log(' '.__FILE__.' '.__LINE__.' | get client info from client_'.$clientid);
  82. $clientinfo = $this->_cacheBackend->get('client_'.$clientid);
  83. if ($clientinfo) {
  84. return $clientinfo;
  85. }
  86. break;
  87. }
  88. return false;
  89. }
  90. public function addClient($clientid) {
  91. //_log(' '.__FILE__.' '.__LINE__.' | add client '.$clientid);
  92. $data = $this->_getChannelData();
  93. $lastid = count($data);
  94. $this->_setClientInfo($clientid,$lastid);
  95. }
  96. public function removeClient($clientid) {
  97. switch ($this->_cacheType) {
  98. case 'file':
  99. $clientPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'client_'.$clientid;
  100. $clientinfo = unserialize(file_get_contents($clientPath));
  101. foreach($clientinfo as $channel => $id) {
  102. $this->setChannel($channel);
  103. $this->_deleteChannelUser($clientId);
  104. }
  105. unlink($clientPath);
  106. break;
  107. case 'memcache':
  108. $clientinfo = $this->_cacheBackend->get('client_'.$clientid);
  109. foreach($clientinfo as $channel => $id) {
  110. $this->setChannel($channel);
  111. $this->_deleteChannelUser($clientid);
  112. }
  113. //_log(' '.__FILE__.' '.__LINE__.' | remove client .'.$clientid.' from channel '.$channel);
  114. $this->_cacheBackend->delete('client_'.$clientid);
  115. break;
  116. }
  117. }
  118. public function removeClientFromChannel($clientid) {
  119. //_log(' '.__FILE__.' '.__LINE__.' | add client '.$clientid);
  120. $data = $this->_getChannelData();
  121. $lastid = count($data);
  122. $this->_addChannelUser($clientid);
  123. $this->_setClientInfo($clientid,NULL);
  124. }
  125. public function addClientToChannel($clientid) {
  126. //_log(' '.__FILE__.' '.__LINE__.' | add client '.$clientid);
  127. $data = $this->_getChannelData();
  128. $lastid = count($data);
  129. $this->_addChannelUser($clientid);
  130. $this->_setClientInfo($clientid,$lastid);
  131. }
  132. public function addChannelData($data) {
  133. $channel = $this->getChannel();
  134. switch ($this->_cacheType) {
  135. case 'file':
  136. $channelPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'channel_'.$channel;
  137. $channelinfo = unserialize(file_get_contents($channelPath));
  138. if (is_array($channelinfo)) {
  139. $channelinfo[] = $data;
  140. } else {
  141. $channelinfo[0] = $data;
  142. }
  143. //_log(' '.__FILE__.' '.__LINE__.' | set channel info to '.$channelPath);
  144. file_put_contents($channelPath, serialize($channelinfo));
  145. break;
  146. case 'memcache':
  147. $channelinfo = $this->_cacheBackend->get('channel_'.$channel);
  148. if (is_array($channelinfo)) {
  149. $channelinfo[] = $data;
  150. } else {
  151. $channelinfo = array();
  152. $channelinfo[0] = $data;
  153. }
  154. //_log(' '.__FILE__.' '.__LINE__.' | set channel info to channel_'.$channel);
  155. $this->_cacheBackend->set('channel_'.$channel,$channelinfo);
  156. break;
  157. }
  158. }
  159. protected function _getChannelData() {
  160. $channel = $this->getChannel();
  161. $channelinfo = array();
  162. switch ($this->_cacheType) {
  163. case 'file':
  164. $channelPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'channel_'.$channel;
  165. //_log(' '.__FILE__.' '.__LINE__.' | get channel info from '.$channelPath);
  166. $channelinfo = unserialize(file_get_contents($channelPath));
  167. break;
  168. case 'memcache':
  169. //_log(' '.__FILE__.' '.__LINE__.' | get channel info from channel_'.$channel);
  170. $channelinfo = $this->_cacheBackend->get('channel_'.$channel);
  171. break;
  172. }
  173. return $channelinfo;
  174. }
  175. protected function _getChannelUsers() {
  176. $channel = $this->getChannel();
  177. $channelinfo = array();
  178. switch ($this->_cacheType) {
  179. case 'file':
  180. $channelUsersPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'channelusers_'.$channel;
  181. //_log(' '.__FILE__.' '.__LINE__.' | get channel users from '.$channelPath);
  182. $channelusers = unserialize(file_get_contents($channelUsersPath));
  183. break;
  184. case 'memcache':
  185. //_log(' '.__FILE__.' '.__LINE__.' | get channel info from channel_'.$channel);
  186. $channelusers = $this->_cacheBackend->get('channelusers_'.$channel);
  187. break;
  188. }
  189. return $channelusers;
  190. }
  191. protected function _addChannelUser($clientid) {
  192. $users = $this->_getChannelUsers();
  193. if (!in_array($clientid,$users)) {
  194. $users[] = $clientid;
  195. $this->_saveChannelUsers($users);
  196. }
  197. }
  198. protected function _deleteChannelUser($clientid) {
  199. $users = $this->_getChannelUsers();
  200. if (in_array($clientid,$users)) {
  201. $users_flip = array_flip($users);
  202. unset($users_flip[$clientid]);
  203. $users = array_flip($users_flip);
  204. $this->_saveChannelUsers($users);
  205. }
  206. }
  207. protected function _saveChannelUsers($users) {
  208. $channel = $this->getChannel();
  209. switch ($this->_cacheType) {
  210. case 'file':
  211. $channelUsersPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'channelusers_'.$channel;
  212. //_log(' '.__FILE__.' '.__LINE__.' | saving channel users to '.$channelUsersPath);
  213. file_put_contents($channelUsersPath, serialize($users));
  214. break;
  215. case 'memcache':
  216. //_log(' '.__FILE__.' '.__LINE__.' | save channel users to channelusers_'.$channel);
  217. $this->_cacheBackend->set('channelusers_'.$channel,$users);
  218. break;
  219. }
  220. }
  221. protected function _setClientInfo($clientid,$lastid = NULL) {
  222. $channel = $this->getChannel();
  223. //_log(' '.__FILE__.' '.__LINE__.' | store clientid,channel,lastid: '.$clientid.' - '.$channel.' - '.$lastid);
  224. switch ($this->_cacheType) {
  225. case 'file':
  226. $clientPath = $this->_cacheBackend . DIRECTORY_SEPARATOR . 'client_'.$clientid;
  227. //_log(' '.__FILE__.' '.__LINE__.' | store client info to '.$clientPath);
  228. $clientinfo = unserialize(file_get_contents($clientPath));
  229. if (is_null($lastid)) {
  230. unset($clientinfo[$channel]);
  231. } else {
  232. $clientinfo[$channel] = $lastid;
  233. }
  234. //_log(' '.__FILE__.' '.__LINE__.' | store client info to '.$clientPath.' :'.print_r($clientinfo,1));
  235. file_put_contents($clientPath, serialize($clientinfo));
  236. break;
  237. case 'memcache':
  238. $clientinfo = $this->_cacheBackend->get('client_'.$clientid);
  239. if (!is_array($clientinfo)) {
  240. $clientinfo = array();
  241. }
  242. if (isset($clientinfo[$channel])) {
  243. if (is_null($lastid)) {
  244. unset($clientinfo[$channel]);
  245. } else {
  246. $clientinfo[$channel] = $lastid;
  247. }
  248. } else {
  249. $clientinfo[$channel] = $lastid;
  250. }
  251. //_log(' '.__FILE__.' '.__LINE__.' | store client info to client_'.$clientid.' :'.print_r($clientinfo,1));
  252. $this->_cacheBackend->set('client_'.$clientid,$clientinfo);
  253. $val = $this->_cacheBackend->get('client_'.$clientid);
  254. //_log(' '.__FILE__.' '.__LINE__.' | client info stored :'.print_r($val,1));
  255. break;
  256. }
  257. }
  258. protected function _normalizeName($channel) {
  259. return str_replace(array('/'),array('_'),strtolower($channel));
  260. }
  261. protected function _loadBackend() {
  262. //_log(' '.__FILE__.' '.__LINE__.' | load storage backend');
  263. $memcache = false;
  264. if (class_exists('Memcache')) {
  265. $memcache = new Memcache;
  266. $memcache->connect($this->_memcacheServer, $this->_memcachePort);
  267. //_log(' '.__FILE__.' '.__LINE__.' | memcache version '.$memcache->getVersion());
  268. }
  269. //$memcache = false;
  270. if ($memcache) {
  271. //_log(' '.__FILE__.' '.__LINE__.' | storage backend = memcache ');
  272. $this->_cacheBackend = $memcache;
  273. $this->_cacheType = 'memcache';
  274. } else {
  275. //_log(' '.__FILE__.' '.__LINE__.' | storage backend = file based at '.sys_get_temp_dir());
  276. if (is_dir('/dev/shm')) {
  277. $this->_cacheBackend = '/dev/shm';
  278. } else {
  279. $this->_cacheBackend = sys_get_temp_dir();
  280. }
  281. $this->_cacheType = 'file';
  282. }
  283. }
  284. }