ZF-1325: "Unwritable session.save_path" when using defaults in Zend_Session and php.ini

Issue Type: Bug Created: 2007-04-28T15:35:47.000+0000 Last Updated: 2008-05-08T14:29:39.000+0000 Status: Resolved Fix version(s): - 1.0.0 RC1 (28/May/07)

Reporter: Chris van der Wel (kritz) Assignee: Ralph Schindler (ralph) Tags: - Zend_Session

Related issues: - ZF-3020



<pre class="highlight"><?php


require_once 'Zend/Session.php';


produces the following exception when session.save_path is not set in php.ini:

Uncaught exception 'Zend_Session_Exception' with message 'Unwritable session.save_path: ' in /path/to/zend/framework/Zend/Session.php:217

According to the PHP manual (…) session.save_path should default to "/tmp", but Zend_Session still complains. The following code works, but the ini_set call shouldn't be necessary.

<pre class="highlight"><?php


require_once 'Zend/Session.php';



Posted by Chris van der Wel (kritz) on 2007-04-28T15:36:38.000+0000

added code blocks.

Posted by Darby Felton (darby) on 2007-04-30T16:00:12.000+0000

Looking at the code, I see in Zend/Session.php at the end of the setOptions() method:

<pre class="highlight">
        $savePath = ini_get('session.save_path');
        if (strpos($savePath, ';') !== false) {
            $savePath = explode(';', $savePath);
            $savePath = realpath(array_pop($savePath));
        if (self::$_ignore_save_path !== true && !is_writable($savePath)) {
            throw new Zend_Session_Exception("Unwritable session.save_path: $savePath");

But when I look at the" rel="nofollow">PHP manual entry I see:

bq. session.save_path defines the argument which is passed to the save handler. If you choose the default files handler, this is the path where the files are created. Defaults to /tmp. See also session_save_path(). bq. There is an optional N argument to this directive that determines the number of directory levels your session files will be spread around in. For example, setting to '5;/tmp' may end up creating a session file and location like /tmp/4/b/1/e/3/sess_4b1e384ad74619bd212e236e52a5a174If . In order to use N you must create all of these directories before use. A small shell script exists in ext/session to do this, it's called Also note that if N is used and greater than 0 then automatic garbage collection will not be performed, see a copy of php.ini for further information. Also, if you use N, be sure to surround session.save_path in "quotes" because the separator (\;) is also used for comments in php.ini.

If we consider it a good design decision that Zend_Session throws an Exception if the session path is not writable, then it must also check all of the child directories of the session.save_path when using the optional N argument. I think that this is not a good option, however, and I tend to question the sensibility of throwing an Exception at this point in the code for the purpose of verifying the session.save_path.

Posted by Darby Felton (darby) on 2007-04-30T16:01:26.000+0000

Volley to [~ralph] for additional comment.

Posted by Ralph Schindler (ralph) on 2007-05-08T11:26:07.000+0000

Ok, after reading through the ext/session C, I know whats going on and why its going on. session_start(), regardless of whether or not it actually does start or not, will set its internal state to active. Which means, session_write_close (effectively) will run at RSHUTDOWN, thus changing the state of ext/session.

Also, the following code has the added benefit of being able to throw exceptions on ANY mod_handler ext/session decides to use, be it mm, files, db, or memcached. The idea as always, is that session_start should emit nothing from PHP and should start clean, otherwise, we should throw an exception as developers might want to be able to handle it at bootstrap time.

<pre class="highlight">
webdeveloper@webdevelopment ~ $ cat testerror.php

ini_set('session.save_path', '/var/log');

class Zend_Session
  public function start()
    set_error_handler(array('Zend_Session_Exception', 'handleSessionStartError'), E_ALL);
    if (Zend_Session_Exception::$sessionStartError !== null) {
       set_error_handler(array('Zend_Session_Exception', 'handleSilentWriteClose'), E_ALL);
       throw new Zend_Session_Exception(Zend_Session_Exception::$sessionStartError);


class Zend_Session_Exception extends Exception
  static public $sessionStartError = null;

  static public function handleSessionStartError($errno, $errstr)
    self::$sessionStartError = $errstr;

  static public function handleSilentWriteClose($errno, $errstr)
  { }


// this wouldbe bootstrap file
} catch (Exception $e) {
  print_r('setup error: ' . $e->getMessage() . "\n");

echo "End of program

webdeveloper@webdevelopment ~ $ php testerror.php
setup error: session_start(): open(/var/log/sess_9bae147b205508d3dab70023eab739eb, O_RDWR) failed: Permission denied (13)
End of program

Posted by Ralph Schindler (ralph) on 2007-05-08T12:28:14.000+0000

ABOVE IS PROOF-OF-CONCEPT ONLY, see the fixes in the code

Fisheye tab above or:

SVN r4763

Posted by Darby Felton (darby) on 2007-05-09T14:37:13.000+0000

Confirmed as fixed. Thanks to [~ralph]!

Have you found an issue?

See the Overview section for more details.


© 2006-2019 by Zend, a Rogue Wave Company. Made with by awesome contributors.

This website is built using zend-expressive and it runs on PHP 7.