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()
.
→ Installation and requirements
Starting Session
By default, Nette will start a session automatically the moment we start reading from it or writing data to it. To manually
start a session, use $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')
.
The section itself is very easy to work with using the set()
, get()
and remove()
methods:
// variable writing
$section->set('userName', 'franta');
// reading a variable, returns null if it does not exist
echo $section->get('userName');
// variable removing
$section->remove('userName');
It's possible to use foreach
cycle to obtain all variables from section:
foreach ($section as $key => $val) {
echo "$key = $val";
}
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');
The third parameter of the set()
method is used to set the expiration of individual variables:
// variable 'flash' expires after 30 seconds
$section->set('flash', $message, '30 seconds');
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()
.
Events $onStart, $onBeforeWrite
Object Nette\Http\Session
has events
$onStart
a $onBeforeWrite
, so you can add callbacks that are called after the session starts or before
it is written to disk and then terminated.
$session->onBeforeWrite[] = function () {
// write data to the session
$this->section->set('basket', $this->basket);
};
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
) and also readAndClose.
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.