Git Product home page Git Product logo

http's Introduction

Nette HTTP Component

Downloads this Month Tests Build Status Windows Coverage Status Latest Stable Version License

Introduction

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.

Documentation can be found on the website.

Do you like Nette DI? Are you looking forward to the new features?

Buy me a coffee

Thank you!

Installation

composer require nette/http

It requires PHP version 8.1 and supports PHP up to 8.3.

HTTP Request

An HTTP request is an Nette\Http\Request object. What is important is that Nette when creating this object, it clears all GET, POST and COOKIE input parameters as well as URLs of control characters and invalid UTF-8 sequences. So you can safely continue working with the data. The cleaned data is then used in presenters and forms.

Class Request is immutable. It has no setters, it has only one so-called wither withUrl(), which does not change the object, but returns a new instance with a modified value.

withUrl(Nette\Http\UrlScript $url): Nette\Http\Request

Returns a clone with a different URL.

getUrl(): Nette\Http\UrlScript

Returns the URL of the request as object [UrlScript|urls#UrlScript].

$url = $httpRequest->getUrl();
echo $url; // https://nette.org/en/documentation?action=edit
echo $url->getHost(); // nette.org

Browsers do not send a fragment to the server, so $url->getFragment() will return an empty string.

getQuery(string $key = null): string|array|null

Returns GET request parameters:

$all = $httpRequest->getQuery();    // array of all URL parameters
$id = $httpRequest->getQuery('id'); // returns GET parameter 'id' (or null)

getPost(string $key = null): string|array|null

Returns POST request parameters:

$all = $httpRequest->getPost();     // array of all POST parameters
$id = $httpRequest->getPost('id');  // returns POST parameter 'id' (or null)

getFile(string $key): Nette\Http\FileUpload|array|null

Returns upload as object Nette\Http\FileUpload:

$file = $httpRequest->getFile('avatar');
if ($file->hasFile()) { // was any file uploaded?
	$file->getName(); // name of the file sent by user
	$file->getSanitizedName(); // the name without dangerous characters
}

getFiles(): array

Returns tree of upload files in a normalized structure, with each leaf an instance of Nette\Http\FileUpload:

$files = $httpRequest->getFiles();

getCookie(string $key): string|array|null

Returns a cookie or null if it does not exist.

$sessId = $httpRequest->getCookie('sess_id');

getCookies(): array

Returns all cookies:

$cookies = $httpRequest->getCookies();

getMethod(): string

Returns the HTTP method with which the request was made.

echo $httpRequest->getMethod(); // GET, POST, HEAD, PUT

isMethod(string $method): bool

Checks the HTTP method with which the request was made. The parameter is case-insensitive.

if ($httpRequest->isMethod('GET')) ...

getHeader(string $header): ?string

Returns an HTTP header or null if it does not exist. The parameter is case-insensitive:

$userAgent = $httpRequest->getHeader('User-Agent');

getHeaders(): array

Returns all HTTP headers as associative array:

$headers = $httpRequest->getHeaders();
echo $headers['Content-Type'];

getReferer(): ?Nette\Http\UrlImmutable

What URL did the user come from? Beware, it is not reliable at all.

isSecured(): bool

Is the connection encrypted (HTTPS)? You may need to [set up a proxy|configuring#HTTP proxy] for proper functionality.

isSameSite(): bool

Is the request coming from the same (sub) domain and is initiated by clicking on a link?

isAjax(): bool

Is it an AJAX request?

getRemoteAddress(): ?string

Returns the user's IP address. You may need to [set up a proxy|configuring#HTTP proxy] for proper functionality.

getRemoteHost(): ?string

Returns DNS translation of the user's IP address. You may need to [set up a proxy|configuring#HTTP proxy] for proper functionality.

getRawBody(): ?string

Returns the body of the HTTP request:

$body = $httpRequest->getRawBody();

detectLanguage(array $langs): ?string

Detects language. As a parameter $lang, we pass an array of languages ​​that the application supports, and it returns the one preferred by browser. It is not magic, the method just uses the Accept-Language header. If no match is reached, it returns null.

// Header sent by browser: Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3

$langs = ['hu', 'pl', 'en']; // languages supported in application
echo $httpRequest->detectLanguage($langs); // en

RequestFactory

The object of the current HTTP request is created by Nette\Http\RequestFactory. If you are writing an application that does not use a DI container, you create a request as follows:

$factory = new Nette\Http\RequestFactory;
$httpRequest = $factory->fromGlobals();

RequestFactory can be configured before calling fromGlobals(). We can disable all sanitization of input parameters from invalid UTF-8 sequences using $factory->setBinary(). And also set up a proxy server, which is important for the correct detection of the user's IP address using $factory->setProxy(...).

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:

// remove spaces from path
$requestFactory->urlFilters['path']['%20'] = '';

// remove dot, comma or right parenthesis form the end of the URL
$requestFactory->urlFilters['url']['[.,)]$'] = '';

// clean the path from duplicated slashes (default filter)
$requestFactory->urlFilters['path']['/{2,}'] = '/';

HTTP Response

An HTTP response is an Nette\Http\Response object. Unlike the Request, the object is mutable, so you can use setters to change the state, ie to send headers. Remember that all setters must be called before any actual output is sent. The isSent() method tells if output have been sent. If it returns true, each attempt to send a header throws an Nette\InvalidStateException exception.

setCode(int $code, string $reason = null)

Changes a status response code. For better source code readability it is recommended to use predefined constants instead of actual numbers.

$httpResponse->setCode(Nette\Http\Response::S404_NotFound);

getCode(): int

Returns the status code of the response.

isSent(): bool

Returns whether headers have already been sent from the server to the browser, so it is no longer possible to send headers or change the status code.

setHeader(string $name, string $value)

Sends an HTTP header and overwrites previously sent header of the same name.

$httpResponse->setHeader('Pragma', 'no-cache');

addHeader(string $name, string $value)

Sends an HTTP header and doesn't overwrite previously sent header of the same name.

$httpResponse->addHeader('Accept', 'application/json');
$httpResponse->addHeader('Accept', 'application/xml');

deleteHeader(string $name)

Deletes a previously sent HTTP header.

getHeader(string $header): ?string

Returns the sent HTTP header, or null if it does not exist. The parameter is case-insensitive.

$pragma = $httpResponse->getHeader('Pragma');

getHeaders(): array

Returns all sent HTTP headers as associative array.

$headers = $httpResponse->getHeaders();
echo $headers['Pragma'];

setContentType(string $type, string $charset = null)

Sends the header Content-Type.

$httpResponse->setContentType('text/plain', 'UTF-8');

redirect(string $url, int $code = self::S302_FOUND): void

Redirects to another URL. Don't forget to quit the script then.

$httpResponse->redirect('http://example.com');
exit;

setExpiration(?string $time)

Sets the expiration of the HTTP document using the Cache-Control and Expires headers. The parameter is either a time interval (as text) or null, which disables caching.

// browser cache expires in one hour
$httpResponse->setExpiration('1 hour');

setCookie(string $name, string $value, $time, string $path = null, string $domain = null, bool $secure = null, bool $httpOnly = null, string $sameSite = null)

Sends a cookie. The default values ​​of the parameters are:

  • $path with scope to all directories ('/')
  • $domain with scope of the current (sub)domain, but not its subdomains
  • $secure defaults to false
  • $httpOnly is true, so the cookie is inaccessible to JavaScript
  • $sameSite is null, so the flag is not specified

The time can be specified as a string or the number of seconds.

$httpResponse->setCookie('lang', 'en', '100 days');

deleteCookie(string $name, string $path = null, string $domain = null, bool $secure = null): void

Deletes a cookie. The default values ​​of the parameters are:

  • $path with scope to all directories ('/')
  • $domain with scope of the current (sub)domain, but not its subdomains
  • $secure defaults to false
$httpResponse->deleteCookie('lang');

Uploaded Files

Method Nette\Http\Request::getFiles() return a tree of upload files in a normalized structure, with each leaf an instance of Nette\Http\FileUpload. These objects encapsulate the data submitted by the <input type=file> form element.

The structure reflects the naming of elements in HTML. In the simplest example, this might be a single named form element submitted as:

<input type="file" name="avatar">

In this case, the $request->getFiles() returns array:

[
	'avatar' => /* FileUpload instance */
]

The FileUpload object is created even if the user did not upload any file or the upload failed. Method hasFile() returns true if a file has been sent:

$request->getFile('avatar')->hasFile();

In the case of an input using array notation for the name:

<input type="file" name="my-form[details][avatar]">

returned tree ends up looking like this:

[
	'my-form' => [
		'details' => [
			'avatar' => /* FileUpload instance */
		],
	],
]

You can also create arrays of files:

<input type="file" name="my-form[details][avatars][] multiple">

In such a case structure looks like:

[
	'my-form' => [
		'details' => [
			'avatars' => [
				0 => /* FileUpload instance */,
				1 => /* FileUpload instance */,
				2 => /* FileUpload instance */,
			],
		],
	],
]

The best way to access index 1 of a nested array is as follows:

$file = Nette\Utils\Arrays::get(
	$request->getFiles(),
	['my-form', 'details', 'avatars', 1],
	null
);
if ($file instanceof FileUpload) {
	...
}

Because you can't trust data from the outside and therefore don't rely on the form of the file structure, it's safer to use the Arrays::get() than the $request->getFiles()['my-form']['details']['avatars'][1], which may fail.

Overview of FileUpload Methods .{toc: FileUpload}

hasFile(): bool

Returns true if the user has uploaded a file.

isOk(): bool

Returns true if the file was uploaded successfully.

getError(): int

Returns the error code associated with the uploaded file. It is be one of UPLOAD_ERR_XXX constants. If the file was uploaded successfully, it returns UPLOAD_ERR_OK.

move(string $dest)

Moves an uploaded file to a new location. If the destination file already exists, it will be overwritten.

$file->move('/path/to/files/name.ext');

getContents(): ?string

Returns the contents of the uploaded file. If the upload was not successful, it returns null.

getContentType(): ?string

Detects the MIME content type of the uploaded file based on its signature. If the upload was not successful or the detection failed, it returns null.

Requires PHP extension fileinfo.

getName(): string

Returns the original file name as submitted by the browser.

Do not trust the value returned by this method. A client could send a malicious filename with the intention to corrupt or hack your application.

getSanitizedName(): string

Returns the sanitized file name. It contains only ASCII characters [a-zA-Z0-9.-]. If the name does not contain such characters, it returns 'unknown'. If the file is JPEG, PNG, GIF, or WebP image, it returns the correct file extension.

getSize(): int

Returns the size of the uploaded file. If the upload was not successful, it returns 0.

getTemporaryFile(): string

Returns the path of the temporary location of the uploaded file. If the upload was not successful, it returns ''.

isImage(): bool

Returns true if the uploaded file is a JPEG, PNG, GIF, or WebP image. Detection is based on its signature. The integrity of the entire file is not checked. You can find out if an image is not corrupted for example by trying to load it.

Requires PHP extension fileinfo.

getImageSize(): ?array

Returns a pair of [width, height] with dimensions of the uploaded image. If the upload was not successful or is not a valid image, it returns null.

toImage(): Nette\Utils\Image

Loads an image as an Image object. If the upload was not successful or is not a valid image, it throws an Nette\Utils\ImageException exception.

Sessions

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

The session is managed by the Nette\Http\Session object.

Starting Session

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

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

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

Section

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

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

We get the section from the session manager:

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

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

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

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

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

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

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

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

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

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

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

$section->warnOnUndefined = true;

How to Set Expiration

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

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

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

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

Session Management

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

start(): void

Starts a session.

isStarted(): bool

Is the session started?

close(): void

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

destroy(): void

Ends and deletes the session.

exists(): bool

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

regenerateId(): void

Generates a new random session ID. Data remain unchanged.

getId(): string

Returns the session ID.

Configuration

Methods must be called before starting a session.

setName(string $name): static

Changes the session name. It is possible to run several different sessions at the same time within one website, each under a different name.

getName(): string

Returns the session name.

setOptions(array $options): static

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

setExpiration(?string $time): static

Sets the time of inactivity after which the session expires.

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

Sets parameters for cookies.

setSavePath(string $path): static

Sets the directory where session files are stored.

setHandler(\SessionHandlerInterface $handler): static

Sets custom handler, see PHP documentation.

Safety First

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

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

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

Url

The Nette\Http\Url class makes it easy to work with the URL and its individual components, which are outlined in this diagram:

 scheme  user  password  host   port    path        query  fragment
   |      |      |        |      |       |            |       |
 /--\   /--\ /------\ /-------\ /--\/----------\ /--------\ /----\
 http://john:xyz%[email protected]:8080/en/download?name=param#footer
 \______\__________________________/
     |               |
  hostUrl        authority

URL generation is intuitive:

use Nette\Http\Url;

$url = new Url;
$url->setScheme('https')
	->setHost('localhost')
	->setPath('/edit')
	->setQueryParameter('foo', 'bar');

echo $url; // 'https://localhost/edit?foo=bar'

You can also parse the URL and then manipulate it:

$url = new Url(
	'http://john:xyz%[email protected]:8080/en/download?name=param#footer'
);

The following methods are available to get or change individual URL components:

Setter Getter Returned value
setScheme(string $scheme) getScheme(): string 'http'
setUser(string $user) getUser(): string 'john'
setPassword(string $password) getPassword(): string 'xyz*12'
setHost(string $host) getHost(): string 'nette.org'
setPort(int $port) getPort(): ?int 8080
setPath(string $path) getPath(): string '/en/download'
setQuery(string|array $query) getQuery(): string 'name=param'
setFragment(string $fragment) getFragment(): string 'footer'
-- getAuthority(): string 'nette.org:8080'
-- getHostUrl(): string 'http://nette.org:8080'
-- getAbsoluteUrl(): string full URL

We can also operate with individual query parameters using:

Setter Getter
setQuery(string|array $query) getQueryParameters(): array
setQueryParameter(string $name, $val) getQueryParameter(string $name)

Method getDomain(int $level = 2) returns the right or left part of the host. This is how it works if the host is www.nette.org:

Usage Result
getDomain(1) 'org'
getDomain(2) 'nette.org'
getDomain(3) 'www.nette.org'
getDomain(0) 'www.nette.org'
getDomain(-1) 'www.nette'
getDomain(-2) 'www'
getDomain(-3) ''

The Url class implements the JsonSerializable interface and has a __toString() method so that the object can be printed or used in data passed to json_encode().

echo $url;
echo json_encode([$url]);

Method isEqual(string|Url $anotherUrl): bool tests whether the two URLs are identical.

$url->isEqual('https://nette.org');

UrlImmutable

The class Nette\Http\UrlImmutable is an immutable alternative to class Url (just as in PHP DateTimeImmutable is immutable alternative to DateTime). Instead of setters, it has so-called withers, which do not change the object, but return new instances with a modified value:

use Nette\Http\UrlImmutable;

$url = new UrlImmutable(
	'http://john:xyz%[email protected]:8080/en/download?name=param#footer'
);

$newUrl = $url
	->withUser('')
	->withPassword('')
	->withPath('/cs/');

echo $newUrl; // 'http://nette.org:8080/cs/?name=param#footer'

The following methods are available to get or change individual URL components:

Wither Getter Returned value
withScheme(string $scheme) getScheme(): string 'http'
withUser(string $user) getUser(): string 'john'
withPassword(string $password) getPassword(): string 'xyz*12'
withHost(string $host) getHost(): string 'nette.org'
withPort(int $port) getPort(): ?int 8080
withPath(string $path) getPath(): string '/en/download'
withQuery(string|array $query) getQuery(): string 'name=param'
withFragment(string $fragment) getFragment(): string 'footer'
-- getAuthority(): string 'nette.org:8080'
-- getHostUrl(): string 'http://nette.org:8080'
-- getAbsoluteUrl(): string full URL

We can also operate with individual query parameters using:

Wither Getter
withQuery(string|array $query) getQueryParameters(): array
-- getQueryParameter(string $name)

The getDomain(int $level = 2) method works the same as the method in Url. Method withoutUserInfo() removes user and password.

The UrlImmutable class implements the JsonSerializable interface and has a __toString() method so that the object can be printed or used in data passed to json_encode().

echo $url;
echo json_encode([$url]);

Method isEqual(string|Url $anotherUrl): bool tests whether the two URLs are identical.

If you like Nette, please make a donation now. Thank you!

http's People

Contributors

adaamz avatar dg avatar enumag avatar fabik avatar fprochazka avatar holantomas avatar hrach avatar hranicka avatar integer avatar jakubboucek avatar janbarasek avatar jantvrdik avatar kravco avatar lexaurin avatar mabar avatar majkl578 avatar mikulas avatar milo avatar misaon avatar ondrejmirtes avatar patrickkusebauch avatar pavelbier avatar paveljurasek avatar pavelkovar avatar peldax avatar radeksimko avatar stekycz avatar tomasvotruba avatar vrana avatar vrtak-cz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

http's Issues

$requestFactory->addUrlFilter doesn't work

Nette 2.4

This code from docs generates the exception: Call to undefined method Nette\Http\RequestFactory::addUrlFilter():

$requestFactory = new Nette\Http\RequestFactory;
$requestFactory->addUrlFilter('%20', '', PHP_URL_PATH);

RequestFactory: Possible remoteAddr spoofing

Load balancers or proxies adds client IP to the end of X-Forwarded-For header.

So if site is behind trusted proxy, we set it by setProxy method and client sends spoofed X-Forwarded-For header then existing RequestFactory code interprets it as real client IP. Because proxy adds his real IP to the end but RequestFactory code gets the first IP from $_SERVER["HTTP_X_FORWARDED_FOR"] array.

Correct solution should be that we check $_SERVER["HTTP_X_FORWARDED_FOR"] array from the end compare to known trusted proxy array (set by setProxy) and use endmost IP that doesn't match any of know proxy IPs.

OK Example:
Site is behind 2 consecutive load balancers: 10.0.0.1 and 10.0.0.2. Clients real IP is 192.168.1.1.
From client there is no X-Forwarded-For header. First proxy set X-Forwarded-For to 192.168.1.1. Second proxy appends IP of first proxy - header will be X-Forwarded-For: 192.168.1.1, 10.0.0.1.
In this case, everything would be alright - we take first IP and it equals to real client IP, but...

Fake IP Example:
Situation as same as previous example but client sends spoofed X-Forwarded-For header. It sends e.g. 172.16.0.1 in that header.
First proxy appends his real IP, second proxy appends IP of first proxy. We have X-Forwarded-For: 172.16.0.1, 192.168.1.1, 10.0.0.1
So RequestFactory uses fake IP as reference.

Session::configure overrides setHandler settings

Imagine this scenario:

$session->setOptions(array('save_handler' => 'redis');
$session->setHandler(new RedisStorageExtension);
$session->start(); //overrides my custom handler with native 'redis'

Nette/Session overrides custom handler on Session::start();

In my situation I cannot leave save_handler with default files value because my handler extends Redis handler, not the default one.
Setting handler after session start could help but session start is lazy and we don't know when it happens.

Edit: Documentation about session handler extensions http://php.net/manual/en/class.sessionhandler.php

SessionSection notice when unlimited session lifetime is set

//in unlimited session
Session::setOptions(['gc_maxlifetime' => '0']);

//set lifetime for section
$section = $session->getSection('foo');
$section->setExpiration(123);

Nette triggers notice The expiration time is greater than the session expiration 0 seconds

SessionSection can't set expiration when browser close.

I it look like as chrome bug, because chrome remeber $_COOKIE[nette-browser].

My chrome Linux 43.0.2357.125 (64-bit).

I try this on sanbox.
Add to presenter:

public function handleSection() {
    $this->session->getSection('h4kuna')->value = 'ok';
    $this->redirect('this');
}


public function renderDefault() {
    $section = $this->session->getSection('h4kuna');
    $section->setExpiration(0);
    $this->template->section = $section->value ? : 'noValue';
}

add to latte:

<a n:href="section!">fill section</a>
<br>
value: {$section} <br>
cookie nette-browser: {ifset $_COOKIE['nette-browser']}{$_COOKIE['nette-browser']}{else}ok does not exists{/ifset}

And use case is:

  • run page, you will see value: noValue
  • click to anchor (change session value)
  • close browser
  • open browser and it is remebered value, i expect noValue

Can anyone confirm?

Firefox and Opera works fine.

Cli - Deault domain/host - for generating routes

Current Nette\Http\RequestFactory doesnt support adding default domain for the request

Problems starts when we need to generate links, after executing the script in CLI
eg.:

    MyCliPresenter->generateLinksAction() {
        echo $this->link('Homepage:default');
    }
    // outputs:  http:/// 

Whole problem could be solved by adding support for default values for domain and adding a bit of code for using them inside RequestFactory::createHttpRequest() method

        ...
        // after line 94 add
        // use default host, if we didnt get any from $SERVER.
        if (!$url->host)
        {
            $url->host = $this->domain;
        }

this->domain should be populated by setter function, with DI in setup

Another sollution is to use change RequestFactory to be easily extendebale, currently if we want to override createHttpRequest(), with own implementation, it fail when when the script tries to write to $this->binary as there is private access on the variable.
For easier extendebility createHttpRequest should be split to smaller pieces. eg. process Query, Hostnamem, Scheme, Binary, etc.

Bellow are 2 current solutions for the problem,

  1. create own HttpRequest and add it as service instead of one generated by factory
  2. change service for httpRequestFactory and HttpRequest for own classes.

bootstrap.php:

    ...
    /**
     * @param \Nette\Configurator $configurator
     * @param \Nette\DI\Compiler $compiler
     */
    $configurator->onCompile[] = function($configurator, $compiler)
    {
        $compiler->addExtension('cli_http', new CliModule\Http\CliHttpRequestExtension());
    };
    ...

CliHttpRequestExtension.php

    namespace CliModule\Http;

    use Nette\DI\CompilerExtension;
    use Nette\DI\Container;
    use Nette\DI\ContainerBuilder;
    use Nette\DI\ServiceDefinition;
    use Nette\Http\Request;
    use Nette\Http\UrlScript;
    use Nette\Utils\Strings;

    class CliHttpRequestExtension extends CompilerExtension
    {
        public function loadConfiguration()
        {
            $container = $this->getContainerBuilder();
            $config    = $this->getConfig();


            /** @var ContainerBuilder $container */
            if ($container->parameters['consoleMode']) {
                // ** variant 1 - create own Request object
                $url = new UrlScript();
                $url->setHost($container->parameters['domain']);
                $url->setScheme('http');

                $arguments = [
                    'url'           => $url,
                    'query'         => NULL,
                    'post'          => NULL,
                    'files'         => NULL,
                    'cookies'       => NULL,
                    'headers'       => NULL,
                    'method'        => NULL,
                    'remoteAddress' => NULL,
                    'remoteHost'    => NULL
                ];

                $container->removeDefinition('httpRequest');
                $container->addDefinition('httpRequest')->setClass('\Nette\Http\Request', $arguments);

                // ** varianta 2- use own RequestFactory **
    //            $container->removeDefinition('httpRequestFactory');
    //            $container->addDefinition('httpRequestFactory')
    //                ->setClass('CliModule\Http\CliRequestFactory')
    //                ->addSetup('setDomain', array($container->parameters['domain']));
    //
    //            $container->getDefinition('httpRequest')
    //                ->setFactory('@CliModule\\CliRequestFactory::createHttpRequest');
    //
            }
        }

    }

CliRequestFactory.php - direct copy of Request factory except added part after line 94

     ...
     public function createHttpRequest()
    {
        // DETECTS URI, base path and script path of the request.
        $url = new UrlScript;
        $url->scheme = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http';
        $url->user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : '';
        $url->password = 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->host = strtolower($pair[1]);
            if (isset($pair[2])) {
                $url->port = (int) substr($pair[2], 1);
            } elseif (isset($_SERVER['SERVER_PORT'])) {
                $url->port = (int) $_SERVER['SERVER_PORT'];
            }
        }

        // Default vyplnenie Host-u
        if (!$url->host)
        {
            $url->host = $this->domain;
        }
        ...

Security: allow removing characters above U+FFFF in RequestFactory

MySQL's utf8 encoding does not support characters above U+FFFF. Using utf8 encoding and not removing characters above U+FFFF can be used to bypass input validation. You can for example use this to bypass minimum length requirement for thread title on Nette forum. See excellent presentation Hacking with Unicode for more practical examples.

Applications must either use utf8mb4 encoding (which supports full UTF-8) or remove all characters above U+FFFF. I think that Nette should support both approaches.

We should certainly allow removing characters above U+FFFF in RequestFactory and either make it default or change default encoding in Nette\Database to utf8mb4.


Note: utf8mb4 encoding is available since MySQL 5.5.3 (2010-03-24)

Restarting session after ID regeneration looses session

  • bug report? yes
  • feature request? no
  • version: 2.4.6

Description

I found problem with restarting session after session ID was regenerated.
This is because \Nette\Http\Session::start() takes session ID always from request cookie. Invocation of \Nette\Http\Session::regenerateId() changes session ID. Closing session and starting session again changes session ID to value which is stored in cookie, but this session ID is already deleted.

Steps To Reproduce

$session = new Session();
$session->start(); // Session ID is taken from cookie.
$session->regenerateId(); // Session ID is changed.
$session->close(); // Session with old ID is deleted, session with new ID is written.
$session->start(); // Session ID is taken from cookie - PROBLEM 
// it is old session ID, not the regenerated one and all session data are lost

https + nginx proxy

If there is https nginx proxy, our webhoster sets SERVER_PORT to 80 for the apache webserver. But the application runs over 443 and https. See their response and dumped headers.

Server headers dump:

HTTPS on
HTTP_HOST www.xxx.cz
HTTP_X_FORWARDED_HOST www.xxx.cz
HTTP_X_FORWARDED_SERVER www.xxx.cz
HTTP_X_FORWARDED_FOR 94.230.146.241
HTTP_FORWARDED_REQUEST_URI /test.php
HTTP_HTTP_X_FORWARDED_PROTO https
HTTP_HTTPS on
HTTP_X_FORWARDED_PROTO https
HTTP_X_FORWARDED_SSL on
SERVER_SIGNATURE no value
SERVER_SOFTWARE Apache
SERVER_NAME www.xxx.cz
SERVER_ADDR 127.0.0.1
SERVER_PORT 80

Response from the webhosting

The HTTPS header is properly set by our front end Nginx web server, as well as the 'X-Forwarded-SSL' header.

The problem here is that the $_SERVER['SERVER_PORT'] variable is always 80, since the Apache (sitting behind the Nginx) never receives HTTPS traffic, rather the front end Nginx does. You are relying on that variable to construct the HttpRequest object in

/home/xxx/webapps/xxx/vendor/nette/nette/NetteHttpRequestFactory.php on line 80:

if (isset($pair[2])) {
$url->port = (int) substr($pair[2], 1);
} elseif (isset($_SERVER['SERVER_PORT'])) {
$url->port = (int) $_SERVER['SERVER_PORT'];
}

...

WebFaction Support

HTTP header Content-Type without charset in PHP 5.6

When you want to set header Content-Type without charset attribute for type text/* then PHP 5.6 adds default charset into the header for you. However it could bring some problems in some cases (back compatibility for example). It is caused by usage of function sapi_apply_default_charset.

Working solution follows

$default_charset = ini_get('default_charset');
ini_set('default_charset', NULL);
// set header
ini_set('default_charset', $default_charset);

I am not sure if it should be solved by Nette. However Nette supports passing charset as second parameter to method IResponse::setContentType so it could. What do you think?

RequestFactory: remove or improve binary mode

Current implementation of binary mode (RequestFactory::setBinary()) is stupid and encourages insecure behavior, because I usually want to transfer only a single parameter in binary. To achieve that user should not disable UTF-8 validation on all input parameters.

We should either

  • remove the binary mode entirely or
  • improve it to support binary mode only for parameter with certain name.

Currently I'm in favor of the first option for the following reasons:

  • transporting binary data is rare
  • users can very easily just use $_GET['binaryData'] or $_POST['binaryData'] – its ugly but practical
  • users can implement wrapper around RequstFactory which would allow specifying that certain parameters should be treated as binary.

Thoughts? cc @dg, @fprochazka


Note: If we choose the remove the binary mode entirely with the vision that users may implement custom wrapper around RequestFactory we may no longer throw exception (see #30 for related discussion) for invalid parameters.

Restart of session doesn't work

I need to close session before long-runing operation, because unclosed session blocks other processes.

$session->start();
//working with session..
$session->close();

//long-running operation

$session->start();
//working with session..
$session->close();

It returns "headers already sent..". Cookie should not be sends again, if I call start() more times, or not?
Sorry for my English.

UrlScript is not standalone - it brokes expected getBasePath behavioral

I cannot use class Nette\HttpUrlScript "smoothly", as standalone (its needed for me for extending of Nette\Http\Request class).

For example:

$url = new \Nette\Http\UrlScript("http://nette.org/admin/script.php/pathinfo/?name=param#fragment");
echo $url->basePath; // expected '/admin/', but output is '/'

My suggestions:

You can specify in the constructor the minimum default functionality, suitable for most cases. My simple example:

class UrlScript extends Url
{
    ...

    public function __construct($url = NULL)
    {
        parent::__construct($url);
        $this->scriptPath =  $this->path;       
    }

    ...
}

With this patch any descendant class of UrlScript (if its instance was created standalone, not from RequestFactory), will have same behavioral as the class Url (in method getBasePath, getRelativeUrl and other).

\Nette\Http\Url dots in query parameters

Version: latest

Bug Description

In query parameters dots are replaces to underscode.
The bug is from internal PHP function parse_str, where . are replaced to _.

Steps To Reproduce

$url = new \Nette\Http\Url('test.xy?test.text=text');
return (string) $url; //returns: 'test.xy?test_text=text'

Expected Behavior

should return: test.xy?test.text=text

rawBody empty for PUT method

If nette called via PUT method, rawBody stay empty. If I try to read from php://input file_get_contents('php://input') after Nette\Http\RequestFactory::createHttpRequest() (in own router) there is right content.

Is it ok behavior?

Request API vs IRequest interface

Request implements a couple of extra methods that are not defined in IRequest interface. Nette 3 deprecated autowiring of Request type, only autowiring via interface is now supported. This means that those extra methods are basically "inaccessible" for any consumer. I think those methods should be either added to the interface, or deprecated and eventually removed.

  • getReferer(): ?Url - the interface already mentions this method in @method annotation, the annotation should be transformed to a proper method definition.
  • isSameSite(): bool - this should be definitely added to the interface.
  • detectLanguage(array $langs): ?string - this can be easily extracted to a separate service and removed from Request.

What are your thoughts? Shall I send a PR(s)?

Related to #137, #90

PHP 7.2 - deprecated "each" calls

  • bug report? yes
  • feature request? no
  • version: 2.4.2

Running security test suite

-- FAILED: SecurityExtension | tests/Security.DI/SecurityExtension.user.phpt
   Exited with error code 255 (expected 0)
   E_DEPRECATED: The each() function is deprecated. This message will be suppressed on further calls

Helpers::ipMatch not working correctly

Hi, after some testing, i figured out, that ipMatch function is not working correctly.

Example:
Network 192.168.1.0/24 (IPs: 192.168.1.0-192.168.1.255)
ipMatch(192.168.1.1, 192.168.1.0/24) -> true, great
ipMatch(192.168.2.1, 192.168.1.0/24) -> true, what isn't nice.

Even if tests are passing:

Assert::true( Helpers::ipMatch('192.168.68.233', '192.168.68.233') ); 

woks fine

Assert::false( Helpers::ipMatch('192.168.68.234', '192.168.68.233') );

works fine

Assert::true( Helpers::ipMatch('192.168.64.0', '192.168.68.233/12') );

Network 192.168.68.233/12 (IPs: 192.160.0.1-192.175.255.255)
fine, it's in subnet

Assert::false( Helpers::ipMatch('192.168.63.255', '192.168.68.233/12') );

not fine, even in subnet, but returns false

Assert::true( Helpers::ipMatch('192.168.79.254', '192.168.68.233/12') );

fine, in subnet

Assert::false( Helpers::ipMatch('192.168.80.0', '192.168.68.233/12') );

in subnet, but returns false

Assert::true( Helpers::ipMatch('127.0.0.1', '192.168.68.233/32') );

not sure, if this should return true

Maybe I missunderstood something, but this is how Mikrotik and Cisco devices are interpreting CIDR network.

Maybe, the logic of function is working backwards...

real expected addresses
/0 /32 1
/1 /31 2
/2 /30 4
/3 /29 8
... ... ...
/12 /20 4096

If I use 192.168.68.233/20 (192.168.64.0-192.168.79.255), it matches expected behaviour as seen in tests..

Not possible to delete a cookie with the same name from multiple domains

  • bug report? yes
  • feature request? no
  • version: 2.4.5, the code in master looks the same

Description

Due to Response::setCookie() calling Helpers::removeDuplicateCookies(), it is not possible to delete a cookie with the same name from multiple domains.

Came upon it today when I found out there may be accidental cookies for subdomains in our project instead of one cookie for our root domain. Tried to delete all the cookies in the subdomains using Response, but failed.

A naive solution would be to put the Helpers::removeDuplicateCookies() call in a condition, which could be controlled by a parameter of Response::setCookie(), the same for Response::deleteCookie(), but these methods can be called from anywhere and even one call w/o the flag to not call the Helpers::removeDuplicateCookies() would break it.
Having this setting as a settable class property would probably work.
Actually I don't know why the Helpers::removeDuplicateCookies() is there, so I cannot really tell if it would make sense.

Steps To Reproduce

$response->deleteCookie('lang', null, 'sub.domain.com');
$response->deleteCookie('lang', null, 'domain.com');

Validation rule Form::PATTERN for Nette\Http\FileUpload

  • bug report? yes
  • feature request? no
  • version: 2.4

Description

Hi,
i have use case

$form->addUpload('csv')
    ->addRule($form::MIME_TYPE, 'Foo', 'text/*')
    // next line does not work
    ->addRule($form::PATTERN, 'Message', '*.csv');

I found in Validator::validatePattern where first parameter is (IControl)->getValue(). It's ok. If i look at (FileUploadControl)->getValue() you get FileUpload and method __toString return tmpName. Where is problem? I don't need run validation on temporary name, but on original name.

For first validation check extension of file. Please you don't write, this valid is not relevant. I can't use PATTERN validation for this moment.

Or throw exception, this rule is not relevant for FileUpload. Because here is wtf behavior.

I understant if you change return value in method __toString(), it can be BC break or anything worse.

getReferer method missing in IRequest

  • bug report? yes
  • feature request? no
  • version: 2.4.7

Hi, when i write $this->getHttpRequest()->getReferer() i encountered a problem when the IDE PhpStorm did not know about the getReferer() method. The reason is that the method is missing in IRequest. I want to ask if it is deliberate or a bug. It would be good either to add or add an appropriate annotation to the IDE to understand that the getReferer method is accessible.
...

snimek obrazovky 2018-03-22 v 12 44 39

Port is not used in links/redirects

Hi,
when I run nette application via standalone server (php -S localhost:8000) there is no port in links and redirects. E.g. there is link with localhost/somepage instead of localhost:8000/somepage.

I am running app on standalone server because of selenium tests and I have workaround with this in test's bootstrap:

Nette\Http\Url::$defaultPorts['http'] = 8000;

It works, but I think port should be used when creating links.

(It is on version 2.2.0)

Consider using session lazy write for PHP 5.6+

PHP 5.6 should land with a lazy write feature (RFC), configured using session_start parameter.
Basically the only new things are read only sessions (proposal 1) and lazy session writes (proposal 2). Read only sessions are not suitable for Nette. Lazy writes are more interesting as those write session changes only if anything was really changed which results in better performation.

Problem with Line 148

Nette\Http\RequestFactory

If the 148 line includes:
foreach ($list as &$v)

dump files:
array (1)
files => array (1)
| 0 => NULL


BUT
If the 148 line includes:
while (list(, $v) = each($list))

dump files:
array (1)
files => array (1)
| 0 => Nette\Http\FileUpload #ee05


Where is problem? Thank you.

Session and PHP startup errors

I'm trying to implement ajax upload today. However there is a problem with error handling. If I try to upload a too large file I get this error:

Possible problem: you are starting session while already having some data in output buffer. This may not work if the outputted data grows. Try starting the session earlier.

Session is started in Container::initialialize() of course. So I looked into the buffer and it contained this:

<b>Warning</b>: POST Content-Length of 360495939 bytes exceeds the limit of 31457280 bytes in <b>Unknown</b> on line <b>0</b><br />

This apparently is a PHP starup error. Everything works fine if I set the directive diplay_startup_errors to 0. But of course I don't want that - at least not in development.

Is there some other way to deal with this? If I'm not mistaken this pretty much means that Nette\Http\Session is incompatible with the directive diplay_startup_errors.

RequestFactory, REQUEST_URI and absolute URLs

Recently I installed this addon to Google Chrome:

https://chrome.google.com/webstore/detail/host-switch-plus/bopepoejgapmihklfepohbilpkcdoaeo

its purpose is something like etc/hosts file with one single exception. If used, it sends HTTP requests like:

GET http://www.example.com/ HTTP/1.1

instead of

GET / HTTP/1.1

which seems to be valid according to https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html

But Nette shows "no routes" and gives me 404.

Because RequestFactory.php where $_SEVER['REQUEST_URI'] is parsed cannot handle absolute URLs. I quick-fixed this simply by modifying urlFilters from:

    /** @var array */
    public $urlFilters = array(
        'path' => array('#/{2,}#' => '/'), // '%20' => ''
        'url' => array(), // '#[.,)]\z#' => ''
    );

to:

    /** @var array */
    public $urlFilters = array(
        'path' => array('#/{2,}#' => '/'), // '%20' => ''
        'url' => array('#^(http|ftp)s?://[^/]+/#' => '/'), // '#[.,)]\z#' => ''
    );

and it works, but I'm not sure if this is the best way to "fix" this.

Session's cookie domain (and possibly other attributes) is not handled (set) properly in some cases

  • Bug report? yes
  • Feature request? no
  • Version: discovered in v2.3.8 (still present in v2.4.4)

Description

If Nette\Http\Session instance is configured using camelCase-styled option names, eg. cookiePath, cookieDomain, etc. (as it is presented in docs: https://doc.nette.org/cs/2.3/sessions). and the client does this:

  1. (new request)
  2. Set up the Session instance
    • Either with $session->setOptions(["cookieDomain" => ".domain.com"])
    • Or in Nette framework using .neon config
  3. Start the session (do not output anything yet)
  4. Close the session (do not output anything yet)
  5. Start the session again (we should able to, we did not send any output yet)

The Nette\Http\Response's setCookie() method is being fired twice (and that's ok). But: the second call does NOT send the cookie's domain property properly - it is empty (as in PHP's default).

If Nette\Http\Session instance is configured using underscore_case-styled option names (eg. cookie_path, cookie_domain), everything works fine.

Steps To Reproduce

Minimum reproduction code: sessions_bug.zip

Expected behaviour

"Correct" (workaround-y) options:

$optionsOK = [
	"cookie_path" => "/",
	"cookie_domain" => ".domainok.com",
];

Expected output:

$ php index.php
Array
(
    [name] => PHPSESSID
    [value] => 3aeue4lsjhrhqs8de8ao8ilbp0
    [time] => 0
    [path] => /
    [domain] => .domainok.com
    [secure] =>
    [httpOnly] => 1
)

Array
(
    [name] => PHPSESSID
    [value] => fgas2ttl3eku0ns6eth8c39r97
    [time] => 0
    [path] => /
    [domain] => .domainok.com
    [secure] =>
    [httpOnly] => 1
)

Actual behaviour:

Options:

$optionsBug = [
	"cookiePath" => "/",
	"cookieDomain" => ".domainbug.com",
];

Output:

$ php index.php
Array
(
    [name] => PHPSESSID
    [value] => n441tegtvgc2u87j8jkmkvt4o1
    [time] => 0
    [path] => /
    [domain] => .domainbug.com
    [secure] =>
    [httpOnly] => 1
)

Array
(
    [name] => PHPSESSID
    [value] => 3k8ercr7pe42ln86l9kn2t6836
    [time] => 0
    [path] => /
    [domain] =>
    [secure] =>
    [httpOnly] => 1
)

PHP Deprecated: Function (null)() is deprecated

Forum: http://forum.nette.org/en/21268-nette-framework-2-2-4-released#p145829

This bug shows self in Nette 2.2.4 as:

PHP Notice: Undefined variable: file in .../src/Http/Response.php:299
PHP Notice: Undefined variable: line in .../src/Http/Response.php:299
PHP Deprecated: Function æĄly°Š() is deprecated in Unknown:0
PHP Deprecated: Function (null)::H‰l$čL‰d$šH‰ĶL‰l$ųH‰\$ąH�ģ8H…ÉA‰üI‰õt!H‹ŪźM() is deprecated in .../cache/latte/xxxx-f58ecdceb29baf29d552a02bd25a2224.php:72
PHP Deprecated: Function (null)() is deprecated in ....
Parse Error
Fatal Error Allowed memory size of 134217728 bytes exhausted (tried to allocate 909587003 bytes)

This bug occures on mine website when I migrated to Nette v2.2.4. It is hard to reproduce, I did it by refreshing page and one from 10-50 requests failed by different error message.

I replaced nette/nette: 2.2.4 in composer by all single packages and I started to downgrade packages one by one. After reverting nette/http to v2.2.1, bugs stopped to showself. After that I bisected commits and I found 0914fe9 as buggy. After that, I upgraded back to nette/nette v2.2.4 and starts to reverting single commit changes. When this was reverted, everithing is OK.

I know it sounds like nonsence. And when PHP bug is about memory corruption this is maybe only final crash. Let's wait for forum feedback.

PHP 5.4.16 as a apache2handler, Debian

Nette\Http\FileUpload PHP notice at method move()

Hi, I have just noticed in my app, that there appears PHP Notice in class Nette\Http\FileUpload on line 160. It is because there is missing use clause on previous line. Sadly I am not familiar with contributing to Nette, so I cannot send PR right now.

I am using Nette 2.4 (Nette\Http is according composer.json also version 2.4)

Starting session in CLI mode

My functional tests are currently failing because of

  [PHPUnit_Framework_Exception] Possible problem: you are starting session while already having some data in output buffer. This may not work if the outputted data grows. Try starting the session earlier.

Setting autoStart to true didn't help either because it's ignored in CLI mode. Why is it ignored and how should I fix my tests then?

https://travis-ci.org/Arachne/Forms/builds/125263850

Tried it on localhost as well with the same result.

Redirects can be used for XSS

If the attacker finds a hole, where he can provide an URL that is passed directly to Http\Response::redirect(), then he's able to create XSS.

for example

/some/page?redirect=%0Ajavascript:alert(/xss/)

Which executes to

public function actionPage($redirect)
{
    $this->redirectUri($redirect);
}

The browser won't redirect, because it's not a valid url, but HTML will be printed

Location: %0Ajavascript:alert(/xss/)


<h1>Redirect</h1>

<p><a href="
javascript:alert(/xss/)">Please click here to continue</a>.</p>

This happened to us in our mailing click-through counter.


Also relevant http://forum.nette.org/cs/21315-parsovani-absolutni-url-routerem-aplikace#p146150

Rename Url::canonicalize() to normalize()

I think that the term canonical URL is usually used in SEO context. However low-level Url class can not know which form is the SEO canonical one. Therefore I suggest to rename canonicalize() to less misleading normalize(). Do you feel the same?

public hosting && url rewrite in .htaccess files && $_SERVER['SCRIPT_NAME'] problems

- possible bug report

- nette version: v: 2.4 (20170221), php version: 7.0.8, apache version: ? 2.4.23 ?

Description

Routing works fine on localhost.
Routing is broken after copying app to public web hosting (wedos.cz).
Some other people are reporting similar problems on wedos user forum.
I traced the problem to file /http/src/http/RequestFactory.php, line 95.
$_SERVER['SCRIPT_NAME'] contains invalid value on public web hosting (it contained filesystem path instead of url) and so was not shortened, which then propagated to urlScript.php, Routing.php, etc..., so all routing was broken and no match was found for any processed requests.
After discovering that, it was easy to google problem description (https://www.google.com/search?q=htaccess+SCRIPT_NAME+problem). And workaround solution.
Problem disappeared after inserting line $path = $_SERVER["REQUEST_URI"]; between lines 100 and 101. Application now seems to be working correcly on both localhost and public hosting. I have not installed command line interpreter or composer, so I cannot run unit tests and check that something else is not broken. Only have downloaded sandbox release and running it on xampp.

Nice framework guys, particularly the error prettifier (I didn't get much further yet). I can't understand how you managed to make such nice thing in php. I hate php. Here are some of the reasons: https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/ :-)

Steps To Reproduce

Not sure.
I'm new to php, nette and apache, so it took me several days to find out the cause of this problem. Mainly because i was suspecting rules in multiple url-rewriting .htaccess files and was meddling with them. Also it is happening only on public hosting and I cannot check or replicate its configuration.

zdravím

Add Session::abort() / Session::rollback()

  • bug report? no
  • feature request? yes

PHP 5.6 introduced new function session_abort. Implementation is trivial, we only need to decide whether to call the method abort (matches the original function name) or rollback (imho more descriptive).

UserStorage implements IUserStorage which is not required

  • bug report? yes
  • feature request? no
  • version: 2.4

Description

Not sure if this is really a bug.

I use nette/http standalone and when I want to instantiate UserStorage it ends with Fatal Error: Interface 'Nette\Security\IUserStorage' not found.

There is no dependency on nette/security in composer.json. But this is only class, that needs this dependency. So maybe it can be enough to add "nette/security": "Allows use UserStorage" to "suggest" section to composer?

Steps To Reproduce

  1. create empty project
  2. require nette/http
  3. $userStorage = new UserStorage()

DI: Nette\Http\Request subclass is required instead of interface

When we want to you own service for http request, subclass of Nette\Http\Request is required

// contex is instance of Nette\DI\Container
$context->removeService("http.request");
// OwnRequest implements Nette\Http\IRequest
$context->addService("http.request", new OwnRequest());

it throws exception

Nette\InvalidArgumentException: Service 'http.request' must be instance of Nette\Http\Request, OwnRequest given

I think same problem is with all services from Nette\Http package. Only interface can be requested, not class.

Maybe problem is with DI container itself, when removeService is called, I wan't to remove all information about particular service, it seems that in DI container still remains some information about removed service, what isn't desirable.

Http POST with Content-type: application/json

Request post attribute is currently filled with data from $_POST array. Would it be possible to set it like this
Nette\Utils\Json::decode(file_get_contents('php://input'), true); when method is POST and Content-type is application/json?

Session in memcached: Nette\InvalidStateException Unable to clear session lock record

This is in my config.neon

session:
    saveHandler: memcached
    savePath: 'localhost:22121'

and I repetedly get this problem...

memcached config is ok, and this test code:

$m = new Memcached();
$m->addServer('localhost', 22121);

$m->set('int', 99);

sets key correctly


Dumping memcache contents
  Number of buckets: 1
  Number of items  : 1
Dumping bucket 1 - 1 total items
add int 1 1479504298 2
99

I'm using php 7.0.12
and memcached 1.4.15

also basic php code:

ini_set('session.save_handler', 'memcached');
ini_set('session.save_path', 'localhost:11211');
session_start();
$_SESSION['BLA'] = 'heeey';

saved key to memcached without any issue and it's possible to read it

also if I set config directly to php.ini it works and session is correctly saved to memcached, but on other request session is lost (I think session id got changed, session data is in memcached)

Is UserStorage::setExpiration with BROWSER_CLOSED working?

Hi,
we're using Nette\Security\User::setExpiration with $whenBrowserIsClosed = TRUE, that calls Nette\Security\UserStorage::setExpiration with flag BROWSER_CLOSED, but I think, that this functionality was removed in pullrequest #103 and then Nette\Security\User::setExpiration function with parametr $whenBrowserIsClosed and flag BROWSER_CLOSED for Nette\Security\UserStorage::setExpiration is unusable and should be removed too.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.