new egw::on_shutdown($callback, $args) method to register shutdonw handlers to run after output send back to user, thought only really working with fastCGI, Apache mod_php waits ...

This commit is contained in:
Ralf Becker 2014-02-21 15:38:38 +00:00
parent 68849834f9
commit a786894c27
3 changed files with 83 additions and 41 deletions

View File

@ -171,8 +171,6 @@ class egw extends egw_minimal
$this->preferences = new preferences(); $this->preferences = new preferences();
$this->applications = new applications(); $this->applications = new applications();
register_shutdown_function(array($this, 'shutdown'));
if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' && $GLOBALS['egw_info']['flags']['currentapp'] != 'logout') if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' && $GLOBALS['egw_info']['flags']['currentapp'] != 'logout')
{ {
$this->verify_session(); $this->verify_session();
@ -211,8 +209,6 @@ class egw extends egw_minimal
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']); date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
} }
register_shutdown_function(array($this, 'shutdown'));
$this->define_egw_constants(); $this->define_egw_constants();
} }
@ -538,20 +534,70 @@ class egw extends egw_minimal
} }
/** /**
* eGW's shutdown handler * registered shutdown callbacks and optional arguments
*
* @var array
*/ */
function shutdown() private static $shutdown_callbacks = array();
/**
* Register a callback to run on shutdown AFTER output send to user
*
* Allows eg. static classes (no destructor) to run on shutdown AND
* garanties to run AFTER output send to user.
*
* @param callable $callback use array($classname, $method) for static methods
* @param array $args=array()
*/
public static function on_shutdown($callback, array $args=array())
{
array_unshift($args, $callback);
// prepend new callback, to run them in oposite order they are registered
array_unshift(self::$shutdown_callbacks, $args);
}
/**
* Shutdown handler running all registered on_shutdown callbacks and then disconnecting from db
*/
function __destruct()
{ {
if (!defined('EGW_SHUTDOWN')) if (!defined('EGW_SHUTDOWN'))
{ {
define('EGW_SHUTDOWN',True); define('EGW_SHUTDOWN',True);
if (class_exists('egw_link',false)) // false = no autoload! // send json response BEFORE flushing output
if (egw_json_request::isJSONRequest())
{ {
egw_link::save_session_cache(); egw_json_response::sendResult();
}
// flush all output to user
/* does NOT work on Apache :-(
for($i = 0; ob_get_level() && $i < 10; ++$i)
{
ob_end_flush();
}
flush();*/
// working for fastCGI :-)
if (function_exists('fastcgi_finish_request'))
{
fastcgi_finish_request();
}
// run all on_shutdown, do NOT stop on exceptions
foreach(self::$shutdown_callbacks as $data)
{
try {
//error_log(__METHOD__."() running ".array2string($data));
$callback = array_shift($data);
call_user_func_array($callback, $data);
}
catch (Exception $ex) {
_egw_log_exception($ex);
}
} }
// call the asyncservice check_run function if it is not explicitly set to cron-only // call the asyncservice check_run function if it is not explicitly set to cron-only
//
if (!$GLOBALS['egw_info']['server']['asyncservice']) // is default if (!$GLOBALS['egw_info']['server']['asyncservice']) // is default
{ {
ExecMethod('phpgwapi.asyncservice.check_run','fallback'); ExecMethod('phpgwapi.asyncservice.check_run','fallback');

View File

@ -122,7 +122,6 @@ class egw_json_request
error_log($_SERVER['PHP_SELF']. ' stopped for security reason. '.$menuaction.' is not valid. class- or function-name must start with ajax!!!'); error_log($_SERVER['PHP_SELF']. ' stopped for security reason. '.$menuaction.' is not valid. class- or function-name must start with ajax!!!');
// send message also to the user // send message also to the user
throw new Exception($_SERVER['PHP_SELF']. ' stopped for security reason. '.$menuaction.' is not valid. class- or function-name must start with ajax!!!'); throw new Exception($_SERVER['PHP_SELF']. ' stopped for security reason. '.$menuaction.' is not valid. class- or function-name must start with ajax!!!');
exit;
} }
if (isset($template)) if (isset($template))
@ -212,7 +211,7 @@ class egw_json_response
*/ */
public function haveJSONResponse() public function haveJSONResponse()
{ {
return (boolean) $this->responseArray; return $this->responseArray || $this->beforeSendDataProcs;
} }
/** /**
@ -220,19 +219,22 @@ class egw_json_response
*/ */
private function sendHeader() private function sendHeader()
{ {
$file = $line = null;
if (headers_sent($file, $line)) if (headers_sent($file, $line))
{ {
error_log(__METHOD__."() header already sent by $file line $line: ".function_backtrace()); error_log(__METHOD__."() header already sent by $file line $line: ".function_backtrace());
} }
else else
//Send the character encoding header {
header('content-type: application/json; charset='.translation::charset()); //Send the character encoding header
header('content-type: application/json; charset='.translation::charset());
}
} }
/** /**
* Private function which is used to send the result via HTTP * Private function which is used to send the result via HTTP
*/ */
public function sendResult() public static function sendResult()
{ {
$inst = self::get(); $inst = self::get();
@ -265,7 +267,7 @@ class egw_json_response
*/ */
public function printOutput() public function printOutput()
{ {
// do nothing, as output is triggered by destructor // do nothing, as output is triggered by egw::__destruct()
} }
/** /**
@ -288,7 +290,7 @@ class egw_json_response
public function initResponseArray() public function initResponseArray()
{ {
$return = $this->responseArray; $return = $this->responseArray;
$this->responseArray = array(); $this->responseArray = $this->beforeSendDataProcs = array();
$this->hasData = false; $this->hasData = false;
return $return; return $return;
} }
@ -571,16 +573,6 @@ class egw_json_response
'params' => $params 'params' => $params
); );
} }
/**
* Destructor
*/
public function __destruct()
{
//Only send the response if this instance is the singleton instance
if ($this == self::get())
$this->sendResult();
}
} }
/** /**

View File

@ -216,6 +216,10 @@ class egw_link extends solink
{ {
self::$file_access_cache = array(); self::$file_access_cache = array();
} }
// register self::save_session_cache to run on shutdown
egw::on_shutdown(array(__CLASS__, 'save_session_cache'));
//error_log(__METHOD__.'() items in title-cache: '.count(self::$title_cache).' file-access-cache: '.count(self::$file_access_cache)); //error_log(__METHOD__.'() items in title-cache: '.count(self::$title_cache).' file-access-cache: '.count(self::$file_access_cache));
} }
@ -260,9 +264,6 @@ class egw_link extends solink
//error_log(__METHOD__.'() items in title-cache: '.count(self::$title_cache).' file-access-cache: '.count(self::$file_access_cache)); //error_log(__METHOD__.'() items in title-cache: '.count(self::$title_cache).' file-access-cache: '.count(self::$file_access_cache));
$GLOBALS['egw']->session->appsession('link_title_cache','phpgwapi',self::$title_cache); $GLOBALS['egw']->session->appsession('link_title_cache','phpgwapi',self::$title_cache);
$GLOBALS['egw']->session->appsession('link_file_access_cache','phpgwapi',self::$file_access_cache); $GLOBALS['egw']->session->appsession('link_file_access_cache','phpgwapi',self::$file_access_cache);
// send out notifications about added, changed or removed links
self::run_notifies();
} }
/** /**
@ -434,7 +435,7 @@ class egw_link extends solink
if (empty($only_app) || $only_app == self::VFS_APPNAME || if (empty($only_app) || $only_app == self::VFS_APPNAME ||
($only_app[0] == '!' && $only_app != '!'.self::VFS_APPNAME)) ($only_app[0] == '!' && $only_app != '!'.self::VFS_APPNAME))
{ {
if ($vfs_ids = self::list_attached($app,$id)) if (($vfs_ids = self::list_attached($app,$id)))
{ {
$ids += $vfs_ids; $ids += $vfs_ids;
} }
@ -496,7 +497,7 @@ class egw_link extends solink
{ {
$links[$id] = array(); $links[$id] = array();
} }
if ($vfs_ids = self::list_attached($app,$id)) if (($vfs_ids = self::list_attached($app,$id)))
{ {
$links[$id] += $vfs_ids; $links[$id] += $vfs_ids;
} }
@ -506,7 +507,7 @@ class egw_link extends solink
{ {
// agregate links by app // agregate links by app
$app_ids = array(); $app_ids = array();
foreach($links as $src_id => &$targets) foreach($links as &$targets)
{ {
foreach($targets as $link) foreach($targets as $link)
{ {
@ -799,7 +800,7 @@ class egw_link extends solink
} }
$method = $reg['title']; $method = $reg['title'];
$title = ExecMethod($method,$id); if (true) $title = ExecMethod($method,$id);
if ($id && is_null($title)) // $app,$id has been deleted ==> unlink all links to it if ($id && is_null($title)) // $app,$id has been deleted ==> unlink all links to it
{ {
@ -858,7 +859,7 @@ class egw_link extends solink
} }
if ($ids_to_query) if ($ids_to_query)
{ {
for ($n = 0; $ids = array_slice($ids_to_query,$n*self::MAX_TITLES_QUERY,self::MAX_TITLES_QUERY); ++$n) for ($n = 0; ($ids = array_slice($ids_to_query,$n*self::MAX_TITLES_QUERY,self::MAX_TITLES_QUERY)); ++$n)
{ {
foreach(ExecMethod(self::$app_register[$app]['titles'],$ids) as $id => $t) foreach(ExecMethod(self::$app_register[$app]['titles'],$ids) as $id => $t)
{ {
@ -1039,6 +1040,7 @@ class egw_link extends solink
if ($app == self::VFS_APPNAME && is_array($link) && !empty($link['type'])) if ($app == self::VFS_APPNAME && is_array($link) && !empty($link['type']))
{ {
$path = self::vfs_path($link['app2'], $link['id2'], $link['id'], true); $path = self::vfs_path($link['app2'], $link['id2'], $link['id'], true);
$p = null;
if (self::mime_open($path, $link['type'], $p)) if (self::mime_open($path, $link['type'], $p))
{ {
$popup = $p; $popup = $p;
@ -1169,11 +1171,11 @@ class egw_link extends solink
* @param string $file VFS path to link to * @param string $file VFS path to link to
* @param string $comment='' comment to add to the link * @param string $comment='' comment to add to the link
*/ */
static function link_file($app,$id,$file,$comment='') static function link_file($app,$id,$file)//,$comment='')
{ {
$app_path = self::vfs_path($app,$id); $app_path = self::vfs_path($app,$id);
$ok = true; $ok = true;
if (file_exists($app_path) || ($Ok = mkdir($app_path,0,true))) if (file_exists($app_path) || ($ok = mkdir($app_path,0,true)))
{ {
if (!egw_vfs::stat($file)) if (!egw_vfs::stat($file))
{ {
@ -1370,6 +1372,10 @@ class egw_link extends solink
{ {
if ($link_id && isset(self::$app_register[$notify_app]) && isset(self::$app_register[$notify_app]['notify'])) if ($link_id && isset(self::$app_register[$notify_app]) && isset(self::$app_register[$notify_app]['notify']))
{ {
if (!self::$notifies)
{
egw::on_shutdown(array(__CLASS__, 'run_notifies'));
}
self::$notifies[] = array( self::$notifies[] = array(
'method' => self::$app_register[$notify_app]['notify'], 'method' => self::$app_register[$notify_app]['notify'],
'type' => $type, 'type' => $type,
@ -1383,7 +1389,7 @@ class egw_link extends solink
} }
/** /**
* Run notifications called by egw_link::save_session_cache from egw::shutdown, after regular processing is finished * Run notifications called by egw::on_shutdown(), after regular processing is finished
*/ */
static public function run_notifies() static public function run_notifies()
{ {
@ -1451,13 +1457,11 @@ class egw_link extends solink
//error_log(__METHOD__."($app,$id,$title,$file_access)"); //error_log(__METHOD__."($app,$id,$title,$file_access)");
if (!is_null($title)) if (!is_null($title))
{ {
$cache =& self::get_cache($app,$id); self::$title_cache[$app.':'.$id] = $title;
$cache = $title;
} }
if (!is_null($file_access)) if (!is_null($file_access))
{ {
$cache =& self::get_cache($app,$id,'file_access'); self::$file_access_cache[$app.':'.$id] = $file_access;
$cache = $file_access;
} }
} }