1 | 2 | simandl | <?php |
2 | | | |
3 | | | namespace Phem\Environment; |
4 | | | |
5 | | | use DateTime; |
6 | | | use Phem\Core\Collection; |
7 | | | use Phem\Libraries\MessageBus\CometPusher; |
8 | | | use Ratchet\Wamp\Exception; |
9 | | | |
10 | | | /** |
11 | | | * @author kubapet |
12 | | | */ |
13 | | | class Application |
14 | | | { |
15 | | | private static function injectFCGIRequest($query) |
16 | | | { |
17 | | | |
18 | | | $cmd = "SCRIPT_NAME=".getcwd().DS."index.php " . |
19 | | | "SCRIPT_FILENAME=".getcwd().DS."index.php " . |
20 | | | "DOCUMENT_ROOT=".getcwd()." " . |
21 | | | "HTTP_HOST=".FPM_INJECT_HTTP_HOST." " . |
22 | | | "REQUEST_METHOD=GET " . |
23 | | | "QUERY_STRING=\"".$query."\" " . |
24 | | | "REQUEST_URI=\"/index.php".$query."\" " . |
25 | | | "cgi-fcgi -bind -connect 127.0.0.1:".FPM_INJECT_PORT." > /tmp/app.log &"; |
26 | | | |
27 | | | echo $cmd; |
28 | | | |
29 | | | echo "BEGIN".exec($cmd)."END"; |
30 | | | |
31 | | | |
32 | | | } |
33 | | | |
34 | | | public static function runParallelTask($controller,$task,$args = null) |
35 | | | { |
36 | | | $query = "controller=$controller&task=$task"; |
37 | | | foreach ($args as $key => $arg) |
38 | | | { |
39 | | | $query .= "&$key=$arg"; |
40 | | | } |
41 | | | |
42 | | | self::injectFCGIRequest($query); |
43 | | | } |
44 | | | |
45 | | | /** |
46 | | | * |
47 | | | * @return Collection |
48 | | | */ |
49 | | | public static function getUsers() |
50 | | | { |
51 | | | if (self::getVar("Phem.".APP_NAMESPACE.".Users") == null) |
52 | | | { |
53 | | | self::setVar("Phem.".APP_NAMESPACE.".Users",new Collection()); |
54 | | | } |
55 | | | return self::getVar("Phem.".APP_NAMESPACE.".Users"); |
56 | | | } |
57 | | | |
58 | | | public static function setUsers(Collection $users) |
59 | | | { |
60 | | | self::setVar("Phem.".APP_NAMESPACE.".Users",$users); |
61 | | | } |
62 | | | |
63 | | | /** |
64 | | | * |
65 | | | * @return Collection |
66 | | | */ |
67 | | | public static function getRecentlyLoggedUsers() |
68 | | | { |
69 | | | if (self::getVar("Phem.".APP_NAMESPACE.".RLUsers") == null) |
70 | | | { |
71 | | | self::setVar("Phem.".APP_NAMESPACE.".RLUsers",new Collection()); |
72 | | | } |
73 | | | return self::getVar("Phem.".APP_NAMESPACE.".RLUsers"); |
74 | | | } |
75 | | | |
76 | | | public static function setRecentlyLoggedUsers(Collection $users) |
77 | | | { |
78 | | | self::setVar("Phem.".APP_NAMESPACE.".RLUsers",$users); |
79 | | | } |
80 | | | |
81 | | | public static function onNewConnection($cid,$sid,$uid) |
82 | | | { |
83 | | | |
84 | | | $lock = self::lockVarsWait(); |
85 | | | |
86 | | | $users = self::getUsers(); |
87 | | | $rlUsers = self::getRecentlyLoggedUsers(); |
88 | | | |
89 | | | if (!($users->containsKey($uid))) |
90 | | | { |
91 | | | $users->put($uid,new Collection()); |
92 | | | if (!($rlUsers->containsKey($uid))) |
93 | | | { |
94 | | | $msgArgs = new MessageArgs(); |
95 | | | $msgArgs->setActionUrl(EnvironmentManager::getLinkBuilder() |
96 | | | ->navigate("Administration","loggedUsers")); |
97 | | | $msgArgs->setActionType("replace"); |
98 | | | $msgArgs->setActionTarget("loggedUsers"); |
99 | | | |
100 | | | $msg = new Message; |
101 | | | $msg->setSubject('UĹživatel je online'); |
102 | | | $msg->getArgs()->add($msgArgs); |
103 | | | $msg->setDate(new DateTime(date("Y-m-d"))); |
104 | | | $msg->setFromUsr($uid); |
105 | | | |
106 | | | $msgArgs->setMessage($msg); |
107 | | | |
108 | | | //$msg->setText($uid); |
109 | | | Application::notify($msg); |
110 | | | |
111 | | | } |
112 | | | } |
113 | | | $sessions = $users->get($uid); |
114 | | | |
115 | | | if (!($sessions->containsKey($sid))) |
116 | | | { |
117 | | | $sessions->set($sid,new Collection()); |
118 | | | } |
119 | | | $connections = $sessions->get($sid); |
120 | | | |
121 | | | if (!($connections->containsKey($cid))) |
122 | | | { |
123 | | | $connections->set($cid,$cid); |
124 | | | } |
125 | | | |
126 | | | self::setUsers($users); |
127 | | | $lock->unlock(); |
128 | | | |
129 | | | } |
130 | | | |
131 | | | public static function onConnectionClose($cid) |
132 | | | { |
133 | | | |
134 | | | //$lb = EnvironmentManager::getLinkBuilder(); |
135 | | | //$lb->externalLink(array("controller"=>"dispatcher","")); |
136 | | | |
137 | | | $uid = null; |
138 | | | $sid = null; |
139 | | | |
140 | | | $lock = self::lockVarsWait(); |
141 | | | |
142 | | | $users = self::getUsers(); |
143 | | | foreach ($users as $user => $sessions) |
144 | | | { |
145 | | | foreach ($sessions as $session => $connections) |
146 | | | { |
147 | | | foreach ($connections as $connection) |
148 | | | { |
149 | | | if ($connection == $cid) |
150 | | | { |
151 | | | $uid = $user; |
152 | | | $sid = $session; |
153 | | | break 3; |
154 | | | } |
155 | | | } |
156 | | | } |
157 | | | } |
158 | | | $sessions = $users->get($uid); |
159 | | | $connections = $sessions->get($sid); |
160 | | | $connections->removeKey($cid); |
161 | | | |
162 | | | if ($connections->isEmpty()) |
163 | | | { |
164 | | | $sessions->removeKey($sid); |
165 | | | } |
166 | | | |
167 | | | if ($sessions->isEmpty()) |
168 | | | { |
169 | | | $users->removeKey($uid); |
170 | | | self::setUsers($users); |
171 | | | |
172 | | | $rlUsers = self::getRecentlyLoggedUsers(); |
173 | | | $rlUsers->put($uid,$uid); |
174 | | | self::setRecentlyLoggedUsers($rlUsers); |
175 | | | |
176 | | | $lock->unlock(); |
177 | | | |
178 | | | //exec("php ".getcwd().DS.FRAMEWORK_DIR.DS."Static".DS."userWentOffline.php ".$uid." &"); |
179 | | | //exec("php ".getcwd().DS.FRAMEWORK_DIR.DS."Static".DS."userWentOffline.sh ".$uid." &"); |
180 | | | |
181 | | | self::runParallelTask("Notification", "userWentOffline", array("uid"=>$uid)); |
182 | | | |
183 | | | return; |
184 | | | } |
185 | | | |
186 | | | self::setUsers($users); |
187 | | | $lock->unlock(); |
188 | | | } |
189 | | | |
190 | | | public static function setVar($key, $value, $ttl = 0) |
191 | | | { |
192 | | | switch (strtolower(APPLICATION_SCOPE_STORAGE)) |
193 | | | { |
194 | | | case 'xcache': |
195 | | | if (!function_exists('xcache_set')) |
196 | | | { |
197 | | | throw new ApplicationException('XCache support is missing'); |
198 | | | } |
199 | | | xcache_set($key, serialize($value), $ttl); |
200 | | | break; |
201 | | | |
202 | | | case 'apc': |
203 | | | if (!function_exists('apc_store')) |
204 | | | { |
205 | | | throw new ApplicationException('APC support is missing'); |
206 | | | } |
207 | | | apc_store($key, $value, $ttl); |
208 | | | break; |
209 | | | |
210 | | | default: |
211 | | | case 'disabled': |
212 | | | throw new ApplicationException( |
213 | | | 'Application scope backend not configured' |
214 | | | ); |
215 | | | } |
216 | | | } |
217 | | | |
218 | | | public static function getVar($key) |
219 | | | { |
220 | | | |
221 | | | switch (strtolower(APPLICATION_SCOPE_STORAGE)) |
222 | | | { |
223 | | | case 'xcache': |
224 | | | if (!function_exists('xcache_set')) |
225 | | | { |
226 | | | throw new ApplicationException('XCache support is missing'); |
227 | | | } |
228 | | | return unserialize(\xcache_get($key)); |
229 | | | |
230 | | | case 'apc': |
231 | | | if (!function_exists('apc_store')) |
232 | | | { |
233 | | | throw new ApplicationException('APC support is missing'); |
234 | | | } |
235 | | | return apc_fetch($key); |
236 | | | |
237 | | | default: |
238 | | | case 'disabled': |
239 | | | throw new ApplicationException( |
240 | | | 'Application scope backend not configured' |
241 | | | ); |
242 | | | } |
243 | | | } |
244 | | | |
245 | | | public static function deleteVar($key) |
246 | | | { |
247 | | | switch (strtolower(APPLICATION_SCOPE_STORAGE)) |
248 | | | { |
249 | | | case 'xcache': |
250 | | | if (!function_exists('xcache_set')) |
251 | | | { |
252 | | | throw new ApplicationException('XCache support is missing'); |
253 | | | } |
254 | | | xcache_unset($key); |
255 | | | break; |
256 | | | |
257 | | | case 'apc': |
258 | | | if (!function_exists('apc_store')) |
259 | | | { |
260 | | | throw new ApplicationException('APC support is missing'); |
261 | | | } |
262 | | | apc_delete($key); |
263 | | | break; |
264 | | | |
265 | | | default: |
266 | | | case 'disabled': |
267 | | | throw new ApplicationException( |
268 | | | 'Application scope backend not configured' |
269 | | | ); |
270 | | | } |
271 | | | } |
272 | | | |
273 | | | public static function lockVarsWait() |
274 | | | { |
275 | | | $lock = new \Phem\Core\ExclusiveLock("appscope"); |
276 | | | while(!$lock->lock()) |
277 | | | { |
278 | | | sleep(0.01); |
279 | | | } |
280 | | | return $lock; |
281 | | | } |
282 | | | |
283 | | | public static function getUserSessions($username) |
284 | | | { |
285 | | | $sessions = new Collection(); |
286 | | | |
287 | | | //$iter = new \APCIterator('user', '/user.name/'); |
288 | | | $iter = \xcache_list(XC_TYPE_VAR, 0)['cache_list']; |
289 | | | //print_r($iter); |
290 | | | foreach ($iter as $item) |
291 | | | { |
292 | | | if (strpos($item["name"], 'user.name') === false) |
293 | | | continue; |
294 | | | if (self::getVar($item['name']) == $username) |
295 | | | { |
296 | | | $session = str_replace('user.name.', '', $item['name']); |
297 | | | $sessions->add($session); |
298 | | | } |
299 | | | } |
300 | | | |
301 | | | return $sessions; |
302 | | | } |
303 | | | |
304 | | | public static function getLoggedUsers() |
305 | | | { |
306 | | | |
307 | | | $em = EnvironmentManager::getEntityManager(); |
308 | | | |
309 | | | $usernames = self::getUsers()->getKeys(); |
310 | | | $usersCollection = new Collection(); |
311 | | | |
312 | | | if ((is_array($usernames))&&(count($usernames)>0)) |
313 | | | { |
314 | | | $users = $em->getRepository('Phem\Libraries\Security\Model\MySQL\User') |
315 | | | ->findBy(array('username' => self::getUsers()->getKeys())); |
316 | | | } |
317 | | | else |
318 | | | { |
319 | | | return $usersCollection; |
320 | | | } |
321 | | | |
322 | | | |
323 | | | foreach ($users as $user) |
324 | | | { |
325 | | | $usersCollection->put($user->getUsername(), $user); |
326 | | | } |
327 | | | |
328 | | | return $usersCollection; |
329 | | | } |
330 | | | |
331 | | | /* public static function getLoggedUsers() |
332 | | | { |
333 | | | |
334 | | | // if (!class_exists("\APCIterator")) |
335 | | | // throw new ApplicationException('APC support is missing'); |
336 | | | |
337 | | | $users = new Collection(); |
338 | | | |
339 | | | //$iter = new \APCIterator('user', '/user.name/'); |
340 | | | $iter = \xcache_list(XC_TYPE_VAR, 0)["cache_list"]; |
341 | | | foreach ($iter as $item) |
342 | | | { |
343 | | | if (strpos($item["name"], 'user.name') === false) |
344 | | | continue; |
345 | | | //echo $item['key'].'<br/>'; |
346 | | | $usrOrig = EnvironmentManager::getUserManager()->getUser(self::getVar($item["name"])); |
347 | | | if (!is_a($usrOrig, '\Phem\Libraries\Security\Model\Common\User')) |
348 | | | continue; |
349 | | | |
350 | | | $usr = clone $usrOrig; |
351 | | | $session = str_replace('user.name.', '', $item['name']); |
352 | | | $usr->setSession($session); |
353 | | | |
354 | | | $users->set($usr->getUsername(), $usr); |
355 | | | } |
356 | | | return $users; |
357 | | | } |
358 | | | */ |
359 | | | |
360 | | | public static function notifyRaw($data) |
361 | | | { |
362 | | | if ((MESSAGEBUS_MODE == "Both") || (MESSAGEBUS_MODE == "WebSocket")) |
363 | | | { |
364 | | | $pusherClassName = ROOT_NAMESPACE."\\Libraries\\MessageBus\\" |
365 | | | . WEBSOCKET_PULLER_MODE."Pusher"; |
366 | | | if (class_exists($pusherClassName)) |
367 | | | { |
368 | | | $pusher = new $pusherClassName(); |
369 | | | $pusher->push($data); |
370 | | | } |
371 | | | else if (strtolower(WEBSOCKET_PULLER_MODE) != "disabled") |
372 | | | { |
373 | | | throw new Exception |
374 | | | ("Unsupported mode given by WEBSOCKET_PULLER_MODE"); |
375 | | | } |
376 | | | } |
377 | | | |
378 | | | if ((MESSAGEBUS_MODE == "Both") || (MESSAGEBUS_MODE == "Comet")) |
379 | | | { |
380 | | | $pusher = new CometPusher(); |
381 | | | $pusher->push($data); |
382 | | | } |
383 | | | } |
384 | | | |
385 | | | public static function notify(Message $message) |
386 | | | { |
387 | | | $em = EnvironmentManager::getEntityManager(); |
388 | | | $em->persist($message); |
389 | | | $em->flush(); |
390 | | | $em->clear(); |
391 | | | |
392 | | | |
393 | | | self::notifyRaw($message); |
394 | | | } |
395 | | | |
396 | | | public static function heartbeat($username) |
397 | | | { |
398 | | | self::setVar('user.name.' . EnvironmentManager::getSession()->getId(), $username); |
399 | | | } |
400 | | | |
401 | | | public static function waitForMessages($timeout) |
402 | | | { |
403 | | | self::setVar('user.pid.' . EnvironmentManager::getSession()->getId(), getmypid()); |
404 | | | |
405 | | | if (!function_exists('pcntl_sigtimedwait')) |
406 | | | { |
407 | | | throw new ApplicationException('PCNTL support is missing (only available on POSIX platform)'); |
408 | | | } |
409 | | | |
410 | | | |
411 | | | //suspend session and db connection before waiting for signal |
412 | | | EnvironmentManager::getSession()->writeClose(); |
413 | | | |
414 | | | $info = array(); |
415 | | | |
416 | | | \pcntl_sigtimedwait(array(SIGUSR1), $info, $timeout); |
417 | | | |
418 | | | EnvironmentManager::getSession()->reopen(); |
419 | | | self::deleteVar('user.pid.' . EnvironmentManager::getSession()->getId()); |
420 | | | |
421 | | | $messages = new Collection(); |
422 | | | //echo 'user.queue.' . EnvironmentManager::getSession()->getId(); |
423 | | | //$iter = new \APCIterator('user', '/user.queue.' . EnvironmentManager::getSession()->getId() . '/'); |
424 | | | |
425 | | | $iter = \xcache_list(XC_TYPE_VAR, 0)['cache_list']; |
426 | | | $varsToDelete = new Collection(); |
427 | | | foreach ($iter as $item) |
428 | | | { |
429 | | | if (strpos($item["name"], 'user.queue') === false) |
430 | | | continue; |
431 | | | //echo $item["name"]."<br />"; |
432 | | | $messages->add(self::getVar($item['name'])); |
433 | | | $varsToDelete->add($item['name']); |
434 | | | } |
435 | | | |
436 | | | foreach ($varsToDelete as $var) |
437 | | | { |
438 | | | self::deleteVar($var); |
439 | | | } |
440 | | | |
441 | | | return $messages; |
442 | | | } |
443 | | | |
444 | | | } |