no message
This commit is contained in:
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
build: off
|
||||
cache:
|
||||
- c:\php -> appveyor.yml
|
||||
- '%LOCALAPPDATA%\Composer\files -> appveyor.yml'
|
||||
|
||||
clone_folder: c:\projects\http
|
||||
|
||||
init:
|
||||
- SET PATH=c:\php;%PATH%
|
||||
- SET PHP=1
|
||||
- SET ANSICON=121x90 (121x90)
|
||||
|
||||
install:
|
||||
# Install PHP
|
||||
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
|
||||
- IF %PHP%==1 cd c:\php
|
||||
- IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip --output php.zip
|
||||
- IF %PHP%==1 7z x php.zip >nul
|
||||
- IF %PHP%==1 echo extension_dir=ext >> php.ini
|
||||
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
|
||||
- IF %PHP%==1 del /Q *.zip
|
||||
- cd c:\projects\http
|
||||
|
||||
# Install Nette Tester
|
||||
- appveyor DownloadFile https://getcomposer.org/composer.phar
|
||||
- php composer.phar install --prefer-dist --no-interaction --no-progress
|
||||
|
||||
test_script:
|
||||
- vendor\bin\tester tests -s -c tests\php-win.ini
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "nette/http",
|
||||
"description": "🌐 Nette Http: abstraction for HTTP request, response and session. Provides careful data sanitization and utility for URL and cookies manipulation.",
|
||||
"keywords": ["nette", "http", "request", "response", "session", "security", "url", "proxy", "cookies"],
|
||||
"homepage": "https://nette.org",
|
||||
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Grudl",
|
||||
"homepage": "https://davidgrudl.com"
|
||||
},
|
||||
{
|
||||
"name": "Nette Community",
|
||||
"homepage": "https://nette.org/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"nette/utils": "^2.4 || ~3.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/di": "^2.4.8 || ~3.0.0",
|
||||
"nette/tester": "^2.0",
|
||||
"tracy/tracy": "^2.4"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/nette": "<2.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-fileinfo": "to detect type of uploaded files",
|
||||
"nette/security": "allows use Nette\\Http\\UserStorage"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
Licenses
|
||||
========
|
||||
|
||||
Good news! You may use Nette Framework under the terms of either
|
||||
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
|
||||
|
||||
The BSD License is recommended for most projects. It is easy to understand and it
|
||||
places almost no restrictions on what you can do with the framework. If the GPL
|
||||
fits better to your project, you can use the framework under this license.
|
||||
|
||||
You don't have to notify anyone which license you are using. You can freely
|
||||
use Nette Framework in commercial projects as long as the copyright header
|
||||
remains intact.
|
||||
|
||||
Please be advised that the name "Nette Framework" is a protected trademark and its
|
||||
usage has some limitations. So please do not use word "Nette" in the name of your
|
||||
project or top-level domain, and choose a name that stands on its own merits.
|
||||
If your stuff is good, it will not take long to establish a reputation for yourselves.
|
||||
|
||||
|
||||
New BSD License
|
||||
---------------
|
||||
|
||||
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of "Nette Framework" nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
This software is provided by the copyright holders and contributors "as is" and
|
||||
any express or implied warranties, including, but not limited to, the implied
|
||||
warranties of merchantability and fitness for a particular purpose are
|
||||
disclaimed. In no event shall the copyright owner or contributors be liable for
|
||||
any direct, indirect, incidental, special, exemplary, or consequential damages
|
||||
(including, but not limited to, procurement of substitute goods or services;
|
||||
loss of use, data, or profits; or business interruption) however caused and on
|
||||
any theory of liability, whether in contract, strict liability, or tort
|
||||
(including negligence or otherwise) arising in any way out of the use of this
|
||||
software, even if advised of the possibility of such damage.
|
||||
|
||||
|
||||
GNU General Public License
|
||||
--------------------------
|
||||
|
||||
GPL licenses are very very long, so instead of including them here we offer
|
||||
you URLs with full text:
|
||||
|
||||
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
|
||||
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
|
||||
Vendored
+222
@@ -0,0 +1,222 @@
|
||||
Nette HTTP Component
|
||||
====================
|
||||
|
||||
[](https://packagist.org/packages/nette/http)
|
||||
[](https://travis-ci.org/nette/http)
|
||||
[](https://ci.appveyor.com/project/dg/http/branch/master)
|
||||
[](https://coveralls.io/github/nette/http?branch=master)
|
||||
[](https://github.com/nette/http/releases)
|
||||
[](https://github.com/nette/http/blob/master/license.md)
|
||||
|
||||
HTTP request and response are encapsulated in `Nette\Http\Request` and `Nette\Http\Response` objects which offer comfortable API and also act as
|
||||
sanitization filter.
|
||||
|
||||
|
||||
HTTP Request
|
||||
-------------
|
||||
|
||||
Nette cleans out data sent by user from control and invalid characters.
|
||||
|
||||
The URL of the request is available as [api:Nette\Http\UrlScript] instance:
|
||||
|
||||
```php
|
||||
$url = $httpRequest->getUrl();
|
||||
echo $url; // e.g. https://nette.org/en/documentation?action=edit
|
||||
echo $url->host; // nette.org
|
||||
```
|
||||
|
||||
Determine current HTTP method:
|
||||
|
||||
```php
|
||||
echo $httpRequest->getMethod(); // GET, POST, HEAD, PUT
|
||||
|
||||
if ($httpRequest->isMethod('GET')) ...
|
||||
```
|
||||
|
||||
Is the connection encrypted (HTTPS)?
|
||||
|
||||
```php
|
||||
echo $httpRequest->isSecured() ? 'yes' : 'no';
|
||||
```
|
||||
|
||||
Is this an AJAX request?
|
||||
|
||||
```php
|
||||
echo $httpRequest->isAjax() ? 'yes' : 'no';
|
||||
```
|
||||
|
||||
What is the user's IP address?
|
||||
|
||||
```php
|
||||
echo $httpRequest->getRemoteAddress(); // user's IP address
|
||||
echo $httpRequest->getRemoteHost(); // and its DNS translation
|
||||
```
|
||||
|
||||
What URL the user came from? Returned as [Nette\Http\Url |urls] object.
|
||||
|
||||
```php
|
||||
echo $httpRequest->getReferer()->host;
|
||||
```
|
||||
|
||||
Request parameters:
|
||||
|
||||
```php
|
||||
$get = $httpRequest->getQuery(); // array of all URL parameters
|
||||
$id = $httpRequest->getQuery('id'); // returns GET parameter 'id' (or null)
|
||||
|
||||
$post = $httpRequest->getPost(); // array of all POST parameters
|
||||
$id = $httpRequest->getPost('id'); // returns POST parameter 'id' (or null)
|
||||
|
||||
$cookies = $httpRequest->getCookies(); // array of all cookies
|
||||
$sessId = $httpRequest->getCookie('sess_id'); // returns the cookie (or null)
|
||||
```
|
||||
|
||||
Uploaded files are encapsulated into [api:Nette\Http\FileUpload] objects:
|
||||
|
||||
```php
|
||||
$files = $httpRequest->getFiles(); // array of all uploaded files
|
||||
|
||||
$file = $httpRequest->getFile('avatar'); // returns one file
|
||||
echo $file->getName(); // name of the file sent by user
|
||||
echo $file->getSanitizedName(); // the name without dangerous characters
|
||||
```
|
||||
|
||||
HTTP headers are also accessible:
|
||||
|
||||
```php
|
||||
// returns associative array of HTTP headers
|
||||
$headers = $httpRequest->getHeaders();
|
||||
|
||||
// returns concrete header (case-insensitive)
|
||||
$userAgent = $httpRequest->getHeader('User-Agent');
|
||||
```
|
||||
|
||||
A useful method is `detectLanguage()`. You can pass it an array with languages supported by application and it returns the one preferred by browser.
|
||||
It is not magic, the method just uses the `Accept-Language` header.
|
||||
|
||||
```php
|
||||
// Header sent by browser: Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3
|
||||
|
||||
$langs = array('hu', 'pl', 'en'); // languages supported in application
|
||||
|
||||
echo $httpRequest->detectLanguage($langs); // en
|
||||
```
|
||||
|
||||
|
||||
RequestFactory and URL filtering
|
||||
------------------
|
||||
|
||||
Object holding current HTTP request is created by [api:Nette\Http\RequestFactory]. Its behavior can be modified.
|
||||
It's possible to clean up URLs from characters that can get into them because of poorly implemented comment systems on various other websites by using filters:
|
||||
|
||||
```php
|
||||
$requestFactory = new Nette\Http\RequestFactory;
|
||||
|
||||
// remove spaces from path
|
||||
$requestFactory->addUrlFilter('%20', '', PHP_URL_PATH);
|
||||
|
||||
// remove dot, comma or right parenthesis form the end of the URL
|
||||
$requestFactory->addUrlFilter('[.,)]$');
|
||||
|
||||
// clean the path from duplicated slashes (default filter)
|
||||
$requestFactory->addUrlFilter('/{2,}', '/', PHP_URL_PATH);
|
||||
```
|
||||
|
||||
And then we let the factory generate a new `httpRequest` and we store it in a system container:
|
||||
|
||||
```php
|
||||
// $container is a system container
|
||||
$container->addService('httpRequest', $requestFactory->createHttpRequest());
|
||||
```
|
||||
|
||||
|
||||
HTTP response
|
||||
--------------
|
||||
|
||||
Whether it is still possible to send headers or change the status code tells the `isSent()` method. If it returns true,
|
||||
it won't be possible to send another header or change the status code.
|
||||
|
||||
In that case, any attempt to send header or change code invokes `Nette\InvalidStateException`. .[caution]
|
||||
|
||||
[Response status code | http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10] can be sent and retrieved this way:
|
||||
|
||||
```php
|
||||
$httpResponse->setCode(Nette\Http\Response::S404_NOT_FOUND);
|
||||
|
||||
echo $httpResponse->getCode(); // 404
|
||||
```
|
||||
|
||||
For better source code readability it is recommended to use predefined constants instead of actual numbers:
|
||||
|
||||
```
|
||||
Http\IResponse::S200_OK
|
||||
Http\IResponse::S204_NO_CONTENT
|
||||
Http\IResponse::S300_MULTIPLE_CHOICES
|
||||
Http\IResponse::S301_MOVED_PERMANENTLY
|
||||
Http\IResponse::S302_FOUND
|
||||
Http\IResponse::S303_SEE_OTHER
|
||||
Http\IResponse::S303_POST_GET
|
||||
Http\IResponse::S304_NOT_MODIFIED
|
||||
Http\IResponse::S307_TEMPORARY_REDIRECT
|
||||
Http\IResponse::S400_BAD_REQUEST
|
||||
Http\IResponse::S401_UNAUTHORIZED
|
||||
Http\IResponse::S403_FORBIDDEN
|
||||
Http\IResponse::S404_NOT_FOUND
|
||||
Http\IResponse::S410_GONE
|
||||
Http\IResponse::S500_INTERNAL_SERVER_ERROR
|
||||
Http\IResponse::S501_NOT_IMPLEMENTED
|
||||
Http\IResponse::S503_SERVICE_UNAVAILABLE
|
||||
```
|
||||
|
||||
Method `setContentType($type, $charset=null)` changes `Content-Type` response header:
|
||||
|
||||
```php
|
||||
$httpResponse->setContentType('text/plain', 'UTF-8');
|
||||
```
|
||||
|
||||
Redirection to another URL is done by `redirect($url, $code=302)` method. Do not forget to terminate the script afterwards!
|
||||
|
||||
```php
|
||||
$httpResponse->redirect('http://example.com');
|
||||
exit;
|
||||
```
|
||||
|
||||
|
||||
To set the document expiration date, we can use `setExpiration()` method. The parameter is either text data, number of seconds or a timestamp:
|
||||
|
||||
```php
|
||||
// browser cache expires in one hour
|
||||
$httpResponse->setExpiration('+ 1 hours');
|
||||
```
|
||||
|
||||
Now we send the HTTP response header:
|
||||
|
||||
```php
|
||||
$httpResponse->setHeader('Pragma', 'no-cache');
|
||||
|
||||
// or if we want to send the same header more times with different values
|
||||
$httpResponse->addHeader('Pragma', 'no-cache');
|
||||
```
|
||||
|
||||
Sent headers are also available:
|
||||
|
||||
```php
|
||||
// returns associative array of headers
|
||||
$headers = $httpResponse->getHeaders();
|
||||
|
||||
// returns concrete header (case-insensitive)
|
||||
$pragma = $httpResponse->getHeader('Pragma');
|
||||
```
|
||||
|
||||
There are two methods for cookie manipulation: `setCookie()` and `deleteCookie()`.
|
||||
|
||||
```php
|
||||
// setCookie($name, $value, $time, [$path, [$domain, [$secure, [$httpOnly]]]])
|
||||
$httpResponse->setCookie('lang', 'en', '100 days'); // send cookie
|
||||
|
||||
// deleteCookie($name, [$path, [$domain, [$secure]]])
|
||||
$httpResponse->deleteCookie('lang'); // delete cookie
|
||||
```
|
||||
|
||||
These two methods can take more parameters: `$path` (subdirectory where the cookie will be available),
|
||||
`$domain` and `$secure`. Their detailed description can be found in PHP manual for [php:setcookie] function.
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Bridges\HttpDI;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* HTTP extension for Nette DI.
|
||||
*/
|
||||
class HttpExtension extends Nette\DI\CompilerExtension
|
||||
{
|
||||
public $defaults = [
|
||||
'proxy' => [],
|
||||
'headers' => [
|
||||
'X-Powered-By' => 'Nette Framework',
|
||||
'Content-Type' => 'text/html; charset=utf-8',
|
||||
],
|
||||
'frames' => 'SAMEORIGIN', // X-Frame-Options
|
||||
'csp' => [], // Content-Security-Policy
|
||||
'cspReportOnly' => [], // Content-Security-Policy-Report-Only
|
||||
'csp-report' => null, // for compatibility
|
||||
'featurePolicy' => [], // Feature-Policy
|
||||
'cookieSecure' => null, // true|false|auto Whether the cookie is available only through HTTPS
|
||||
'sameSiteProtection' => null, // activates Request::isSameSite() protection
|
||||
];
|
||||
|
||||
/** @var bool */
|
||||
private $cliMode;
|
||||
|
||||
|
||||
public function __construct($cliMode = false)
|
||||
{
|
||||
$this->cliMode = $cliMode;
|
||||
}
|
||||
|
||||
|
||||
public function loadConfiguration()
|
||||
{
|
||||
$builder = $this->getContainerBuilder();
|
||||
$config = $this->validateConfig($this->defaults);
|
||||
|
||||
$builder->addDefinition($this->prefix('requestFactory'))
|
||||
->setClass(Nette\Http\RequestFactory::class)
|
||||
->addSetup('setProxy', [$config['proxy']]);
|
||||
|
||||
$builder->addDefinition($this->prefix('request'))
|
||||
->setClass(Nette\Http\Request::class)
|
||||
->setFactory('@Nette\Http\RequestFactory::createHttpRequest');
|
||||
|
||||
$builder->addDefinition($this->prefix('response'))
|
||||
->setClass(Nette\Http\Response::class);
|
||||
|
||||
$builder->addDefinition($this->prefix('context'))
|
||||
->setClass(Nette\Http\Context::class)
|
||||
->addSetup('::trigger_error', ['Service http.context is deprecated.', E_USER_DEPRECATED]);
|
||||
|
||||
if ($this->name === 'http') {
|
||||
$builder->addAlias('nette.httpRequestFactory', $this->prefix('requestFactory'));
|
||||
$builder->addAlias('nette.httpContext', $this->prefix('context'));
|
||||
$builder->addAlias('httpRequest', $this->prefix('request'));
|
||||
$builder->addAlias('httpResponse', $this->prefix('response'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function beforeCompile()
|
||||
{
|
||||
$builder = $this->getContainerBuilder();
|
||||
if (isset($this->config['cookieSecure'])) {
|
||||
$value = $this->config['cookieSecure'] === 'auto'
|
||||
? $builder::literal('$this->getService(?)->isSecured()', [$this->prefix('request')])
|
||||
: (bool) $this->config['cookieSecure'];
|
||||
|
||||
$builder->getDefinition($this->prefix('response'))
|
||||
->addSetup('$cookieSecure', [$value]);
|
||||
$builder->getDefinitionByType(Nette\Http\Session::class)
|
||||
->addSetup('setOptions', [['cookie_secure' => $value]]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function afterCompile(Nette\PhpGenerator\ClassType $class)
|
||||
{
|
||||
if ($this->cliMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$initialize = $class->getMethod('initialize');
|
||||
$config = $this->getConfig();
|
||||
$headers = $config['headers'];
|
||||
|
||||
if (isset($config['frames']) && $config['frames'] !== true) {
|
||||
$frames = $config['frames'];
|
||||
if ($frames === false) {
|
||||
$frames = 'DENY';
|
||||
} elseif (preg_match('#^https?:#', $frames)) {
|
||||
$frames = "ALLOW-FROM $frames";
|
||||
}
|
||||
$headers['X-Frame-Options'] = $frames;
|
||||
}
|
||||
|
||||
if (isset($config['csp-report'])) {
|
||||
trigger_error('Rename csp-repost to cspReportOnly in config.', E_USER_DEPRECATED);
|
||||
$config['cspReportOnly'] = $config['csp-report'];
|
||||
}
|
||||
|
||||
foreach (['csp', 'cspReportOnly'] as $key) {
|
||||
if (empty($config[$key])) {
|
||||
continue;
|
||||
}
|
||||
$value = self::buildPolicy($config[$key]);
|
||||
if (strpos($value, "'nonce'")) {
|
||||
$value = Nette\DI\ContainerBuilder::literal(
|
||||
'str_replace(?, ? . (isset($cspNonce) \? $cspNonce : $cspNonce = base64_encode(Nette\Utils\Random::generate(16, "\x00-\xFF"))), ?)',
|
||||
["'nonce", "'nonce-", $value]
|
||||
);
|
||||
}
|
||||
$headers['Content-Security-Policy' . ($key === 'csp' ? '' : '-Report-Only')] = $value;
|
||||
}
|
||||
|
||||
if (!empty($config['featurePolicy'])) {
|
||||
$headers['Feature-Policy'] = self::buildPolicy($config['featurePolicy']);
|
||||
}
|
||||
|
||||
foreach ($headers as $key => $value) {
|
||||
if ($value != null) { // intentionally ==
|
||||
$initialize->addBody('$this->getService(?)->setHeader(?, ?);', [$this->prefix('response'), $key, $value]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($config['sameSiteProtection'])) {
|
||||
$initialize->addBody('$this->getService(?)->setCookie(...?);', [$this->prefix('response'), ['nette-samesite', '1', 0, '/', null, null, true, 'Strict']]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static function buildPolicy(array $config)
|
||||
{
|
||||
static $nonQuoted = ['require-sri-for' => 1, 'sandbox' => 1];
|
||||
$value = '';
|
||||
foreach ($config as $type => $policy) {
|
||||
if ($policy === false) {
|
||||
continue;
|
||||
}
|
||||
$policy = $policy === true ? [] : (array) $policy;
|
||||
$value .= $type;
|
||||
foreach ($policy as $item) {
|
||||
$value .= !isset($nonQuoted[$type]) && preg_match('#^[a-z-]+\z#', $item) ? " '$item'" : " $item";
|
||||
}
|
||||
$value .= '; ';
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Bridges\HttpDI;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Session extension for Nette DI.
|
||||
*/
|
||||
class SessionExtension extends Nette\DI\CompilerExtension
|
||||
{
|
||||
public $defaults = [
|
||||
'debugger' => false,
|
||||
'autoStart' => 'smart', // true|false|smart
|
||||
'expiration' => null,
|
||||
'handler' => null,
|
||||
];
|
||||
|
||||
/** @var bool */
|
||||
private $debugMode;
|
||||
|
||||
/** @var bool */
|
||||
private $cliMode;
|
||||
|
||||
|
||||
public function __construct($debugMode = false, $cliMode = false)
|
||||
{
|
||||
$this->debugMode = $debugMode;
|
||||
$this->cliMode = $cliMode;
|
||||
}
|
||||
|
||||
|
||||
public function loadConfiguration()
|
||||
{
|
||||
$builder = $this->getContainerBuilder();
|
||||
$config = $this->getConfig() + $this->defaults;
|
||||
$this->setConfig($config);
|
||||
|
||||
$session = $builder->addDefinition($this->prefix('session'))
|
||||
->setFactory(Nette\Http\Session::class);
|
||||
|
||||
if ($config['expiration']) {
|
||||
$session->addSetup('setExpiration', [$config['expiration']]);
|
||||
}
|
||||
if ($config['handler']) {
|
||||
$session->addSetup('setHandler', [$config['handler']]);
|
||||
}
|
||||
if (isset($config['cookieDomain']) && $config['cookieDomain'] === 'domain') {
|
||||
$config['cookieDomain'] = $builder::literal('$this->getByType(Nette\Http\IRequest::class)->getUrl()->getDomain(2)');
|
||||
}
|
||||
if (isset($config['cookieSecure']) && $config['cookieSecure'] === 'auto') {
|
||||
$config['cookieSecure'] = $builder::literal('$this->getByType(Nette\Http\IRequest::class)->isSecured()');
|
||||
}
|
||||
|
||||
if ($this->debugMode && $config['debugger']) {
|
||||
$session->addSetup('@Tracy\Bar::addPanel', [
|
||||
new Nette\DI\Statement(Nette\Bridges\HttpTracy\SessionPanel::class),
|
||||
]);
|
||||
}
|
||||
|
||||
unset($config['expiration'], $config['handler'], $config['autoStart'], $config['debugger']);
|
||||
if (!empty($config)) {
|
||||
$session->addSetup('setOptions', [$config]);
|
||||
}
|
||||
|
||||
if ($this->name === 'session') {
|
||||
$builder->addAlias('session', $this->prefix('session'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function afterCompile(Nette\PhpGenerator\ClassType $class)
|
||||
{
|
||||
if ($this->cliMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$initialize = $class->getMethod('initialize');
|
||||
$config = $this->getConfig();
|
||||
$name = $this->prefix('session');
|
||||
|
||||
if ($config['autoStart'] === 'smart') {
|
||||
$initialize->addBody('$this->getService(?)->exists() && $this->getService(?)->start();', [$name, $name]);
|
||||
|
||||
} elseif ($config['autoStart']) {
|
||||
$initialize->addBody('$this->getService(?)->start();', [$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Bridges\HttpTracy;
|
||||
|
||||
use Nette;
|
||||
use Tracy;
|
||||
|
||||
|
||||
/**
|
||||
* Session panel for Debugger Bar.
|
||||
*/
|
||||
class SessionPanel implements Tracy\IBarPanel
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/**
|
||||
* Renders tab.
|
||||
* @return string
|
||||
*/
|
||||
public function getTab()
|
||||
{
|
||||
ob_start(function () {});
|
||||
require __DIR__ . '/templates/SessionPanel.tab.phtml';
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders panel.
|
||||
* @return string
|
||||
*/
|
||||
public function getPanel()
|
||||
{
|
||||
ob_start(function () {});
|
||||
require __DIR__ . '/templates/SessionPanel.panel.phtml';
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Nette\Bridges\HttpTracy;
|
||||
|
||||
use Tracy\Dumper;
|
||||
|
||||
?>
|
||||
<style class="tracy-debug">
|
||||
#tracy-debug .nette-SessionPanel-parameters pre {
|
||||
background: #FDF5CE;
|
||||
padding: .4em .7em;
|
||||
border: 1px dotted silver;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Session #<?= htmlspecialchars(session_id(), ENT_IGNORE, 'UTF-8') ?> (Lifetime: <?= htmlspecialchars(ini_get('session.cookie_lifetime'), ENT_NOQUOTES, 'UTF-8'); ?>)</h1>
|
||||
|
||||
<div class="tracy-inner nette-SessionPanel">
|
||||
<?php if (empty($_SESSION)):?>
|
||||
<p><i>empty</i></p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<?php
|
||||
foreach ($_SESSION as $k => $v) {
|
||||
if ($k === '__NF') {
|
||||
$k = 'Nette Session';
|
||||
$v = isset($v['DATA']) ? $v['DATA'] : null;
|
||||
} elseif ($k === '_tracy') {
|
||||
continue;
|
||||
}
|
||||
echo '<tr><th>', htmlspecialchars($k, ENT_IGNORE, 'UTF-8'), '</th><td>', Dumper::toHtml($v, [Dumper::LIVE => true]), "</td></tr>\n";
|
||||
}?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Nette\Bridges\HttpTracy;
|
||||
|
||||
?>
|
||||
<span title="Session #<?= htmlspecialchars(session_id(), ENT_IGNORE | ENT_QUOTES, 'UTF-8') ?>">
|
||||
<svg viewBox="0 0 2048 2048"><path fill="#52362c" d="m1691 1396-67 394h-133s2-446 0-586v-4c0-109 89-197 200-197 110 0 200 88 200 197s-89 197-200 197zm-1131-192c-2 141 0 586 0 586h-133l-67-394h-1c-110 0-199-88-199-197s89-197 200-197c110 0 200 88 200 197v4zm865 61v394h-399-403v-394c262-27 529-27 802 0zm0-66c-273-27-541-27-802 0-2-163-119-258-266-262-176-545 233-693 669-693 440 0 841 149 665 693-148 4-265 99-266 263z"/></svg>
|
||||
</span>
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* HTTP-specific tasks.
|
||||
*/
|
||||
class Context
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
|
||||
/** @var IResponse */
|
||||
private $response;
|
||||
|
||||
|
||||
public function __construct(IRequest $request, IResponse $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to cache the sent entity by its last modification date.
|
||||
* @param string|int|\DateTimeInterface last modified time
|
||||
* @param string strong entity tag validator
|
||||
* @return bool
|
||||
*/
|
||||
public function isModified($lastModified = null, $etag = null)
|
||||
{
|
||||
if ($lastModified) {
|
||||
$this->response->setHeader('Last-Modified', Helpers::formatDate($lastModified));
|
||||
}
|
||||
if ($etag) {
|
||||
$this->response->setHeader('ETag', '"' . addslashes($etag) . '"');
|
||||
}
|
||||
|
||||
$ifNoneMatch = $this->request->getHeader('If-None-Match');
|
||||
if ($ifNoneMatch === '*') {
|
||||
$match = true; // match, check if-modified-since
|
||||
|
||||
} elseif ($ifNoneMatch !== null) {
|
||||
$etag = $this->response->getHeader('ETag');
|
||||
|
||||
if ($etag == null || strpos(' ' . strtr($ifNoneMatch, ",\t", ' '), ' ' . $etag) === false) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
$match = true; // match, check if-modified-since
|
||||
}
|
||||
}
|
||||
|
||||
$ifModifiedSince = $this->request->getHeader('If-Modified-Since');
|
||||
if ($ifModifiedSince !== null) {
|
||||
$lastModified = $this->response->getHeader('Last-Modified');
|
||||
if ($lastModified != null && strtotime($lastModified) <= strtotime($ifModifiedSince)) {
|
||||
$match = true;
|
||||
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($match)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->response->setCode(IResponse::S304_NOT_MODIFIED);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return IRequest
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return IResponse
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Provides access to individual files that have been uploaded by a client.
|
||||
*
|
||||
* @property-read string $name
|
||||
* @property-read string $sanitizedName
|
||||
* @property-read string|null $contentType
|
||||
* @property-read int $size
|
||||
* @property-read string $temporaryFile
|
||||
* @property-read int $error
|
||||
* @property-read bool $ok
|
||||
* @property-read string|null $contents
|
||||
*/
|
||||
class FileUpload
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var string */
|
||||
private $type;
|
||||
|
||||
/** @var int */
|
||||
private $size;
|
||||
|
||||
/** @var string */
|
||||
private $tmpName;
|
||||
|
||||
/** @var int */
|
||||
private $error;
|
||||
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
foreach (['name', 'size', 'tmp_name', 'error'] as $key) {
|
||||
if (!isset($value[$key]) || !is_scalar($value[$key])) {
|
||||
$this->error = UPLOAD_ERR_NO_FILE;
|
||||
return; // or throw exception?
|
||||
}
|
||||
}
|
||||
$this->name = $value['name'];
|
||||
$this->size = $value['size'];
|
||||
$this->tmpName = $value['tmp_name'];
|
||||
$this->error = $value['error'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the file name.
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the sanitized file name.
|
||||
* @return string
|
||||
*/
|
||||
public function getSanitizedName()
|
||||
{
|
||||
return trim(Nette\Utils\Strings::webalize($this->name, '.', false), '.-');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the MIME content type of an uploaded file.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
if ($this->isOk() && $this->type === null) {
|
||||
$this->type = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $this->tmpName);
|
||||
}
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the size of an uploaded file.
|
||||
* @return int
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the path to an uploaded file.
|
||||
* @return string
|
||||
*/
|
||||
public function getTemporaryFile()
|
||||
{
|
||||
return $this->tmpName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the path to an uploaded file.
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->tmpName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the error code. {@link http://php.net/manual/en/features.file-upload.errors.php}
|
||||
* @return int
|
||||
*/
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is there any error?
|
||||
* @return bool
|
||||
*/
|
||||
public function isOk()
|
||||
{
|
||||
return $this->error === UPLOAD_ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFile()
|
||||
{
|
||||
return $this->error !== UPLOAD_ERR_NO_FILE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move uploaded file to new location.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function move($dest)
|
||||
{
|
||||
$dir = dirname($dest);
|
||||
Nette\Utils\FileSystem::createDir($dir);
|
||||
@unlink($dest); // @ - file may not exists
|
||||
Nette\Utils\Callback::invokeSafe(
|
||||
is_uploaded_file($this->tmpName) ? 'move_uploaded_file' : 'rename',
|
||||
[$this->tmpName, $dest],
|
||||
function ($message) use ($dest) {
|
||||
throw new Nette\InvalidStateException("Unable to move uploaded file '$this->tmpName' to '$dest'. $message");
|
||||
}
|
||||
);
|
||||
@chmod($dest, 0666); // @ - possible low permission to chmod
|
||||
$this->tmpName = $dest;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is uploaded file GIF, PNG or JPEG?
|
||||
* @return bool
|
||||
*/
|
||||
public function isImage()
|
||||
{
|
||||
return in_array($this->getContentType(), ['image/gif', 'image/png', 'image/jpeg'], true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the image.
|
||||
* @return Nette\Utils\Image
|
||||
* @throws Nette\Utils\ImageException
|
||||
*/
|
||||
public function toImage()
|
||||
{
|
||||
return Nette\Utils\Image::fromFile($this->tmpName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the dimensions of an uploaded image as array.
|
||||
* @return array|null
|
||||
*/
|
||||
public function getImageSize()
|
||||
{
|
||||
return $this->isOk() ? @getimagesize($this->tmpName) : null; // @ - files smaller than 12 bytes causes read error
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get file contents.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContents()
|
||||
{
|
||||
// future implementation can try to work around safe_mode and open_basedir limitations
|
||||
return $this->isOk() ? file_get_contents($this->tmpName) : null;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
use Nette\Utils\DateTime;
|
||||
|
||||
|
||||
/**
|
||||
* Rendering helpers for HTTP.
|
||||
*/
|
||||
class Helpers
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/**
|
||||
* Returns HTTP valid date format.
|
||||
* @param string|int|\DateTimeInterface
|
||||
* @return string
|
||||
*/
|
||||
public static function formatDate($time)
|
||||
{
|
||||
$time = DateTime::from($time);
|
||||
$time->setTimezone(new \DateTimeZone('GMT'));
|
||||
return $time->format('D, d M Y H:i:s \G\M\T');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is IP address in CIDR block?
|
||||
* @return bool
|
||||
*/
|
||||
public static function ipMatch($ip, $mask)
|
||||
{
|
||||
list($mask, $size) = explode('/', $mask . '/');
|
||||
$tmp = function ($n) { return sprintf('%032b', $n); };
|
||||
$ip = implode('', array_map($tmp, unpack('N*', inet_pton($ip))));
|
||||
$mask = implode('', array_map($tmp, unpack('N*', inet_pton($mask))));
|
||||
$max = strlen($ip);
|
||||
if (!$max || $max !== strlen($mask) || (int) $size < 0 || (int) $size > $max) {
|
||||
return false;
|
||||
}
|
||||
return strncmp($ip, $mask, $size === '' ? $max : (int) $size) === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes duplicate cookies from response.
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public static function removeDuplicateCookies()
|
||||
{
|
||||
if (headers_sent($file, $line) || ini_get('suhosin.cookie.encrypt')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$flatten = [];
|
||||
foreach (headers_list() as $header) {
|
||||
if (preg_match('#^Set-Cookie: .+?=#', $header, $m)) {
|
||||
$flatten[$m[0]] = $header;
|
||||
header_remove('Set-Cookie');
|
||||
}
|
||||
}
|
||||
foreach (array_values($flatten) as $key => $header) {
|
||||
header($header, $key === 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
|
||||
/**
|
||||
* IHttpRequest provides access scheme for request sent via HTTP.
|
||||
*/
|
||||
interface IRequest
|
||||
{
|
||||
/** HTTP request method */
|
||||
const
|
||||
GET = 'GET',
|
||||
POST = 'POST',
|
||||
HEAD = 'HEAD',
|
||||
PUT = 'PUT',
|
||||
DELETE = 'DELETE',
|
||||
PATCH = 'PATCH',
|
||||
OPTIONS = 'OPTIONS';
|
||||
|
||||
/**
|
||||
* Returns URL object.
|
||||
* @return UrlScript
|
||||
*/
|
||||
function getUrl();
|
||||
|
||||
/********************* query, post, files & cookies ****************d*g**/
|
||||
|
||||
/**
|
||||
* Returns variable provided to the script via URL query ($_GET).
|
||||
* If no key is passed, returns the entire array.
|
||||
* @param string key
|
||||
* @param mixed default value
|
||||
* @return mixed
|
||||
*/
|
||||
function getQuery($key = null, $default = null);
|
||||
|
||||
/**
|
||||
* Returns variable provided to the script via POST method ($_POST).
|
||||
* If no key is passed, returns the entire array.
|
||||
* @param string key
|
||||
* @param mixed default value
|
||||
* @return mixed
|
||||
*/
|
||||
function getPost($key = null, $default = null);
|
||||
|
||||
/**
|
||||
* Returns uploaded file.
|
||||
* @param string key
|
||||
* @return FileUpload|array|null
|
||||
*/
|
||||
function getFile($key);
|
||||
|
||||
/**
|
||||
* Returns uploaded files.
|
||||
* @return array
|
||||
*/
|
||||
function getFiles();
|
||||
|
||||
/**
|
||||
* Returns variable provided to the script via HTTP cookies.
|
||||
* @param string key
|
||||
* @param mixed default value
|
||||
* @return mixed
|
||||
*/
|
||||
function getCookie($key, $default = null);
|
||||
|
||||
/**
|
||||
* Returns variables provided to the script via HTTP cookies.
|
||||
* @return array
|
||||
*/
|
||||
function getCookies();
|
||||
|
||||
/********************* method & headers ****************d*g**/
|
||||
|
||||
/**
|
||||
* Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive.
|
||||
* @return string
|
||||
*/
|
||||
function getMethod();
|
||||
|
||||
/**
|
||||
* Checks HTTP request method.
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
function isMethod($method);
|
||||
|
||||
/**
|
||||
* Return the value of the HTTP header. Pass the header name as the
|
||||
* plain, HTTP-specified header name (e.g. 'Accept-Encoding').
|
||||
* @param string
|
||||
* @param string|null
|
||||
* @return string|null
|
||||
*/
|
||||
function getHeader($header, $default = null);
|
||||
|
||||
/**
|
||||
* Returns all HTTP headers.
|
||||
* @return array
|
||||
*/
|
||||
function getHeaders();
|
||||
|
||||
/**
|
||||
* Is the request sent via secure channel (https)?
|
||||
* @return bool
|
||||
*/
|
||||
function isSecured();
|
||||
|
||||
/**
|
||||
* Is AJAX request?
|
||||
* @return bool
|
||||
*/
|
||||
function isAjax();
|
||||
|
||||
/**
|
||||
* Returns the IP address of the remote client.
|
||||
* @return string|null
|
||||
*/
|
||||
function getRemoteAddress();
|
||||
|
||||
/**
|
||||
* Returns the host of the remote client.
|
||||
* @return string|null
|
||||
*/
|
||||
function getRemoteHost();
|
||||
|
||||
/**
|
||||
* Returns raw content of HTTP request body.
|
||||
* @return string|null
|
||||
*/
|
||||
function getRawBody();
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
|
||||
/**
|
||||
* IHttpResponse interface.
|
||||
*/
|
||||
interface IResponse
|
||||
{
|
||||
/** @var int cookie expiration: forever (23.1.2037) */
|
||||
const PERMANENT = 2116333333;
|
||||
|
||||
/** @var int cookie expiration: until the browser is closed */
|
||||
const BROWSER = 0;
|
||||
|
||||
/** HTTP 1.1 response code */
|
||||
const
|
||||
S100_CONTINUE = 100,
|
||||
S101_SWITCHING_PROTOCOLS = 101,
|
||||
S102_PROCESSING = 102,
|
||||
S200_OK = 200,
|
||||
S201_CREATED = 201,
|
||||
S202_ACCEPTED = 202,
|
||||
S203_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
S204_NO_CONTENT = 204,
|
||||
S205_RESET_CONTENT = 205,
|
||||
S206_PARTIAL_CONTENT = 206,
|
||||
S207_MULTI_STATUS = 207,
|
||||
S208_ALREADY_REPORTED = 208,
|
||||
S226_IM_USED = 226,
|
||||
S300_MULTIPLE_CHOICES = 300,
|
||||
S301_MOVED_PERMANENTLY = 301,
|
||||
S302_FOUND = 302,
|
||||
S303_SEE_OTHER = 303,
|
||||
S303_POST_GET = 303,
|
||||
S304_NOT_MODIFIED = 304,
|
||||
S305_USE_PROXY = 305,
|
||||
S307_TEMPORARY_REDIRECT = 307,
|
||||
S308_PERMANENT_REDIRECT = 308,
|
||||
S400_BAD_REQUEST = 400,
|
||||
S401_UNAUTHORIZED = 401,
|
||||
S402_PAYMENT_REQUIRED = 402,
|
||||
S403_FORBIDDEN = 403,
|
||||
S404_NOT_FOUND = 404,
|
||||
S405_METHOD_NOT_ALLOWED = 405,
|
||||
S406_NOT_ACCEPTABLE = 406,
|
||||
S407_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
S408_REQUEST_TIMEOUT = 408,
|
||||
S409_CONFLICT = 409,
|
||||
S410_GONE = 410,
|
||||
S411_LENGTH_REQUIRED = 411,
|
||||
S412_PRECONDITION_FAILED = 412,
|
||||
S413_REQUEST_ENTITY_TOO_LARGE = 413,
|
||||
S414_REQUEST_URI_TOO_LONG = 414,
|
||||
S415_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
S416_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
||||
S417_EXPECTATION_FAILED = 417,
|
||||
S421_MISDIRECTED_REQUEST = 421,
|
||||
S422_UNPROCESSABLE_ENTITY = 422,
|
||||
S423_LOCKED = 423,
|
||||
S424_FAILED_DEPENDENCY = 424,
|
||||
S426_UPGRADE_REQUIRED = 426,
|
||||
S428_PRECONDITION_REQUIRED = 428,
|
||||
S429_TOO_MANY_REQUESTS = 429,
|
||||
S431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
S451_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||
S500_INTERNAL_SERVER_ERROR = 500,
|
||||
S501_NOT_IMPLEMENTED = 501,
|
||||
S502_BAD_GATEWAY = 502,
|
||||
S503_SERVICE_UNAVAILABLE = 503,
|
||||
S504_GATEWAY_TIMEOUT = 504,
|
||||
S505_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
S506_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
S507_INSUFFICIENT_STORAGE = 507,
|
||||
S508_LOOP_DETECTED = 508,
|
||||
S510_NOT_EXTENDED = 510,
|
||||
S511_NETWORK_AUTHENTICATION_REQUIRED = 511;
|
||||
|
||||
/**
|
||||
* Sets HTTP response code.
|
||||
* @param int
|
||||
* @return static
|
||||
*/
|
||||
function setCode($code);
|
||||
|
||||
/**
|
||||
* Returns HTTP response code.
|
||||
* @return int
|
||||
*/
|
||||
function getCode();
|
||||
|
||||
/**
|
||||
* Sends a HTTP header and replaces a previous one.
|
||||
* @param string header name
|
||||
* @param string header value
|
||||
* @return static
|
||||
*/
|
||||
function setHeader($name, $value);
|
||||
|
||||
/**
|
||||
* Adds HTTP header.
|
||||
* @param string header name
|
||||
* @param string header value
|
||||
* @return static
|
||||
*/
|
||||
function addHeader($name, $value);
|
||||
|
||||
/**
|
||||
* Sends a Content-type HTTP header.
|
||||
* @param string mime-type
|
||||
* @param string charset
|
||||
* @return static
|
||||
*/
|
||||
function setContentType($type, $charset = null);
|
||||
|
||||
/**
|
||||
* Redirects to a new URL.
|
||||
* @param string URL
|
||||
* @param int HTTP code
|
||||
* @return void
|
||||
*/
|
||||
function redirect($url, $code = self::S302_FOUND);
|
||||
|
||||
/**
|
||||
* Sets the number of seconds before a page cached on a browser expires.
|
||||
* @param string|int|\DateTimeInterface time, value 0 means "until the browser is closed"
|
||||
* @return static
|
||||
*/
|
||||
function setExpiration($seconds);
|
||||
|
||||
/**
|
||||
* Checks if headers have been sent.
|
||||
* @return bool
|
||||
*/
|
||||
function isSent();
|
||||
|
||||
/**
|
||||
* Returns value of an HTTP header.
|
||||
* @param string
|
||||
* @param string|null
|
||||
* @return string|null
|
||||
*/
|
||||
function getHeader($header, $default = null);
|
||||
|
||||
/**
|
||||
* Returns a list of headers to sent.
|
||||
* @return array (name => value)
|
||||
*/
|
||||
function getHeaders();
|
||||
|
||||
/**
|
||||
* Sends a cookie.
|
||||
* @param string name of the cookie
|
||||
* @param string value
|
||||
* @param string|int|\DateTimeInterface time, value 0 means "until the browser is closed"
|
||||
* @param string
|
||||
* @param string
|
||||
* @param bool
|
||||
* @param bool
|
||||
* @return static
|
||||
*/
|
||||
function setCookie($name, $value, $expire, $path = null, $domain = null, $secure = null, $httpOnly = null);
|
||||
|
||||
/**
|
||||
* Deletes a cookie.
|
||||
* @param string name of the cookie.
|
||||
* @param string
|
||||
* @param string
|
||||
* @param bool
|
||||
* @return void
|
||||
*/
|
||||
function deleteCookie($name, $path = null, $domain = null, $secure = null);
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
|
||||
/**
|
||||
* User session storage for PHP < 5.4. @see http://php.net/session_set_save_handler
|
||||
*
|
||||
* @deprecated since PHP 5.4, use \SessionHandlerInterface
|
||||
*/
|
||||
interface ISessionStorage
|
||||
{
|
||||
function open($savePath, $sessionName);
|
||||
|
||||
function close();
|
||||
|
||||
function read($id);
|
||||
|
||||
function write($id, $data);
|
||||
|
||||
function remove($id);
|
||||
|
||||
function clean($maxlifetime);
|
||||
}
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* HttpRequest provides access scheme for request sent via HTTP.
|
||||
*
|
||||
* @property-read UrlScript $url
|
||||
* @property-read array $query
|
||||
* @property-read array $post
|
||||
* @property-read array $files
|
||||
* @property-read array $cookies
|
||||
* @property-read string $method
|
||||
* @property-read array $headers
|
||||
* @property-read Url|null $referer
|
||||
* @property-read bool $secured
|
||||
* @property-read bool $ajax
|
||||
* @property-read string|null $remoteAddress
|
||||
* @property-read string|null $remoteHost
|
||||
* @property-read string|null $rawBody
|
||||
*/
|
||||
class Request implements IRequest
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string */
|
||||
private $method;
|
||||
|
||||
/** @var UrlScript */
|
||||
private $url;
|
||||
|
||||
/** @var array */
|
||||
private $post;
|
||||
|
||||
/** @var array */
|
||||
private $files;
|
||||
|
||||
/** @var array */
|
||||
private $cookies;
|
||||
|
||||
/** @var array */
|
||||
private $headers;
|
||||
|
||||
/** @var string|null */
|
||||
private $remoteAddress;
|
||||
|
||||
/** @var string|null */
|
||||
private $remoteHost;
|
||||
|
||||
/** @var callable|null */
|
||||
private $rawBodyCallback;
|
||||
|
||||
|
||||
public function __construct(UrlScript $url, $query = null, $post = null, $files = null, $cookies = null,
|
||||
$headers = null, $method = null, $remoteAddress = null, $remoteHost = null, $rawBodyCallback = null)
|
||||
{
|
||||
$this->url = $url;
|
||||
if ($query !== null) {
|
||||
trigger_error('Nette\Http\Request::__construct(): parameter $query is deprecated.', E_USER_DEPRECATED);
|
||||
$url->setQuery($query);
|
||||
}
|
||||
$this->post = (array) $post;
|
||||
$this->files = (array) $files;
|
||||
$this->cookies = (array) $cookies;
|
||||
$this->headers = array_change_key_case((array) $headers, CASE_LOWER);
|
||||
$this->method = $method ?: 'GET';
|
||||
$this->remoteAddress = $remoteAddress;
|
||||
$this->remoteHost = $remoteHost;
|
||||
$this->rawBodyCallback = $rawBodyCallback;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns URL object.
|
||||
* @return UrlScript
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return clone $this->url;
|
||||
}
|
||||
|
||||
|
||||
/********************* query, post, files & cookies ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns variable provided to the script via URL query ($_GET).
|
||||
* If no key is passed, returns the entire array.
|
||||
* @param string key
|
||||
* @param mixed default value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQuery($key = null, $default = null)
|
||||
{
|
||||
if (func_num_args() === 0) {
|
||||
return $this->url->getQueryParameters();
|
||||
} else {
|
||||
return $this->url->getQueryParameter($key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns variable provided to the script via POST method ($_POST).
|
||||
* If no key is passed, returns the entire array.
|
||||
* @param string key
|
||||
* @param mixed default value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPost($key = null, $default = null)
|
||||
{
|
||||
if (func_num_args() === 0) {
|
||||
return $this->post;
|
||||
|
||||
} elseif (isset($this->post[$key])) {
|
||||
return $this->post[$key];
|
||||
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns uploaded file.
|
||||
* @param string key
|
||||
* @return FileUpload|array|null
|
||||
*/
|
||||
public function getFile($key)
|
||||
{
|
||||
return isset($this->files[$key]) ? $this->files[$key] : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns uploaded files.
|
||||
* @return array
|
||||
*/
|
||||
public function getFiles()
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns variable provided to the script via HTTP cookies.
|
||||
* @param string key
|
||||
* @param mixed default value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCookie($key, $default = null)
|
||||
{
|
||||
return isset($this->cookies[$key]) ? $this->cookies[$key] : $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns variables provided to the script via HTTP cookies.
|
||||
* @return array
|
||||
*/
|
||||
public function getCookies()
|
||||
{
|
||||
return $this->cookies;
|
||||
}
|
||||
|
||||
|
||||
/********************* method & headers ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive.
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the request method is the given one.
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function isMethod($method)
|
||||
{
|
||||
return strcasecmp($this->method, $method) === 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function isPost()
|
||||
{
|
||||
trigger_error('Method isPost() is deprecated, use isMethod(\'POST\') instead.', E_USER_DEPRECATED);
|
||||
return $this->isMethod('POST');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the value of the HTTP header. Pass the header name as the
|
||||
* plain, HTTP-specified header name (e.g. 'Accept-Encoding').
|
||||
* @param string
|
||||
* @param string|null
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHeader($header, $default = null)
|
||||
{
|
||||
$header = strtolower($header);
|
||||
return isset($this->headers[$header]) ? $this->headers[$header] : $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all HTTP headers.
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns referrer.
|
||||
* @return Url|null
|
||||
*/
|
||||
public function getReferer()
|
||||
{
|
||||
return isset($this->headers['referer']) ? new Url($this->headers['referer']) : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the request sent via secure channel (https)?
|
||||
* @return bool
|
||||
*/
|
||||
public function isSecured()
|
||||
{
|
||||
return $this->url->getScheme() === 'https';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is the request sent from the same origin?
|
||||
* @return bool
|
||||
*/
|
||||
public function isSameSite()
|
||||
{
|
||||
return isset($this->cookies['nette-samesite']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is AJAX request?
|
||||
* @return bool
|
||||
*/
|
||||
public function isAjax()
|
||||
{
|
||||
return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the IP address of the remote client.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRemoteAddress()
|
||||
{
|
||||
return $this->remoteAddress;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the host of the remote client.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRemoteHost()
|
||||
{
|
||||
if ($this->remoteHost === null && $this->remoteAddress !== null) {
|
||||
$this->remoteHost = gethostbyaddr($this->remoteAddress);
|
||||
}
|
||||
return $this->remoteHost;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns raw content of HTTP request body.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRawBody()
|
||||
{
|
||||
return $this->rawBodyCallback ? call_user_func($this->rawBodyCallback) : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse Accept-Language header and returns preferred language.
|
||||
* @param string[] supported languages
|
||||
* @return string|null
|
||||
*/
|
||||
public function detectLanguage(array $langs)
|
||||
{
|
||||
$header = $this->getHeader('Accept-Language');
|
||||
if (!$header) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$s = strtolower($header); // case insensitive
|
||||
$s = strtr($s, '_', '-'); // cs_CZ means cs-CZ
|
||||
rsort($langs); // first more specific
|
||||
preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches);
|
||||
|
||||
if (!$matches[0]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$max = 0;
|
||||
$lang = null;
|
||||
foreach ($matches[1] as $key => $value) {
|
||||
$q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key];
|
||||
if ($q > $max) {
|
||||
$max = $q;
|
||||
$lang = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
}
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
|
||||
/**
|
||||
* Current HTTP request factory.
|
||||
*/
|
||||
class RequestFactory
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @internal */
|
||||
const CHARS = '\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}';
|
||||
|
||||
/** @var array */
|
||||
public $urlFilters = [
|
||||
'path' => ['#/{2,}#' => '/'], // '%20' => ''
|
||||
'url' => [], // '#[.,)]\z#' => ''
|
||||
];
|
||||
|
||||
/** @var bool */
|
||||
private $binary = false;
|
||||
|
||||
/** @var array */
|
||||
private $proxies = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param bool
|
||||
* @return static
|
||||
*/
|
||||
public function setBinary($binary = true)
|
||||
{
|
||||
$this->binary = (bool) $binary;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array|string
|
||||
* @return static
|
||||
*/
|
||||
public function setProxy($proxy)
|
||||
{
|
||||
$this->proxies = (array) $proxy;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates current HttpRequest object.
|
||||
* @return Request
|
||||
*/
|
||||
public function createHttpRequest()
|
||||
{
|
||||
// DETECTS URI, base path and script path of the request.
|
||||
$url = new UrlScript;
|
||||
$url->setScheme(!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http');
|
||||
$url->setUser(isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '');
|
||||
$url->setPassword(isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '');
|
||||
|
||||
// host & port
|
||||
if (
|
||||
(isset($_SERVER[$tmp = 'HTTP_HOST']) || isset($_SERVER[$tmp = 'SERVER_NAME']))
|
||||
&& preg_match('#^([a-z0-9_.-]+|\[[a-f0-9:]+\])(:\d+)?\z#i', $_SERVER[$tmp], $pair)
|
||||
) {
|
||||
$url->setHost(strtolower($pair[1]));
|
||||
if (isset($pair[2])) {
|
||||
$url->setPort((int) substr($pair[2], 1));
|
||||
} elseif (isset($_SERVER['SERVER_PORT'])) {
|
||||
$url->setPort((int) $_SERVER['SERVER_PORT']);
|
||||
}
|
||||
}
|
||||
|
||||
// path & query
|
||||
$requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
|
||||
$requestUrl = preg_replace('#^\w++://[^/]++#', '', $requestUrl);
|
||||
$requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']);
|
||||
$tmp = explode('?', $requestUrl, 2);
|
||||
$path = Url::unescape($tmp[0], '%/?#');
|
||||
$path = Strings::fixEncoding(Strings::replace($path, $this->urlFilters['path']));
|
||||
$url->setPath($path);
|
||||
$url->setQuery(isset($tmp[1]) ? $tmp[1] : '');
|
||||
|
||||
// detect script path
|
||||
$lpath = strtolower($path);
|
||||
$script = isset($_SERVER['SCRIPT_NAME']) ? strtolower($_SERVER['SCRIPT_NAME']) : '';
|
||||
if ($lpath !== $script) {
|
||||
$max = min(strlen($lpath), strlen($script));
|
||||
for ($i = 0; $i < $max && $lpath[$i] === $script[$i]; $i++);
|
||||
$path = $i ? substr($path, 0, strrpos($path, '/', $i - strlen($path) - 1) + 1) : '/';
|
||||
}
|
||||
$url->setScriptPath($path);
|
||||
|
||||
// GET, POST, COOKIE
|
||||
$useFilter = (!in_array(ini_get('filter.default'), ['', 'unsafe_raw'], true) || ini_get('filter.default_flags'));
|
||||
|
||||
$query = $url->getQueryParameters();
|
||||
$post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? [] : $_POST);
|
||||
$cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? [] : $_COOKIE);
|
||||
|
||||
// remove invalid characters
|
||||
$reChars = '#^[' . self::CHARS . ']*+\z#u';
|
||||
if (!$this->binary) {
|
||||
$list = [&$query, &$post, &$cookies];
|
||||
while (list($key, $val) = @each($list)) { // @ intentionally, deprecated in PHP 7.2
|
||||
foreach ($val as $k => $v) {
|
||||
if (is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
|
||||
unset($list[$key][$k]);
|
||||
|
||||
} elseif (is_array($v)) {
|
||||
$list[$key][$k] = $v;
|
||||
$list[] = &$list[$key][$k];
|
||||
|
||||
} else {
|
||||
$list[$key][$k] = (string) preg_replace('#[^' . self::CHARS . ']+#u', '', $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($list, $key, $val, $k, $v);
|
||||
}
|
||||
$url->setQuery($query);
|
||||
|
||||
|
||||
// FILES and create FileUpload objects
|
||||
$files = [];
|
||||
$list = [];
|
||||
if (!empty($_FILES)) {
|
||||
foreach ($_FILES as $k => $v) {
|
||||
if (
|
||||
!is_array($v)
|
||||
|| !isset($v['name'], $v['type'], $v['size'], $v['tmp_name'], $v['error'])
|
||||
|| (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error()))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$v['@'] = &$files[$k];
|
||||
$list[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
while (list(, $v) = @each($list)) { // @ intentionally, deprecated in PHP 7.2
|
||||
if (!isset($v['name'])) {
|
||||
continue;
|
||||
|
||||
} elseif (!is_array($v['name'])) {
|
||||
if (!$this->binary && (!preg_match($reChars, $v['name']) || preg_last_error())) {
|
||||
$v['name'] = '';
|
||||
}
|
||||
if ($v['error'] !== UPLOAD_ERR_NO_FILE) {
|
||||
$v['@'] = new FileUpload($v);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($v['name'] as $k => $foo) {
|
||||
if (!$this->binary && is_string($k) && (!preg_match($reChars, $k) || preg_last_error())) {
|
||||
continue;
|
||||
}
|
||||
$list[] = [
|
||||
'name' => $v['name'][$k],
|
||||
'type' => $v['type'][$k],
|
||||
'size' => $v['size'][$k],
|
||||
'tmp_name' => $v['tmp_name'][$k],
|
||||
'error' => $v['error'][$k],
|
||||
'@' => &$v['@'][$k],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// HEADERS
|
||||
if (function_exists('apache_request_headers')) {
|
||||
$headers = apache_request_headers();
|
||||
} else {
|
||||
$headers = [];
|
||||
foreach ($_SERVER as $k => $v) {
|
||||
if (strncmp($k, 'HTTP_', 5) == 0) {
|
||||
$k = substr($k, 5);
|
||||
} elseif (strncmp($k, 'CONTENT_', 8)) {
|
||||
continue;
|
||||
}
|
||||
$headers[strtr($k, '_', '-')] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$remoteAddr = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
|
||||
$remoteHost = !empty($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
|
||||
|
||||
// use real client address and host if trusted proxy is used
|
||||
$usingTrustedProxy = $remoteAddr && array_filter($this->proxies, function ($proxy) use ($remoteAddr) {
|
||||
return Helpers::ipMatch($remoteAddr, $proxy);
|
||||
});
|
||||
if ($usingTrustedProxy) {
|
||||
if (!empty($_SERVER['HTTP_FORWARDED'])) {
|
||||
$forwardParams = preg_split('/[,;]/', $_SERVER['HTTP_FORWARDED']);
|
||||
foreach ($forwardParams as $forwardParam) {
|
||||
list($key, $value) = explode('=', $forwardParam, 2) + [1 => null];
|
||||
$proxyParams[strtolower(trim($key))][] = trim($value, " \t\"");
|
||||
}
|
||||
|
||||
if (isset($proxyParams['for'])) {
|
||||
$address = $proxyParams['for'][0];
|
||||
if (strpos($address, '[') === false) { //IPv4
|
||||
$remoteAddr = explode(':', $address)[0];
|
||||
} else { //IPv6
|
||||
$remoteAddr = substr($address, 1, strpos($address, ']') - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($proxyParams['host']) && count($proxyParams['host']) === 1) {
|
||||
$host = $proxyParams['host'][0];
|
||||
$startingDelimiterPosition = strpos($host, '[');
|
||||
if ($startingDelimiterPosition === false) { //IPv4
|
||||
$remoteHostArr = explode(':', $host);
|
||||
$remoteHost = $remoteHostArr[0];
|
||||
if (isset($remoteHostArr[1])) {
|
||||
$url->setPort((int) $remoteHostArr[1]);
|
||||
}
|
||||
} else { //IPv6
|
||||
$endingDelimiterPosition = strpos($host, ']');
|
||||
$remoteHost = substr($host, strpos($host, '[') + 1, $endingDelimiterPosition - 1);
|
||||
$remoteHostArr = explode(':', substr($host, $endingDelimiterPosition));
|
||||
if (isset($remoteHostArr[1])) {
|
||||
$url->setPort((int) $remoteHostArr[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scheme = (isset($proxyParams['proto']) && count($proxyParams['proto']) === 1) ? $proxyParams['proto'][0] : 'http';
|
||||
$url->setScheme(strcasecmp($scheme, 'https') === 0 ? 'https' : 'http');
|
||||
} else {
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
|
||||
$url->setScheme(strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0 ? 'https' : 'http');
|
||||
$url->setPort($url->getScheme() === 'https' ? 443 : 80);
|
||||
}
|
||||
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
|
||||
$url->setPort((int) $_SERVER['HTTP_X_FORWARDED_PORT']);
|
||||
}
|
||||
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
$xForwardedForWithoutProxies = array_filter(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']), function ($ip) {
|
||||
return !array_filter($this->proxies, function ($proxy) use ($ip) {
|
||||
return filter_var(trim($ip), FILTER_VALIDATE_IP) !== false && Helpers::ipMatch(trim($ip), $proxy);
|
||||
});
|
||||
});
|
||||
$remoteAddr = trim(end($xForwardedForWithoutProxies));
|
||||
$xForwardedForRealIpKey = key($xForwardedForWithoutProxies);
|
||||
}
|
||||
|
||||
if (isset($xForwardedForRealIpKey) && !empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
|
||||
$xForwardedHost = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
|
||||
if (isset($xForwardedHost[$xForwardedForRealIpKey])) {
|
||||
$remoteHost = trim($xForwardedHost[$xForwardedForRealIpKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// method, eg. GET, PUT, ...
|
||||
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : null;
|
||||
if (
|
||||
$method === 'POST'
|
||||
&& isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
|
||||
&& preg_match('#^[A-Z]+\z#', $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])
|
||||
) {
|
||||
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
|
||||
}
|
||||
|
||||
// raw body
|
||||
$rawBodyCallback = function () {
|
||||
return file_get_contents('php://input');
|
||||
};
|
||||
|
||||
return new Request($url, null, $post, $files, $cookies, $headers, $method, $remoteAddr, $remoteHost, $rawBodyCallback);
|
||||
}
|
||||
}
|
||||
+322
@@ -0,0 +1,322 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
use Nette\Utils\DateTime;
|
||||
|
||||
|
||||
/**
|
||||
* HttpResponse class.
|
||||
*
|
||||
* @property-read array $headers
|
||||
*/
|
||||
class Response implements IResponse
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string The domain in which the cookie will be available */
|
||||
public $cookieDomain = '';
|
||||
|
||||
/** @var string The path in which the cookie will be available */
|
||||
public $cookiePath = '/';
|
||||
|
||||
/** @var bool Whether the cookie is available only through HTTPS */
|
||||
public $cookieSecure = false;
|
||||
|
||||
/** @var bool Whether the cookie is hidden from client-side */
|
||||
public $cookieHttpOnly = true;
|
||||
|
||||
/** @var bool Whether warn on possible problem with data in output buffer */
|
||||
public $warnOnBuffer = true;
|
||||
|
||||
/** @var bool Send invisible garbage for IE 6? */
|
||||
private static $fixIE = true;
|
||||
|
||||
/** @var int HTTP response code */
|
||||
private $code = self::S200_OK;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (is_int($code = http_response_code())) {
|
||||
$this->code = $code;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets HTTP response code.
|
||||
* @param int
|
||||
* @param string
|
||||
* @return static
|
||||
* @throws Nette\InvalidArgumentException if code is invalid
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function setCode($code, $reason = null)
|
||||
{
|
||||
$code = (int) $code;
|
||||
if ($code < 100 || $code > 599) {
|
||||
throw new Nette\InvalidArgumentException("Bad HTTP response '$code'.");
|
||||
}
|
||||
self::checkHeaders();
|
||||
$this->code = $code;
|
||||
|
||||
static $hasReason = [ // hardcoded in PHP
|
||||
100, 101,
|
||||
200, 201, 202, 203, 204, 205, 206,
|
||||
300, 301, 302, 303, 304, 305, 307, 308,
|
||||
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 426, 428, 429, 431,
|
||||
500, 501, 502, 503, 504, 505, 506, 511,
|
||||
];
|
||||
if ($reason || !in_array($code, $hasReason, true)) {
|
||||
$protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
|
||||
header("$protocol $code " . ($reason ?: 'Unknown status'));
|
||||
} else {
|
||||
http_response_code($code);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns HTTP response code.
|
||||
* @return int
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a HTTP header and replaces a previous one.
|
||||
* @param string header name
|
||||
* @param string header value
|
||||
* @return static
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function setHeader($name, $value)
|
||||
{
|
||||
self::checkHeaders();
|
||||
if ($value === null) {
|
||||
header_remove($name);
|
||||
} elseif (strcasecmp($name, 'Content-Length') === 0 && ini_get('zlib.output_compression')) {
|
||||
// ignore, PHP bug #44164
|
||||
} else {
|
||||
header($name . ': ' . $value, true, $this->code);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds HTTP header.
|
||||
* @param string header name
|
||||
* @param string header value
|
||||
* @return static
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function addHeader($name, $value)
|
||||
{
|
||||
self::checkHeaders();
|
||||
header($name . ': ' . $value, false, $this->code);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a Content-type HTTP header.
|
||||
* @param string mime-type
|
||||
* @param string charset
|
||||
* @return static
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function setContentType($type, $charset = null)
|
||||
{
|
||||
$this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : ''));
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Redirects to a new URL. Note: call exit() after it.
|
||||
* @param string URL
|
||||
* @param int HTTP code
|
||||
* @return void
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function redirect($url, $code = self::S302_FOUND)
|
||||
{
|
||||
$this->setCode($code);
|
||||
$this->setHeader('Location', $url);
|
||||
if (preg_match('#^https?:|^\s*+[a-z0-9+.-]*+[^:]#i', $url)) {
|
||||
$escapedUrl = htmlspecialchars($url, ENT_IGNORE | ENT_QUOTES, 'UTF-8');
|
||||
echo "<h1>Redirect</h1>\n\n<p><a href=\"$escapedUrl\">Please click here to continue</a>.</p>";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of seconds before a page cached on a browser expires.
|
||||
* @param string|int|\DateTimeInterface time, value 0 means "must-revalidate"
|
||||
* @return static
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function setExpiration($time)
|
||||
{
|
||||
$this->setHeader('Pragma', null);
|
||||
if (!$time) { // no cache
|
||||
$this->setHeader('Cache-Control', 's-maxage=0, max-age=0, must-revalidate');
|
||||
$this->setHeader('Expires', 'Mon, 23 Jan 1978 10:00:00 GMT');
|
||||
return $this;
|
||||
}
|
||||
|
||||
$time = DateTime::from($time);
|
||||
$this->setHeader('Cache-Control', 'max-age=' . ($time->format('U') - time()));
|
||||
$this->setHeader('Expires', Helpers::formatDate($time));
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if headers have been sent.
|
||||
* @return bool
|
||||
*/
|
||||
public function isSent()
|
||||
{
|
||||
return headers_sent();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns value of an HTTP header.
|
||||
* @param string
|
||||
* @param string|null
|
||||
* @return string|null
|
||||
*/
|
||||
public function getHeader($header, $default = null)
|
||||
{
|
||||
$header .= ':';
|
||||
$len = strlen($header);
|
||||
foreach (headers_list() as $item) {
|
||||
if (strncasecmp($item, $header, $len) === 0) {
|
||||
return ltrim(substr($item, $len));
|
||||
}
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of headers to sent.
|
||||
* @return array (name => value)
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
$headers = [];
|
||||
foreach (headers_list() as $header) {
|
||||
$a = strpos($header, ':');
|
||||
$headers[substr($header, 0, $a)] = (string) substr($header, $a + 2);
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function date($time = null)
|
||||
{
|
||||
trigger_error('Method date() is deprecated, use Nette\Http\Helpers::formatDate() instead.', E_USER_DEPRECATED);
|
||||
return Helpers::formatDate($time);
|
||||
}
|
||||
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (
|
||||
self::$fixIE
|
||||
&& isset($_SERVER['HTTP_USER_AGENT'])
|
||||
&& strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== false
|
||||
&& in_array($this->code, [400, 403, 404, 405, 406, 408, 409, 410, 500, 501, 505], true)
|
||||
&& preg_match('#^text/html(?:;|$)#', $this->getHeader('Content-Type'))
|
||||
) {
|
||||
echo Nette\Utils\Random::generate(2e3, " \t\r\n"); // sends invisible garbage for IE
|
||||
self::$fixIE = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends a cookie.
|
||||
* @param string name of the cookie
|
||||
* @param string value
|
||||
* @param string|int|\DateTimeInterface expiration time, value 0 means "until the browser is closed"
|
||||
* @param string
|
||||
* @param string
|
||||
* @param bool
|
||||
* @param bool
|
||||
* @param string
|
||||
* @return static
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function setCookie($name, $value, $time, $path = null, $domain = null, $secure = null, $httpOnly = null, $sameSite = null)
|
||||
{
|
||||
self::checkHeaders();
|
||||
$options = [
|
||||
'expires' => $time ? (int) DateTime::from($time)->format('U') : 0,
|
||||
'path' => $path === null ? $this->cookiePath : (string) $path,
|
||||
'domain' => $domain === null ? $this->cookieDomain : (string) $domain,
|
||||
'secure' => $secure === null ? $this->cookieSecure : (bool) $secure,
|
||||
'httponly' => $httpOnly === null ? $this->cookieHttpOnly : (bool) $httpOnly,
|
||||
'samesite' => (string) $sameSite,
|
||||
];
|
||||
if (PHP_VERSION_ID >= 70300) {
|
||||
setcookie($name, $value, $options);
|
||||
} else {
|
||||
setcookie(
|
||||
$name,
|
||||
$value,
|
||||
$options['expires'],
|
||||
$options['path'] . ($sameSite ? "; SameSite=$sameSite" : ''),
|
||||
$options['domain'],
|
||||
$options['secure'],
|
||||
$options['httponly']
|
||||
);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a cookie.
|
||||
* @param string name of the cookie.
|
||||
* @param string
|
||||
* @param string
|
||||
* @param bool
|
||||
* @return void
|
||||
* @throws Nette\InvalidStateException if HTTP headers have been sent
|
||||
*/
|
||||
public function deleteCookie($name, $path = null, $domain = null, $secure = null)
|
||||
{
|
||||
$this->setCookie($name, false, 0, $path, $domain, $secure);
|
||||
}
|
||||
|
||||
|
||||
private function checkHeaders()
|
||||
{
|
||||
if (PHP_SAPI === 'cli') {
|
||||
} elseif (headers_sent($file, $line)) {
|
||||
throw new Nette\InvalidStateException('Cannot send header after HTTP headers have been sent' . ($file ? " (output started at $file:$line)." : '.'));
|
||||
|
||||
} elseif ($this->warnOnBuffer && ob_get_length() && !array_filter(ob_get_status(true), function ($i) { return !$i['chunk_size']; })) {
|
||||
trigger_error('Possible problem: you are sending a HTTP header while already having some data in output buffer. Try Tracy\OutputDebugger or start session earlier.');
|
||||
}
|
||||
}
|
||||
}
|
||||
+546
@@ -0,0 +1,546 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Provides access to session sections as well as session settings and management methods.
|
||||
*/
|
||||
class Session
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** Default file lifetime */
|
||||
const DEFAULT_FILE_LIFETIME = 3 * Nette\Utils\DateTime::HOUR;
|
||||
|
||||
/** @var bool has been session ID regenerated? */
|
||||
private $regenerated = false;
|
||||
|
||||
/** @var bool has been session started? */
|
||||
private static $started = false;
|
||||
|
||||
/** @var array default configuration */
|
||||
private $options = [
|
||||
// security
|
||||
'referer_check' => '', // must be disabled because PHP implementation is invalid
|
||||
'use_cookies' => 1, // must be enabled to prevent Session Hijacking and Fixation
|
||||
'use_only_cookies' => 1, // must be enabled to prevent Session Fixation
|
||||
'use_trans_sid' => 0, // must be disabled to prevent Session Hijacking and Fixation
|
||||
|
||||
// cookies
|
||||
'cookie_lifetime' => 0, // until the browser is closed
|
||||
'cookie_path' => '/', // cookie is available within the entire domain
|
||||
'cookie_domain' => '', // cookie is available on current subdomain only
|
||||
'cookie_secure' => false, // cookie is available on HTTP & HTTPS
|
||||
'cookie_httponly' => true, // must be enabled to prevent Session Hijacking
|
||||
|
||||
// other
|
||||
'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME, // 3 hours
|
||||
];
|
||||
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
|
||||
/** @var IResponse */
|
||||
private $response;
|
||||
|
||||
/** @var \SessionHandlerInterface */
|
||||
private $handler;
|
||||
|
||||
|
||||
public function __construct(IRequest $request, IResponse $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts and initializes session data.
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return void
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
if (self::$started) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->configure($this->options);
|
||||
|
||||
if (!session_id()) { // session is started for first time
|
||||
$id = $this->request->getCookie(session_name());
|
||||
if (is_string($id) && preg_match('#^[0-9a-zA-Z,-]{22,256}\z#i', $id)) {
|
||||
session_id($id);
|
||||
} else {
|
||||
unset($_COOKIE[session_name()]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// session_start returns false on failure only sometimes
|
||||
Nette\Utils\Callback::invokeSafe('session_start', [], function ($message) use (&$e) {
|
||||
$e = new Nette\InvalidStateException($message);
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
if ($e) {
|
||||
@session_write_close(); // this is needed
|
||||
throw $e;
|
||||
}
|
||||
|
||||
self::$started = true;
|
||||
|
||||
/* structure:
|
||||
__NF: Data, Meta, Time
|
||||
DATA: section->variable = data
|
||||
META: section->variable = Timestamp
|
||||
*/
|
||||
$nf = &$_SESSION['__NF'];
|
||||
|
||||
if (!is_array($nf)) {
|
||||
$nf = [];
|
||||
}
|
||||
|
||||
// regenerate empty session
|
||||
if (empty($nf['Time'])) {
|
||||
$nf['Time'] = time();
|
||||
if (!empty($id)) { // ensures that the session was created in strict mode (see use_strict_mode)
|
||||
$this->regenerateId();
|
||||
}
|
||||
}
|
||||
|
||||
// process meta metadata
|
||||
if (isset($nf['META'])) {
|
||||
$now = time();
|
||||
// expire section variables
|
||||
foreach ($nf['META'] as $section => $metadata) {
|
||||
if (is_array($metadata)) {
|
||||
foreach ($metadata as $variable => $value) {
|
||||
if (!empty($value['T']) && $now > $value['T']) {
|
||||
if ($variable === '') { // expire whole section
|
||||
unset($nf['META'][$section], $nf['DATA'][$section]);
|
||||
continue 2;
|
||||
}
|
||||
unset($nf['META'][$section][$variable], $nf['DATA'][$section][$variable]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
register_shutdown_function([$this, 'clean']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Has been session started?
|
||||
* @return bool
|
||||
*/
|
||||
public function isStarted()
|
||||
{
|
||||
return (bool) self::$started;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ends the current session and store session data.
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (self::$started) {
|
||||
$this->clean();
|
||||
session_write_close();
|
||||
self::$started = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroys all data registered to a session.
|
||||
* @return void
|
||||
*/
|
||||
public function destroy()
|
||||
{
|
||||
if (!self::$started) {
|
||||
throw new Nette\InvalidStateException('Session is not started.');
|
||||
}
|
||||
|
||||
session_destroy();
|
||||
$_SESSION = null;
|
||||
self::$started = false;
|
||||
if (!$this->response->isSent()) {
|
||||
$params = session_get_cookie_params();
|
||||
$this->response->deleteCookie(session_name(), $params['path'], $params['domain'], $params['secure']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does session exists for the current request?
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
return self::$started || $this->request->getCookie($this->getName()) !== null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Regenerates the session ID.
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return void
|
||||
*/
|
||||
public function regenerateId()
|
||||
{
|
||||
if (self::$started && !$this->regenerated) {
|
||||
if (headers_sent($file, $line)) {
|
||||
throw new Nette\InvalidStateException('Cannot regenerate session ID after HTTP headers have been sent' . ($file ? " (output started at $file:$line)." : '.'));
|
||||
}
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
session_regenerate_id(true);
|
||||
session_write_close();
|
||||
}
|
||||
$backup = $_SESSION;
|
||||
session_start();
|
||||
$_SESSION = $backup;
|
||||
}
|
||||
$this->regenerated = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current session ID. Don't make dependencies, can be changed for each request.
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return session_id();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the session name to a specified one.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
if (!is_string($name) || !preg_match('#[^0-9.][^.]*\z#A', $name)) {
|
||||
throw new Nette\InvalidArgumentException('Session name must be a string and cannot contain dot.');
|
||||
}
|
||||
|
||||
session_name($name);
|
||||
return $this->setOptions([
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the session name.
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return isset($this->options['name']) ? $this->options['name'] : session_name();
|
||||
}
|
||||
|
||||
|
||||
/********************* sections management ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns specified session section.
|
||||
* @param string
|
||||
* @param string
|
||||
* @return SessionSection
|
||||
* @throws Nette\InvalidArgumentException
|
||||
*/
|
||||
public function getSection($section, $class = SessionSection::class)
|
||||
{
|
||||
return new $class($this, $section);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a session section exist and is not empty.
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSection($section)
|
||||
{
|
||||
if ($this->exists() && !self::$started) {
|
||||
$this->start();
|
||||
}
|
||||
|
||||
return !empty($_SESSION['__NF']['DATA'][$section]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Iteration over all sections.
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
if ($this->exists() && !self::$started) {
|
||||
$this->start();
|
||||
}
|
||||
|
||||
if (isset($_SESSION['__NF']['DATA'])) {
|
||||
return new \ArrayIterator(array_keys($_SESSION['__NF']['DATA']));
|
||||
|
||||
} else {
|
||||
return new \ArrayIterator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cleans and minimizes meta structures. This method is called automatically on shutdown, do not call it directly.
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
if (!self::$started || empty($_SESSION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nf = &$_SESSION['__NF'];
|
||||
if (isset($nf['META']) && is_array($nf['META'])) {
|
||||
foreach ($nf['META'] as $name => $foo) {
|
||||
if (empty($nf['META'][$name])) {
|
||||
unset($nf['META'][$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($nf['META'])) {
|
||||
unset($nf['META']);
|
||||
}
|
||||
|
||||
if (empty($nf['DATA'])) {
|
||||
unset($nf['DATA']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/********************* configuration ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Sets session options.
|
||||
* @param array
|
||||
* @return static
|
||||
* @throws Nette\NotSupportedException
|
||||
* @throws Nette\InvalidStateException
|
||||
*/
|
||||
public function setOptions(array $options)
|
||||
{
|
||||
$normalized = [];
|
||||
foreach ($options as $key => $value) {
|
||||
if (!strncmp($key, 'session.', 8)) { // back compatibility
|
||||
$key = substr($key, 8);
|
||||
}
|
||||
$key = strtolower(preg_replace('#(.)(?=[A-Z])#', '$1_', $key)); // camelCase -> snake_case
|
||||
$normalized[$key] = $value;
|
||||
}
|
||||
if (self::$started) {
|
||||
$this->configure($normalized);
|
||||
}
|
||||
$this->options = $normalized + $this->options;
|
||||
if (!empty($normalized['auto_start'])) {
|
||||
$this->start();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all session options.
|
||||
* @return array
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures session environment.
|
||||
* @param array
|
||||
* @return void
|
||||
*/
|
||||
private function configure(array $config)
|
||||
{
|
||||
$special = ['cache_expire' => 1, 'cache_limiter' => 1, 'save_path' => 1, 'name' => 1];
|
||||
$cookie = $origCookie = session_get_cookie_params();
|
||||
|
||||
foreach ($config as $key => $value) {
|
||||
if ($value === null || ini_get("session.$key") == $value) { // intentionally ==
|
||||
continue;
|
||||
|
||||
} elseif (strncmp($key, 'cookie_', 7) === 0) {
|
||||
$cookie[substr($key, 7)] = $value;
|
||||
|
||||
} else {
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
throw new Nette\InvalidStateException("Unable to set 'session.$key' to value '$value' when session has been started" . (self::$started ? '.' : ' by session.auto_start or session_start().'));
|
||||
}
|
||||
if (isset($special[$key])) {
|
||||
$key = "session_$key";
|
||||
$key($value);
|
||||
|
||||
} elseif (function_exists('ini_set')) {
|
||||
ini_set("session.$key", (string) $value);
|
||||
|
||||
} elseif (ini_get("session.$key") != $value) { // intentionally !=
|
||||
throw new Nette\NotSupportedException("Unable to set 'session.$key' to '$value' because function ini_set() is disabled.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($cookie !== $origCookie) {
|
||||
if (PHP_VERSION_ID >= 70300) {
|
||||
session_set_cookie_params($cookie);
|
||||
} else {
|
||||
session_set_cookie_params(
|
||||
$cookie['lifetime'],
|
||||
$cookie['path'] . (isset($cookie['samesite']) ? '; SameSite=' . $cookie['samesite'] : ''),
|
||||
$cookie['domain'],
|
||||
$cookie['secure'],
|
||||
$cookie['httponly']
|
||||
);
|
||||
}
|
||||
if (self::$started) {
|
||||
$this->sendCookie();
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->handler) {
|
||||
session_set_save_handler($this->handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the amount of time allowed between requests before the session will be terminated.
|
||||
* @param string|int|\DateTimeInterface time, value 0 means "until the browser is closed"
|
||||
* @return static
|
||||
*/
|
||||
public function setExpiration($time)
|
||||
{
|
||||
if (empty($time)) {
|
||||
return $this->setOptions([
|
||||
'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME,
|
||||
'cookie_lifetime' => 0,
|
||||
]);
|
||||
|
||||
} else {
|
||||
$time = Nette\Utils\DateTime::from($time)->format('U') - time();
|
||||
return $this->setOptions([
|
||||
'gc_maxlifetime' => $time,
|
||||
'cookie_lifetime' => $time,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the session cookie parameters.
|
||||
* @param string path
|
||||
* @param string domain
|
||||
* @param bool secure
|
||||
* @param string samesite
|
||||
* @return static
|
||||
*/
|
||||
public function setCookieParameters($path, $domain = null, $secure = null, $samesite = null)
|
||||
{
|
||||
return $this->setOptions([
|
||||
'cookie_path' => $path,
|
||||
'cookie_domain' => $domain,
|
||||
'cookie_secure' => $secure,
|
||||
'cookie_samesite' => $samesite,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the session cookie parameters.
|
||||
* @return array containing items: lifetime, path, domain, secure, httponly
|
||||
*/
|
||||
public function getCookieParameters()
|
||||
{
|
||||
return session_get_cookie_params();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets path of the directory used to save session data.
|
||||
* @return static
|
||||
*/
|
||||
public function setSavePath($path)
|
||||
{
|
||||
return $this->setOptions([
|
||||
'save_path' => $path,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated use setHandler().
|
||||
* @return static
|
||||
*/
|
||||
public function setStorage(ISessionStorage $storage)
|
||||
{
|
||||
if (self::$started) {
|
||||
throw new Nette\InvalidStateException('Unable to set storage when session has been started.');
|
||||
}
|
||||
session_set_save_handler(
|
||||
[$storage, 'open'], [$storage, 'close'], [$storage, 'read'],
|
||||
[$storage, 'write'], [$storage, 'remove'], [$storage, 'clean']
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets user session handler.
|
||||
* @return static
|
||||
*/
|
||||
public function setHandler(\SessionHandlerInterface $handler)
|
||||
{
|
||||
if (self::$started) {
|
||||
throw new Nette\InvalidStateException('Unable to set handler when session has been started.');
|
||||
}
|
||||
$this->handler = $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends the session cookies.
|
||||
* @return void
|
||||
*/
|
||||
private function sendCookie()
|
||||
{
|
||||
$cookie = $this->getCookieParameters();
|
||||
$this->response->setCookie(
|
||||
session_name(), session_id(),
|
||||
$cookie['lifetime'] ? $cookie['lifetime'] + time() : 0,
|
||||
$cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly'],
|
||||
isset($cookie['samesite']) ? $cookie['samesite'] : null
|
||||
);
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Session section.
|
||||
*/
|
||||
class SessionSection implements \IteratorAggregate, \ArrayAccess
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var bool */
|
||||
public $warnOnUndefined = false;
|
||||
|
||||
/** @var Session */
|
||||
private $session;
|
||||
|
||||
/** @var string */
|
||||
private $name;
|
||||
|
||||
/** @var array|null session data storage */
|
||||
private $data;
|
||||
|
||||
/** @var array|bool session metadata storage */
|
||||
private $meta = false;
|
||||
|
||||
|
||||
/**
|
||||
* Do not call directly. Use Session::getSection().
|
||||
*/
|
||||
public function __construct(Session $session, $name)
|
||||
{
|
||||
if (!is_string($name)) {
|
||||
throw new Nette\InvalidArgumentException('Session namespace must be a string, ' . gettype($name) . ' given.');
|
||||
}
|
||||
|
||||
$this->session = $session;
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
|
||||
private function start()
|
||||
{
|
||||
if ($this->meta === false) {
|
||||
$this->session->start();
|
||||
$this->data = &$_SESSION['__NF']['DATA'][$this->name];
|
||||
$this->meta = &$_SESSION['__NF']['META'][$this->name];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an iterator over all section variables.
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$this->start();
|
||||
if (isset($this->data)) {
|
||||
return new \ArrayIterator($this->data);
|
||||
} else {
|
||||
return new \ArrayIterator;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a variable in this session section.
|
||||
* @param string name
|
||||
* @param mixed value
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->start();
|
||||
$this->data[$name] = $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a variable from this session section.
|
||||
* @param string name
|
||||
* @return mixed
|
||||
*/
|
||||
public function &__get($name)
|
||||
{
|
||||
$this->start();
|
||||
if ($this->warnOnUndefined && !array_key_exists($name, $this->data)) {
|
||||
trigger_error("The variable '$name' does not exist in session section");
|
||||
}
|
||||
|
||||
return $this->data[$name];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether a variable in this session section is set.
|
||||
* @param string name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name)
|
||||
{
|
||||
if ($this->session->exists()) {
|
||||
$this->start();
|
||||
}
|
||||
return isset($this->data[$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unsets a variable in this session section.
|
||||
* @param string name
|
||||
* @return void
|
||||
*/
|
||||
public function __unset($name)
|
||||
{
|
||||
$this->start();
|
||||
unset($this->data[$name], $this->meta[$name]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a variable in this session section.
|
||||
* @param string name
|
||||
* @param mixed value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($name, $value)
|
||||
{
|
||||
$this->__set($name, $value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets a variable from this session section.
|
||||
* @param string name
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($name)
|
||||
{
|
||||
return $this->__get($name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether a variable in this session section is set.
|
||||
* @param string name
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($name)
|
||||
{
|
||||
return $this->__isset($name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unsets a variable in this session section.
|
||||
* @param string name
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($name)
|
||||
{
|
||||
$this->__unset($name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the expiration of the section or specific variables.
|
||||
* @param string|int|\DateTimeInterface time
|
||||
* @param mixed optional list of variables / single variable to expire
|
||||
* @return static
|
||||
*/
|
||||
public function setExpiration($time, $variables = null)
|
||||
{
|
||||
$this->start();
|
||||
if ($time) {
|
||||
$time = Nette\Utils\DateTime::from($time)->format('U');
|
||||
$max = (int) ini_get('session.gc_maxlifetime');
|
||||
if ($max !== 0 && ($time - time() > $max + 3)) { // 0 - unlimited in memcache handler, 3 - bulgarian constant
|
||||
trigger_error("The expiration time is greater than the session expiration $max seconds");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (is_array($variables) ? $variables : [$variables] as $variable) {
|
||||
$this->meta[$variable]['T'] = $time ?: null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the expiration from the section or specific variables.
|
||||
* @param mixed optional list of variables / single variable to expire
|
||||
* @return void
|
||||
*/
|
||||
public function removeExpiration($variables = null)
|
||||
{
|
||||
$this->start();
|
||||
foreach (is_array($variables) ? $variables : [$variables] as $variable) {
|
||||
unset($this->meta[$variable]['T']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cancels the current session section.
|
||||
* @return void
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->start();
|
||||
$this->data = null;
|
||||
$this->meta = null;
|
||||
}
|
||||
}
|
||||
Vendored
+514
@@ -0,0 +1,514 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* URI Syntax (RFC 3986).
|
||||
*
|
||||
* <pre>
|
||||
* scheme user password host port basePath relativeUrl
|
||||
* | | | | | | |
|
||||
* /--\ /--\ /------\ /-------\ /--\/--\/----------------------------\
|
||||
* http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl
|
||||
* \__________________________/\____________/^\________/^\______/
|
||||
* | | | |
|
||||
* authority path query fragment
|
||||
* </pre>
|
||||
*
|
||||
* - authority: [user[:password]@]host[:port]
|
||||
* - hostUrl: http://user:password@nette.org:8042
|
||||
* - basePath: /en/ (everything before relative URI not including the script name)
|
||||
* - baseUrl: http://user:password@nette.org:8042/en/
|
||||
* - relativeUrl: manual.php
|
||||
*
|
||||
* @property string $scheme
|
||||
* @property string $user
|
||||
* @property string $password
|
||||
* @property string $host
|
||||
* @property int $port
|
||||
* @property string $path
|
||||
* @property string $query
|
||||
* @property string $fragment
|
||||
* @property-read string $absoluteUrl
|
||||
* @property-read string $authority
|
||||
* @property-read string $hostUrl
|
||||
* @property-read string $basePath
|
||||
* @property-read string $baseUrl
|
||||
* @property-read string $relativeUrl
|
||||
* @property-read array $queryParameters
|
||||
*/
|
||||
class Url implements \JsonSerializable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array */
|
||||
public static $defaultPorts = [
|
||||
'http' => 80,
|
||||
'https' => 443,
|
||||
'ftp' => 21,
|
||||
'news' => 119,
|
||||
'nntp' => 119,
|
||||
];
|
||||
|
||||
/** @var string */
|
||||
private $scheme = '';
|
||||
|
||||
/** @var string */
|
||||
private $user = '';
|
||||
|
||||
/** @var string */
|
||||
private $password = '';
|
||||
|
||||
/** @var string */
|
||||
private $host = '';
|
||||
|
||||
/** @var int|null */
|
||||
private $port;
|
||||
|
||||
/** @var string */
|
||||
private $path = '';
|
||||
|
||||
/** @var array */
|
||||
private $query = [];
|
||||
|
||||
/** @var string */
|
||||
private $fragment = '';
|
||||
|
||||
|
||||
/**
|
||||
* @param string|self
|
||||
* @throws Nette\InvalidArgumentException if URL is malformed
|
||||
*/
|
||||
public function __construct($url = null)
|
||||
{
|
||||
if (is_string($url)) {
|
||||
$p = @parse_url($url); // @ - is escalated to exception
|
||||
if ($p === false) {
|
||||
throw new Nette\InvalidArgumentException("Malformed or unsupported URI '$url'.");
|
||||
}
|
||||
|
||||
$this->scheme = isset($p['scheme']) ? $p['scheme'] : '';
|
||||
$this->port = isset($p['port']) ? $p['port'] : null;
|
||||
$this->host = isset($p['host']) ? rawurldecode($p['host']) : '';
|
||||
$this->user = isset($p['user']) ? rawurldecode($p['user']) : '';
|
||||
$this->password = isset($p['pass']) ? rawurldecode($p['pass']) : '';
|
||||
$this->setPath(isset($p['path']) ? $p['path'] : '');
|
||||
$this->setQuery(isset($p['query']) ? $p['query'] : []);
|
||||
$this->fragment = isset($p['fragment']) ? rawurldecode($p['fragment']) : '';
|
||||
|
||||
} elseif ($url instanceof self) {
|
||||
foreach ($this as $key => $val) {
|
||||
$this->$key = $url->$key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the scheme part of URI.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setScheme($value)
|
||||
{
|
||||
$this->scheme = (string) $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the scheme part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getScheme()
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the user name part of URI.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setUser($value)
|
||||
{
|
||||
$this->user = (string) $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the user name part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the password part of URI.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setPassword($value)
|
||||
{
|
||||
$this->password = (string) $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the password part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the host part of URI.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setHost($value)
|
||||
{
|
||||
$this->host = (string) $value;
|
||||
$this->setPath($this->path);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the host part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getHost()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the part of domain.
|
||||
* @return string
|
||||
*/
|
||||
public function getDomain($level = 2)
|
||||
{
|
||||
$parts = ip2long($this->host) ? [$this->host] : explode('.', $this->host);
|
||||
$parts = $level >= 0 ? array_slice($parts, -$level) : array_slice($parts, 0, $level);
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the port part of URI.
|
||||
* @param int
|
||||
* @return static
|
||||
*/
|
||||
public function setPort($value)
|
||||
{
|
||||
$this->port = (int) $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the port part of URI.
|
||||
* @return int|null
|
||||
*/
|
||||
public function getPort()
|
||||
{
|
||||
return $this->port ?: (isset(self::$defaultPorts[$this->scheme]) ? self::$defaultPorts[$this->scheme] : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the path part of URI.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setPath($value)
|
||||
{
|
||||
$this->path = (string) $value;
|
||||
if ($this->host && substr($this->path, 0, 1) !== '/') {
|
||||
$this->path = '/' . $this->path;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the path part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the query part of URI.
|
||||
* @param string|array
|
||||
* @return static
|
||||
*/
|
||||
public function setQuery($value)
|
||||
{
|
||||
$this->query = is_array($value) ? $value : self::parseQuery($value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends the query part of URI.
|
||||
* @param string|array
|
||||
* @return static
|
||||
*/
|
||||
public function appendQuery($value)
|
||||
{
|
||||
$this->query = is_array($value)
|
||||
? $value + $this->query
|
||||
: self::parseQuery($this->getQuery() . '&' . $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the query part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return http_build_query($this->query, '', '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getQueryParameters()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @param mixed
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQueryParameter($name, $default = null)
|
||||
{
|
||||
return isset($this->query[$name]) ? $this->query[$name] : $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @param mixed null unsets the parameter
|
||||
* @return static
|
||||
*/
|
||||
public function setQueryParameter($name, $value)
|
||||
{
|
||||
$this->query[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the fragment part of URI.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setFragment($value)
|
||||
{
|
||||
$this->fragment = (string) $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fragment part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getFragment()
|
||||
{
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the entire URI including query string and fragment.
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteUrl()
|
||||
{
|
||||
return $this->getHostUrl() . $this->path
|
||||
. (($tmp = $this->getQuery()) ? '?' . $tmp : '')
|
||||
. ($this->fragment === '' ? '' : '#' . $this->fragment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the [user[:pass]@]host[:port] part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getAuthority()
|
||||
{
|
||||
return $this->host === ''
|
||||
? ''
|
||||
: ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https'
|
||||
? rawurlencode($this->user) . ($this->password === '' ? '' : ':' . rawurlencode($this->password)) . '@'
|
||||
: '')
|
||||
. $this->host
|
||||
. ($this->port && (!isset(self::$defaultPorts[$this->scheme]) || $this->port !== self::$defaultPorts[$this->scheme])
|
||||
? ':' . $this->port
|
||||
: '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the scheme and authority part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getHostUrl()
|
||||
{
|
||||
return ($this->scheme ? $this->scheme . ':' : '')
|
||||
. (($authority = $this->getAuthority()) || $this->scheme ? '//' . $authority : '');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base-path.
|
||||
* @return string
|
||||
*/
|
||||
public function getBasePath()
|
||||
{
|
||||
$pos = strrpos($this->path, '/');
|
||||
return $pos === false ? '' : substr($this->path, 0, $pos + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base-URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->getHostUrl() . $this->getBasePath();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the relative-URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getRelativeUrl()
|
||||
{
|
||||
return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* URL comparison.
|
||||
* @param string|self
|
||||
* @return bool
|
||||
*/
|
||||
public function isEqual($url)
|
||||
{
|
||||
$url = new self($url);
|
||||
$query = $url->query;
|
||||
ksort($query);
|
||||
$query2 = $this->query;
|
||||
ksort($query2);
|
||||
$http = in_array($this->scheme, ['http', 'https'], true);
|
||||
return $url->scheme === $this->scheme
|
||||
&& !strcasecmp($url->host, $this->host)
|
||||
&& $url->getPort() === $this->getPort()
|
||||
&& ($http || $url->user === $this->user)
|
||||
&& ($http || $url->password === $this->password)
|
||||
&& self::unescape($url->path, '%/') === self::unescape($this->path, '%/')
|
||||
&& $query === $query2
|
||||
&& $url->fragment === $this->fragment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transforms URL to canonical form.
|
||||
* @return static
|
||||
*/
|
||||
public function canonicalize()
|
||||
{
|
||||
$this->path = preg_replace_callback(
|
||||
'#[^!$&\'()*+,/:;=@%]+#',
|
||||
function ($m) { return rawurlencode($m[0]); },
|
||||
self::unescape($this->path, '%/')
|
||||
);
|
||||
$this->host = strtolower($this->host);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getAbsoluteUrl();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->getAbsoluteUrl();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Similar to rawurldecode, but preserves reserved chars encoded.
|
||||
* @param string to decode
|
||||
* @param string reserved characters
|
||||
* @return string
|
||||
*/
|
||||
public static function unescape($s, $reserved = '%;/?:@&=+$,')
|
||||
{
|
||||
// reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
|
||||
// within a path segment, the characters "/", ";", "=", "?" are reserved
|
||||
// within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved.
|
||||
if ($reserved !== '') {
|
||||
$s = preg_replace_callback(
|
||||
'#%(' . substr(chunk_split(bin2hex($reserved), 2, '|'), 0, -1) . ')#i',
|
||||
function ($m) { return '%25' . strtoupper($m[1]); },
|
||||
$s
|
||||
);
|
||||
}
|
||||
return rawurldecode($s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses query string.
|
||||
* @return array
|
||||
*/
|
||||
public static function parseQuery($s)
|
||||
{
|
||||
parse_str($s, $res);
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Immutable representation of a URL.
|
||||
*
|
||||
* <pre>
|
||||
* scheme user password host port path query fragment
|
||||
* | | | | | | | |
|
||||
* /--\ /--\ /------\ /-------\ /--\/------------\ /--------\ /------\
|
||||
* http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl
|
||||
* \______\__________________________/
|
||||
* | |
|
||||
* hostUrl authority
|
||||
* </pre>
|
||||
*
|
||||
* @property-read string $scheme
|
||||
* @property-read string $user
|
||||
* @property-read string $password
|
||||
* @property-read string $host
|
||||
* @property-read int $port
|
||||
* @property-read string $path
|
||||
* @property-read string $query
|
||||
* @property-read string $fragment
|
||||
* @property-read string $absoluteUrl
|
||||
* @property-read string $authority
|
||||
* @property-read string $hostUrl
|
||||
* @property-read array $queryParameters
|
||||
*/
|
||||
class UrlImmutable implements \JsonSerializable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string */
|
||||
private $scheme = '';
|
||||
|
||||
/** @var string */
|
||||
private $user = '';
|
||||
|
||||
/** @var string */
|
||||
private $password = '';
|
||||
|
||||
/** @var string */
|
||||
private $host = '';
|
||||
|
||||
/** @var int|null */
|
||||
private $port;
|
||||
|
||||
/** @var string */
|
||||
private $path = '';
|
||||
|
||||
/** @var array */
|
||||
private $query = [];
|
||||
|
||||
/** @var string */
|
||||
private $fragment = '';
|
||||
|
||||
/** @var string */
|
||||
private $authority = '';
|
||||
|
||||
|
||||
/**
|
||||
* @param string|self|Url $url
|
||||
* @throws Nette\InvalidArgumentException if URL is malformed
|
||||
*/
|
||||
public function __construct($url)
|
||||
{
|
||||
if ($url instanceof Url || $url instanceof self || is_string($url)) {
|
||||
$url = is_string($url) ? new Url($url) : $url;
|
||||
[$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment] = $url->export();
|
||||
} else {
|
||||
throw new Nette\InvalidArgumentException;
|
||||
}
|
||||
|
||||
$this->build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withScheme(string $scheme)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->scheme = $scheme;
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getScheme(): string
|
||||
{
|
||||
return $this->scheme;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withUser(string $user)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->user = $user;
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getUser(): string
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withPassword(string $password)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->password = $password;
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withHost(string $host)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->host = $host;
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getHost(): string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
|
||||
public function getDomain(int $level = 2): string
|
||||
{
|
||||
$parts = ip2long($this->host) ? [$this->host] : explode('.', $this->host);
|
||||
$parts = $level >= 0 ? array_slice($parts, -$level) : array_slice($parts, 0, $level);
|
||||
return implode('.', $parts);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withPort(int $port)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->port = $port;
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getPort(): ?int
|
||||
{
|
||||
return $this->port ?: (Url::$defaultPorts[$this->scheme] ?? null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withPath(string $path)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->path = $path;
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|array $query
|
||||
* @return static
|
||||
*/
|
||||
public function withQuery($query)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->query = is_array($query) ? $query : Url::parseQuery($query);
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getQuery(): string
|
||||
{
|
||||
return http_build_query($this->query, '', '&', PHP_QUERY_RFC3986);
|
||||
}
|
||||
|
||||
|
||||
public function getQueryParameters(): array
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array|string|null
|
||||
*/
|
||||
public function getQueryParameter(string $name)
|
||||
{
|
||||
return $this->query[$name] ?? null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withFragment(string $fragment)
|
||||
{
|
||||
$dolly = clone $this;
|
||||
$dolly->fragment = $fragment;
|
||||
$dolly->build();
|
||||
return $dolly;
|
||||
}
|
||||
|
||||
|
||||
public function getFragment(): string
|
||||
{
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the entire URI including query string and fragment.
|
||||
*/
|
||||
public function getAbsoluteUrl(): string
|
||||
{
|
||||
return $this->getHostUrl() . $this->path
|
||||
. (($tmp = $this->getQuery()) ? '?' . $tmp : '')
|
||||
. ($this->fragment === '' ? '' : '#' . $this->fragment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the [user[:pass]@]host[:port] part of URI.
|
||||
*/
|
||||
public function getAuthority(): string
|
||||
{
|
||||
return $this->authority;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the scheme and authority part of URI.
|
||||
*/
|
||||
public function getHostUrl(): string
|
||||
{
|
||||
return ($this->scheme ? $this->scheme . ':' : '')
|
||||
. ($this->authority ? '//' . $this->authority : '');
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getAbsoluteUrl();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|Url|self $url
|
||||
*/
|
||||
public function isEqual($url): bool
|
||||
{
|
||||
return (new Url($this))->isEqual($url);
|
||||
}
|
||||
|
||||
|
||||
public function jsonSerialize(): string
|
||||
{
|
||||
return $this->getAbsoluteUrl();
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
final public function export(): array
|
||||
{
|
||||
return [$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment];
|
||||
}
|
||||
|
||||
|
||||
protected function build(): void
|
||||
{
|
||||
if ($this->host && substr($this->path, 0, 1) !== '/') {
|
||||
$this->path = '/' . $this->path;
|
||||
}
|
||||
|
||||
$this->authority = $this->host === ''
|
||||
? ''
|
||||
: ($this->user !== ''
|
||||
? rawurlencode($this->user) . ($this->password === '' ? '' : ':' . rawurlencode($this->password)) . '@'
|
||||
: '')
|
||||
. $this->host
|
||||
. ($this->port && (!isset(Url::$defaultPorts[$this->scheme]) || $this->port !== Url::$defaultPorts[$this->scheme])
|
||||
? ':' . $this->port
|
||||
: '');
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
|
||||
/**
|
||||
* Extended HTTP URL.
|
||||
*
|
||||
* <pre>
|
||||
* http://nette.org/admin/script.php/pathinfo/?name=param#fragment
|
||||
* \_______________/\________/
|
||||
* | |
|
||||
* scriptPath pathInfo
|
||||
* </pre>
|
||||
*
|
||||
* - scriptPath: /admin/script.php (or simply /admin/ when script is directory index)
|
||||
* - pathInfo: /pathinfo/ (additional path information)
|
||||
*
|
||||
* @property string $scriptPath
|
||||
* @property-read string $pathInfo
|
||||
*/
|
||||
class UrlScript extends Url
|
||||
{
|
||||
/** @var string */
|
||||
private $scriptPath;
|
||||
|
||||
|
||||
public function __construct($url = null, $scriptPath = '')
|
||||
{
|
||||
parent::__construct($url);
|
||||
$this->setScriptPath($scriptPath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the script-path part of URI.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setScriptPath($value)
|
||||
{
|
||||
$this->scriptPath = (string) $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the script-path part of URI.
|
||||
* @return string
|
||||
*/
|
||||
public function getScriptPath()
|
||||
{
|
||||
return $this->scriptPath ?: $this->path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the base-path.
|
||||
* @return string
|
||||
*/
|
||||
public function getBasePath()
|
||||
{
|
||||
$pos = strrpos($this->getScriptPath(), '/');
|
||||
return $pos === false ? '' : substr($this->getPath(), 0, $pos + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the additional path information.
|
||||
* @return string
|
||||
*/
|
||||
public function getPathInfo()
|
||||
{
|
||||
return (string) substr($this->getPath(), strlen($this->getScriptPath()));
|
||||
}
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Http;
|
||||
|
||||
use Nette;
|
||||
use Nette\Security\IIdentity;
|
||||
|
||||
|
||||
/**
|
||||
* Session storage for user object.
|
||||
*/
|
||||
class UserStorage implements Nette\Security\IUserStorage
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string */
|
||||
private $namespace = '';
|
||||
|
||||
/** @var Session */
|
||||
private $sessionHandler;
|
||||
|
||||
/** @var SessionSection */
|
||||
private $sessionSection;
|
||||
|
||||
|
||||
public function __construct(Session $sessionHandler)
|
||||
{
|
||||
$this->sessionHandler = $sessionHandler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the authenticated status of this user.
|
||||
* @param bool
|
||||
* @return static
|
||||
*/
|
||||
public function setAuthenticated($state)
|
||||
{
|
||||
$section = $this->getSessionSection(true);
|
||||
$section->authenticated = (bool) $state;
|
||||
|
||||
// Session Fixation defence
|
||||
$this->sessionHandler->regenerateId();
|
||||
|
||||
if ($state) {
|
||||
$section->reason = null;
|
||||
$section->authTime = time(); // informative value
|
||||
|
||||
} else {
|
||||
$section->reason = self::MANUAL;
|
||||
$section->authTime = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this user authenticated?
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuthenticated()
|
||||
{
|
||||
$session = $this->getSessionSection(false);
|
||||
return $session && $session->authenticated;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the user identity.
|
||||
* @return static
|
||||
*/
|
||||
public function setIdentity(IIdentity $identity = null)
|
||||
{
|
||||
$this->getSessionSection(true)->identity = $identity;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current user identity, if any.
|
||||
* @return Nette\Security\IIdentity|null
|
||||
*/
|
||||
public function getIdentity()
|
||||
{
|
||||
$session = $this->getSessionSection(false);
|
||||
return $session ? $session->identity : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes namespace; allows more users to share a session.
|
||||
* @param string
|
||||
* @return static
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
if ($this->namespace !== $namespace) {
|
||||
$this->namespace = (string) $namespace;
|
||||
$this->sessionSection = null;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current namespace.
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables log out after inactivity.
|
||||
* @param string|int|\DateTimeInterface Number of seconds or timestamp
|
||||
* @param int flag IUserStorage::CLEAR_IDENTITY
|
||||
* @return static
|
||||
*/
|
||||
public function setExpiration($time, $flags = 0)
|
||||
{
|
||||
$section = $this->getSessionSection(true);
|
||||
if ($time) {
|
||||
$time = Nette\Utils\DateTime::from($time)->format('U');
|
||||
$section->expireTime = $time;
|
||||
$section->expireDelta = $time - time();
|
||||
|
||||
} else {
|
||||
unset($section->expireTime, $section->expireDelta);
|
||||
}
|
||||
|
||||
$section->expireIdentity = (bool) ($flags & self::CLEAR_IDENTITY);
|
||||
$section->setExpiration($time, 'foo'); // time check
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Why was user logged out?
|
||||
* @return int|null
|
||||
*/
|
||||
public function getLogoutReason()
|
||||
{
|
||||
$session = $this->getSessionSection(false);
|
||||
return $session ? $session->reason : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns and initializes $this->sessionSection.
|
||||
* @return SessionSection|null
|
||||
*/
|
||||
protected function getSessionSection($need)
|
||||
{
|
||||
if ($this->sessionSection !== null) {
|
||||
return $this->sessionSection;
|
||||
}
|
||||
|
||||
if (!$need && !$this->sessionHandler->exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->sessionSection = $section = $this->sessionHandler->getSection('Nette.Http.UserStorage/' . $this->namespace);
|
||||
|
||||
if (!$section->identity instanceof IIdentity || !is_bool($section->authenticated)) {
|
||||
$section->remove();
|
||||
}
|
||||
|
||||
if ($section->authenticated && $section->expireDelta > 0) { // check time expiration
|
||||
if ($section->expireTime < time()) {
|
||||
$section->reason = self::INACTIVITY;
|
||||
$section->authenticated = false;
|
||||
if ($section->expireIdentity) {
|
||||
unset($section->identity);
|
||||
}
|
||||
}
|
||||
$section->expireTime = time() + $section->expireDelta; // sliding expiration
|
||||
}
|
||||
|
||||
if (!$section->authenticated) {
|
||||
unset($section->expireTime, $section->expireDelta, $section->expireIdentity, $section->authTime);
|
||||
}
|
||||
|
||||
return $this->sessionSection;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user