3 года с опозданием, но вот решение, которое я придумал. Бонус: работает в IE7 +
Использование:
- jQuery 1.9.1
- jQuery UI 1.10 (быстрое диалоговое окно и индикатор выполнения)
- Полифилл события Реми
- JSON2, полифилл
Таблица событий:
create table updates(
evt_id int unsigned not null auto_increment,
user_id int unsigned not null,
evt_type enum('start','update','finish') not null,
evt_msg varchar(255) not null,
primary key (evt_id)
)
HTML:
<?php
include 'libconfig.php';
session_write_close();
if(count($_POST)){
$db=db_get_connection();
$stm=new PDOStatementWrapper(db_prepare($db,'INSERT INTO bupdates VALUES (:event_id,:user_id,:type,:message)'));
if($stm->run(array(
':event_id'=>0,
':user_id'=>App::user()->getId(),
':type'=>$_POST['type'],
':message'=>$_POST['message']
)))echo 'Inserted';
return;
}
?>
<!doctype html>
<html>
<head>
<title>tester</title>
<link rel=stylesheet href="s/jquery-ui-1.10.3.custom.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="js/jquery-ui-1.10.3.custom.min.js"></script>
<script src="js/eventsource.js"></script>
<script src="js/json2.js"></script>
<script>
var MixerStatusMonitor=(function(){
var _src=null,
_handler={
onStart:function(e){
MixerStatus.setMax(parseInt(e.data));
},
onUpdate:function(e){
var data=JSON.parse(e.data);
MixerStatus.setValue(parseInt(data.progress));
MixerStatus.setStatus(data.message);
},
onFinish:function(e){
//var data=JSON.parse(e.data);
MixerStatus.hide();
_src.close();
}
};
return {
init:function(){
if(_src)_src.close();
_src=new EventSource('/daemon/updates.php?type=b');
_src.addEventListener('update',_handler.onUpdate,false);
_src.addEventListener('start',_handler.onStart,false);
_src.addEventListener('finish',_handler.onFinish,false);
MixerStatus.show();
}
};
})();
var MixerStatus=(function(){
var dialog=null,pbar=null,text=null;
return {
init:function(){
dialog=$('#buildStatus').dialog({autoOpen:false});
pbar=$('#buildStatus .progress').progressbar({value:false});
text=$('#buildStatus .text').progressbar();
},
setStatus:function(txt){
text.html(txt);
},
setMax:function(val){
pbar.progressbar('option','max',val);
},
setValue:function(val){
pbar.progressbar('option','value',val);
},
show:function(){
dialog.dialog('open');
},
hide:function(){
dialog.dialog('close');
}
};
})();
$(document).ready(function(){
MixerStatus.init();//build the UI
$('#updater').on('submit',function(){
$.ajax({
type:'post',
url:'test-updates.php',
data:$('#updater').serialize(),
beforeSend:function(){
if($('#updater select[name=type]').val()=='start'){
MixerStatusMonitor.init();
}
}
});
return false;
});
});
</script>
</head>
<body>
<p>Start event sets the max
<p>update event: {"progress":"","message":""}
<p>finish event: {"progress":"","message":""}
<form id=updater>
message: <input type=text name=message value="15"><br>
event type: <select name=type>
<option value=start>start</option>
<option value=update>update</option>
<option value=finish>finish</option>
</select><br>
<button>send message</button>
</form>
<div id=buildStatus title="Building">
<div class=text></div>
<div class=progress></div>
</div>
<div id=messages></div>
</body>
</html>
PHP:
<?php
header('Content-Type: text/event-stream');
define('TYPE_BROADCAST','b');
define('MAX_FAILURES',30);//30 seconds
define('MAX_WAIT',30);//30 seconds
define('MAX_START_WAIT',6);//30 seconds
/*
* URL arguments:
* type
*/
include '../libconfig.php';
session_write_close();
if(!App::loggedIn() || !App::user()){
printEvent(0,'finish','Login session has expired.');
}
if($_GET['type']==TYPE_BROADCAST){//not needed;specific to the app I am creating
$db=db_get_connection();
$stm=new PDOStatementWrapper(db_prepare($db,'SELECT * FROM updates WHERE user_id=:user_id AND evt_id>:last_id'));
$args=array(':user_id'=>App::user()->getId(),':last_id'=>0);
$stm->bindParam(':user_id',$args[':user_id'],PDO::PARAM_INT);
$stm->bindParam(':last_id',$args[':last_id'],PDO::PARAM_INT);
$failures=0;
$nomsg=0;
if(!isset($_SERVER['HTTP_LAST_EVENT_ID'])){
$start=new PDOStatementWrapper(db_prepare($db,'SELECT * FROM updates WHERE user_id=:user_id ORDER BY evt_id DESC'));
$start->bindValue(':user_id',$args[':user_id'],PDO::PARAM_INT);
$startwait=0;
while(1){
if($startwait>MAX_START_WAIT){
printEvent(0,'finish','Timed out waiting for the process to start.');
return;
}
sleep(5);
$startwait++;
if(!$start->run()){
printEvent(0,'finish','DB error while getting the starting event.');
return;
}
while($start->loadNext()){
if($start->get('evt_type')=='finish')continue 2;
if($start->get('evt_type')=='start')break;
}
if($start->get('evt_type')=='start'){
$args[':last_id']=$start->get('evt_id');
printEvent($start->get('evt_id'),'start',$start->get('evt_msg'));
break;
}
}
}else
$args[':last_id']=$_SERVER['HTTP_LAST_EVENT_ID'];
if($args[':last_id']===0){
printEvent(0,'finish','ll');
exit;
}
while(1){
sleep(1);
if(!$stm->run()){
$failures++;
if($failures>MAX_FAILURES){
printEvent(0,'finish','Max failures reached.');
break;
}
}
if($stm->loadNext()){
$failures=0;
$nomsg=0;
do{
if($stm->get('evt_type')=='finish')break;
$args[':last_id']=$stm->get('evt_id');
printEvent($stm->get('evt_id'),$stm->get('evt_type'),$stm->get('evt_msg'));
}while($stm->loadNext());
if($stm->get('evt_type')=='finish'){
printEvent($args[':last_id'],'finish',$stm->get('evt_msg'));
break;
}
}else{
$nomsg++;
if($nomsg>MAX_WAIT){
exit;//TODO: test
}
}
}
}else{
printEvent(0,'close','Unknown event type.');
}
function printEvent($id,$name,$data){
echo "id: $id\nevent: $name\n";
if(is_array($data)){
foreach($data as $datum)
echo "data: $datum\n";
echo "\n";
}else
echo "data: $data\n\n";
flush();
if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
$_SERVER['HTTP_X_REQUESTED_WITH']=='XMLHttpRequest')exit;//ajax request. Need to kill the connection.
}
В случае, если вам интересно узнать о PDOStatementWrapper
, источник для него здесь . Извините, он не содержит ничего интегрированного с CodeIgniter.