分享

关于Zend_Session与Session自动启动间冲突解决办法

 nywrb 2012-09-06

关于Zend_Session与Session自动启动间冲突解决办法

王志刚|发表于2011-01-10

昨天进行网站搬家(从www.softechallenger.com搬到www.iacademe.net,原来那家服务商实在不像话,一个备案两个月都下不来),两边网站的环境都是PHP5+MySQL+Zend,网站本身没有做任何修改,以为应该可以很顺利两个小时内就可以搞定的事。当我创建好数据库,将所有的源代码都上传服务器,并设置好一些必须的目录操作权限(有如履历保存目录等需要赋予读写权限)后,在FireFox中输入网址:www.iacademe.net,以为大功告成。没想到出现了如下的Exception信息。

exception=exception 'Zend_Session_Exception' with message 'session has already been started by session.auto-start or session_start()' in C:\PHP5\includes\Zend\Session.php:462 Stack trace: #0 C:\PHP5\includes\Zend\Session\Namespace.php(143): Zend_Session::start(true) #1 C:\Apache2.2\htdocs\website\application\DispatchPlugin.class.php(7): Zend_Session_Namespace->__construct('myApp') #2 C:\PHP5\includes\Zend\Controller\Plugin\Broker.php(287): DispatchPlugin->dispatchLoopStartup(Object(Zend_Controller_Request_Http)) #3 C:\PHP5\includes\Zend\Controller\Front.php(928): Zend_Controller_Plugin_Broker->dispatchLoopStartup(Object(Zend_Controller_Request_Http)) #4 C:\Apache2.2\htdocs\website\index.php(36): Zend_Controller_Front->dispatch() #5 {main}

Exception信息的大致意思是session已经启动过了,程序(DispatchPlugin.class.php)中试图在次启动session,于是便挂了。

怎么回事?两边的服务器的环境明明是一样的呀?于是我仔细比对了两个服务器设置(当然使用phpinfo()函数了,代码为<?php phpinfo();?>)。发现一个与session相关的设置两边不同,原服务器中将session .auto_start设置为off,而新服务器的session .auto_start设置为on(绝大多数虚拟服务提供商都会将session .auto_start设置为off的,让用户自行处理session的开启与关闭,我不知道这家服务提供商是那里出毛病了,估计是不了解zend_session的使用)。

这个session .auto_start设置在php.ini中,通常为

session .auto_start=0

自动开启session的情况下为

session .auto_start=1



我在程序中是通过【new Zend_Session_Namespace(”myApp”)】的形式开始session的(关于Zend_Session的用法请参照官方网站,URL为:http://framework./manual/zh/zend.session.basic_usage.html)。【new Zend_Session_Namespace(”myApp”)】中默认重新开启session,如果此时session已经自动开启,将会抛出上述Exception。

解决的方法很简单将php.ini中的session .auto_start设置为0就可以了,可是这家服务商不愿意帮我修改(理由是影响其他已经在使用的用户,道理也通,谁我要用虚拟空间服务呢,专有服务器就可以想怎么改就怎么改了)。

既然php.ini不让修改,只能另想办法了,另一个办法也挺简单,就是修改【.htaccess】文件,在此文件中强制将session .auto_start重新设置为off,也能解决问题。但是,对方这次将【.htaccess】文件中与系统设置相关的项目锁定了,我在【.htaccess】文件中加入【php_value session.auto_start 0】后,浏览器中会出现【Internal error 400】的错误(在本地测试过,这样做可以解决上述问题)。看来这一招也悲剧了。

最后能想到的是,既然自动启动session无法改变,我只能修改程序代码了。但是自己网站中使用到session地方实在太多,而且我也不想放弃【new Zend_Session_Namespace(”myApp”)】这种使用Zend_Session的方式,决定对Zend Framework动手了。既然【new Zend_Session_Namespace(”myApp”)】中会默认自动开启Session,我将自动开启Session部分代码关闭掉不就得了。于是我修改了Session.php(Zend目录下)的start方法,有中文注释处就我动过刀子的地方。

public static function start($options = false)

{

if (self::$_sessionStarted && self::$_destroyed) {

require_once 'Zend/Session/Exception.php';

throw new Zend_Session_Exception('The session was explicitly destroyed during this request, attempting to re-start is not allowed.');

}



if (self::$_sessionStarted) {

return; // already started

}



// make sure our default options (at the least) have been set

if (!self::$_defaultOptionsSet) {

self::setOptions(is_array($options) ? $options : array());

}



// In strict mode, do not allow auto-starting Zend_Session, such as via "new Zend_Session_Namespace()"

if (self::$_strict && $options === true) {

/** @see Zend_Session_Exception */

require_once 'Zend/Session/Exception.php';

throw new Zend_Session_Exception('You must explicitly start the session with Zend_Session::start() when session options are set to strict.');

}



$filename = $linenum = null;

if (!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {

/** @see Zend_Session_Exception */

require_once 'Zend/Session/Exception.php';

throw new Zend_Session_Exception("Session must be started before any output has been sent to the browser;"

. " output started in {$filename}/{$linenum}");

}

//当SID已定义(即Session已开启)将会抛出异常

//应用需要将这个抛出注释的地方注释起来

// See http://www./manual/en/ref.session.php for explanation

//if (!self::$_unitTestEnabled && defined('SID')) {

/** @see Zend_Session_Exception */

// require_once 'Zend/Session/Exception.php';

// throw new Zend_Session_Exception('session has already been started by session.auto-start or session_start()');

//}




/**

* Hack to throw exceptions on start instead of php errors

* @see http://framework./issues/browse/ZF-1325

*/



$errorLevel = (is_int(self::$_throwStartupExceptions)) ? self::$_throwStartupExceptions : E_ALL;



/** @see Zend_Session_Exception */

if (!self::$_unitTestEnabled) {



if (self::$_throwStartupExceptions) {

require_once 'Zend/Session/Exception.php';

set_error_handler(array('Zend_Session_Exception', 'handleSessionStartError'), $errorLevel);

}

//将启动Session的动作注释掉,并将$startedCleanly设置为true。

//$startedCleanly = session_start();

$startedCleanly = true;




if (self::$_throwStartupExceptions) {

restore_error_handler();

}



if (!$startedCleanly || Zend_Session_Exception::$sessionStartError != null) {

if (self::$_throwStartupExceptions) {

set_error_handler(array('Zend_Session_Exception', 'handleSilentWriteClose'), $errorLevel);

}

session_write_close();

if (self::$_throwStartupExceptions) {

restore_error_handler();

throw new Zend_Session_Exception(__CLASS__ . '::' . __FUNCTION__ . '() - ' . Zend_Session_Exception::$sessionStartError);

}

}

}



parent::$_readable = true;

parent::$_writable = true;

self::$_sessionStarted = true;

if (self::$_regenerateIdState === -1) {

self::regenerateId();

}



// run validators if they exist

if (isset($_SESSION['__ZF']['VALID'])) {

self::_processValidators();

}



self::_processStartupMetadataGlobal();

}



注意:本人网站中没有使用服务商提供的Zend库,而是将Zend、PEAR等库都传到网站空间中,所有我才能自己修改Zend中的相关代码。

以上修改过程只是没有办法的,如能采取请两种解决办法,很快就完事了,通常不应提倡修改Zend代码。但本帖能稍微有助于大家多多了解Zend_Session的用法,如能对碰到类似问题的朋友有所帮助,将不胜荣幸。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多