Sessions

HTTP is a stateless protocol, but almost every application needs to keep state between requests, e.g. content of a shopping cart. This is what a session is used for. Let's see

  • how to use sessions
  • how to avoid naming conflicts
  • how to set expiration

When using sessions, each user receives a unique identifier called session ID, which is passed in a cookie. This serves as the key to the session data. Unlike cookies, which are stored on the browser side, session data is stored on the server side.

We configure session in configuration, the choice of expiration time is important.

The session is managed by the Nette\Http\Session object, which you get by passing it using dependency injection. In presenters simply call $session = $this->getSession().

Starting Session

By default, Nette automatically starts a session if the HTTP request contains a cookie with a session ID. It also starts automatically when we start reading from or writing data to it. Manually is session started by $session->start().

PHP sends HTTP headers affecting caching when starting the session, see session_cache_limiter, and possibly a cookie with the session ID. Therefore, it is always necessary to start the session before sending any output to the browser, otherwise an exception will be thrown. So if you know that a session will be used during page rendering, start it manually before, for example in the presenter.

In developer mode, Tracy starts the session because it uses it to display redirection and AJAX requests bars in the Tracy Bar.

Section

In pure PHP, the session data store is implemented as an array accessible via a global variable $_SESSION. The problem is that applications normally consist of a number of independent parts, and if all have only one same array available, sooner or later a name collision will occur.

Nette Framework solves the problem by dividing the entire space into sections (objects Nette\Http\SessionSection). Each unit then uses its own section with a unique name and no collisions can occur.

We get the section from the session manager:

$section = $session->getSection('unique name');

In the presenter it is enough to call getSession() with the parameter:

// $this is Presenter
$section = $this->getSession('unique name');

The existence of the section can be checked by the method $session->hasSection('unique name').

And then it's really simple to work with that section:

// variable writing
$section['userName'] = 'john'; // or $section->userName = 'john';

// variable reading
echo $section['userName'];     // or echo $section->userName;

// variable removing
unset($section['userName']);   // or unset($section->userName);

The preferred way to write is using square brackets, because Nette can better distinguish between writing and reading and knows when to automatically start the session.

It's possible to use foreach cycle to obtain all variables from section:

foreach ($section as $key => $val) {
	echo "$key = $val";
}

Accessing a non-existent variable does not generate any error (the returned value is null). It could be undesirable behavior in some cases and that's why there is a possibility to change it:

$section->warnOnUndefined = true;

How to Set Expiration

Expiration can be set for individual sections or even individual variables. We can let the user's login expire in 20 minutes, but still remember the contents of a shopping cart.

// section will expire after 20 minutes
$section->setExpiration('20 minutes');

// variable $section['flash'] will expire after 30 seconds
$section->setExpiration('30 seconds', 'flash');

Besides a relative time in seconds one can use a UNIX timestamp or a textual form. Interesting is a value of 0, which sets an expiration time to the moment when the user closes his browser. In browser Chrome in the “System” section Continue running background apps when Google Chrome is closed., this option is default enable. It causes the cookie that has a set expiration to close the browser, the cookie still lives.

// variable $section->password will expire when a user closes a browser
$section->setExpiration(0, 'password');

Remember that the expiration time of the whole session (see session configuration) must be equal to or higher than the time set for individual sections or variables.

The cancellation of the previously set expiration can be achieved by the method removeExpiration(). Immediate deletion of the whole section will be ensured by the method remove().

Session Management

Overview of methods of the Nette\Http\Session class for session management:

start(): void

Starts a session.

isStarted(): bool

Is the session started?

close(): void

Ends the session. The session ends automatically at the end of the script.

destroy(): void

Ends and deletes the session.

exists(): bool

Does the HTTP request contain a cookie with a session ID?

regenerateId(): void

Generates a new random session ID. Data remain unchanged.

getId(): string

Returns the session ID.

Configuration

We configure session in configuration. If you are writing an application that does not use a DI container, use these methods to configure it. They must be called before starting the session.

setName(string $name): static

Sets the name of the cookie which is used to transmit the session ID. The default name is PHPSESSID. This is useful if you run several different applications on the same site.

getName(): string

Returns the name of session cookie.

setOptions(array $options)static

Configures the session. It is possible to set all PHP session directives (in camelCase format, eg write savePath instead of session.save_path).

setExpiration(?string $time)static

Sets the time of inactivity after which the session expires.

setCookieParameters(string $path, string $domain=null, bool $secure=null, string $samesite=null)static

Sets parameters for cookies. You can change the default parameter values in configuration.

setSavePath(string $path)static

Sets the directory where session files are stored.

setHandler(\SessionHandlerInterface $handler)static

Sets custom handler, see PHP documentation.

Safety First

The server assumes that it communicates with the same user as long as requests contain the same session ID. The task of security mechanisms is to ensure that this behavior really works and that there is no possibility to substitute or steal an identifier.

That's why Nette Framework properly configures PHP directives to transfer session ID only in cookies, to avoid access from JavaScript and to ignore the identifiers in the URL. Moreover in critical moments, such as user login, it generates a new Session ID.

Function ini_set is used for configuring PHP, but unfortunately, its use is prohibited at some web hosting services. If it's your case, try to ask your hosting provider to allow this function for you, or at least to configure his server properly.

version: 4.0 3.x 2.x