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->applications = new applications();
register_shutdown_function(array($this, 'shutdown'));
if ($GLOBALS['egw_info']['flags']['currentapp'] != 'login' && $GLOBALS['egw_info']['flags']['currentapp'] != 'logout')
{
$this->verify_session();
@ -211,8 +209,6 @@ class egw extends egw_minimal
date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']);
}
register_shutdown_function(array($this, 'shutdown'));
$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'))
{
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
//
if (!$GLOBALS['egw_info']['server']['asyncservice']) // is default
{
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!!!');
// 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!!!');
exit;
}
if (isset($template))
@ -212,7 +211,7 @@ class egw_json_response
*/
public function haveJSONResponse()
{
return (boolean) $this->responseArray;
return $this->responseArray || $this->beforeSendDataProcs;
}
/**
@ -220,19 +219,22 @@ class egw_json_response
*/
private function sendHeader()
{
$file = $line = null;
if (headers_sent($file, $line))
{
error_log(__METHOD__."() header already sent by $file line $line: ".function_backtrace());
}
else
{
//Send the character encoding header
header('content-type: application/json; charset='.translation::charset());
}
}
/**
* Private function which is used to send the result via HTTP
*/
public function sendResult()
public static function sendResult()
{
$inst = self::get();
@ -265,7 +267,7 @@ class egw_json_response
*/
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()
{
$return = $this->responseArray;
$this->responseArray = array();
$this->responseArray = $this->beforeSendDataProcs = array();
$this->hasData = false;
return $return;
}
@ -571,16 +573,6 @@ class egw_json_response
'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();
}
// 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));
}
@ -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));
$GLOBALS['egw']->session->appsession('link_title_cache','phpgwapi',self::$title_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 ||
($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;
}
@ -496,7 +497,7 @@ class egw_link extends solink
{
$links[$id] = array();
}
if ($vfs_ids = self::list_attached($app,$id))
if (($vfs_ids = self::list_attached($app,$id)))
{
$links[$id] += $vfs_ids;
}
@ -506,7 +507,7 @@ class egw_link extends solink
{
// agregate links by app
$app_ids = array();
foreach($links as $src_id => &$targets)
foreach($links as &$targets)
{
foreach($targets as $link)
{
@ -799,7 +800,7 @@ class egw_link extends solink
}
$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
{
@ -858,7 +859,7 @@ class egw_link extends solink
}
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)
{
@ -1039,6 +1040,7 @@ class egw_link extends solink
if ($app == self::VFS_APPNAME && is_array($link) && !empty($link['type']))
{
$path = self::vfs_path($link['app2'], $link['id2'], $link['id'], true);
$p = null;
if (self::mime_open($path, $link['type'], $p))
{
$popup = $p;
@ -1169,11 +1171,11 @@ class egw_link extends solink
* @param string $file VFS path to link to
* @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);
$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))
{
@ -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 (!self::$notifies)
{
egw::on_shutdown(array(__CLASS__, 'run_notifies'));
}
self::$notifies[] = array(
'method' => self::$app_register[$notify_app]['notify'],
'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()
{
@ -1451,13 +1457,11 @@ class egw_link extends solink
//error_log(__METHOD__."($app,$id,$title,$file_access)");
if (!is_null($title))
{
$cache =& self::get_cache($app,$id);
$cache = $title;
self::$title_cache[$app.':'.$id] = $title;
}
if (!is_null($file_access))
{
$cache =& self::get_cache($app,$id,'file_access');
$cache = $file_access;
self::$file_access_cache[$app.':'.$id] = $file_access;
}
}