diff --git a/phpgwapi/inc/class.egw.inc.php b/phpgwapi/inc/class.egw.inc.php index 53338b9c14..38c6381da5 100644 --- a/phpgwapi/inc/class.egw.inc.php +++ b/phpgwapi/inc/class.egw.inc.php @@ -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'); diff --git a/phpgwapi/inc/class.egw_json.inc.php b/phpgwapi/inc/class.egw_json.inc.php index c025d8882d..aed641837a 100644 --- a/phpgwapi/inc/class.egw_json.inc.php +++ b/phpgwapi/inc/class.egw_json.inc.php @@ -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()); + { + //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(); - } } /** diff --git a/phpgwapi/inc/class.egw_link.inc.php b/phpgwapi/inc/class.egw_link.inc.php index c318b5fe75..8ce12c4770 100644 --- a/phpgwapi/inc/class.egw_link.inc.php +++ b/phpgwapi/inc/class.egw_link.inc.php @@ -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; } }