GithubHelp home page GithubHelp logo

dflydev-fig-cookies's Introduction

FIG Cookies

Managing Cookies for PSR-7 Requests and Responses.

Latest Stable Version Total Downloads Latest Unstable Version License
Build Status Scrutinizer Code Quality Code Coverage Code Climate
Join the chat at https://gitter.im/dflydev/dflydev-fig-cookies

Installation

$> composer require dflydev/fig-cookies

Concepts

FIG Cookies tackles two problems, managing Cookie Request headers and managing Set-Cookie Response headers. It does this by way of introducing a Cookies class to manage collections of Cookie instances and a SetCookies class to manage collections of SetCookie instances.

Instantiating these collections looks like this:

// Get a collection representing the cookies in the Cookie headers
// of a PSR-7 Request.
$cookies = Dflydev\FigCookies\Cookies::fromRequest($request);

// Get a collection representing the cookies in the Set-Cookie headers
// of a PSR-7 Response
$setCookies = Dflydev\FigCookies\SetCookies::fromResponse($response);

After modifying these collections in some way, they are rendered into a PSR-7 Request or PSR-7 Response like this:

// Render the Cookie headers and add them to the headers of a
// PSR-7 Request.
$request = $cookies->renderIntoCookieHeader($request);

// Render the Set-Cookie headers and add them to the headers of a
// PSR-7 Response.
$response = $setCookies->renderIntoSetCookieHeader($response);

Like PSR-7 Messages, Cookie, Cookies, SetCookie, and SetCookies are all represented as immutable value objects and all mutators will return new instances of the original with the requested changes.

While this style of design has many benefits it can become fairly verbose very quickly. In order to get around that, FIG Cookies provides two facades in an attempt to help simplify things and make the whole process less verbose.

Basic Usage

The easiest way to start working with FIG Cookies is by using the FigRequestCookies and FigResponseCookies classes. They are facades to the primitive FIG Cookies classes. Their jobs are to make common cookie related tasks easier and less verbose than working with the primitive classes directly.

There is overhead on creating Cookies and SetCookies and rebuilding requests and responses. Each of the FigCookies methods will go through this process so be wary of using too many of these calls in the same section of code. In some cases it may be better to work with the primitive FIG Cookies classes directly rather than using the facades.

Request Cookies

Requests include cookie information in the Cookie request header. The cookies in this header are represented by the Cookie class.

use Dflydev\FigCookies\Cookie;

$cookie = Cookie::create('theme', 'blue');

To easily work with request cookies, use the FigRequestCookies facade.

Get a Request Cookie

The get method will return a Cookie instance. If no cookie by the specified name exists, the returned Cookie instance will have a null value.

The optional third parameter to get sets the value that should be used if a cookie does not exist.

use Dflydev\FigCookies\FigRequestCookies;

$cookie = FigRequestCookies::get($request, 'theme');
$cookie = FigRequestCookies::get($request, 'theme', 'default-theme');

Set a Request Cookie

The set method will either add a cookie or replace an existing cookie.

The Cookie primitive is used as the second argument.

use Dflydev\FigCookies\FigRequestCookies;

$request = FigRequestCookies::set($request, Cookie::create('theme', 'blue'));

Modify a Request Cookie

The modify method allows for replacing the contents of a cookie based on the current cookie with the specified name. The third argument is a callable that takes a Cookie instance as its first argument and is expected to return a Cookie instance.

If no cookie by the specified name exists, a new Cookie instance with a null value will be passed to the callable.

use Dflydev\FigCookies\FigRequestCookies;

$modify = function (Cookie $cookie) {
    $value = $cookie->getValue();

    // ... inspect current $value and determine if $value should
    // change or if it can stay the same. in all cases, a cookie
    // should be returned from this callback...

    return $cookie->withValue($value);
}

$request = FigRequestCookies::modify($request, 'theme', $modify);

Remove a Request Cookie

The remove method removes a cookie from the request headers if it exists.

use Dflydev\FigCookies\FigRequestCookies;

$request = FigRequestCookies::remove($request, 'theme');

Note that this does not cause the client to remove the cookie. Take a look at the SetCookie class' expire() method to do that.

Response Cookies

Responses include cookie information in the Set-Cookie response header. The cookies in these headers are represented by the SetCookie class.

use Dflydev\FigCookies\Modifier\SameSite;
use Dflydev\FigCookies\SetCookie;

$setCookie = SetCookie::create('lu')
    ->withValue('Rg3vHJZnehYLjVg7qi3bZjzg')
    ->withExpires('Tue, 15-Jan-2013 21:47:38 GMT')
    ->withMaxAge(500)
    ->rememberForever()
    ->withPath('/')
    ->withDomain('.example.com')
    ->withSecure(true)
    ->withHttpOnly(true)
    ->withSameSite(SameSite::lax())
;

To easily work with response cookies, use the FigResponseCookies facade.

Get a Response Cookie

The get method will return a SetCookie instance. If no cookie by the specified name exists, the returned SetCookie instance will have a null value.

The optional third parameter to get sets the value that should be used if a cookie does not exist.

use Dflydev\FigCookies\FigResponseCookies;

$setCookie = FigResponseCookies::get($response, 'theme');
$setCookie = FigResponseCookies::get($response, 'theme', 'simple');

Set a Response Cookie

The set method will either add a cookie or replace an existing cookie.

The SetCookie primitive is used as the second argument.

use Dflydev\FigCookies\FigResponseCookies;

$response = FigResponseCookies::set($response, SetCookie::create('token')
    ->withValue('a9s87dfz978a9')
    ->withDomain('example.com')
    ->withPath('/firewall')
);

Modify a Response Cookie

The modify method allows for replacing the contents of a cookie based on the current cookie with the specified name. The third argument is a callable that takes a SetCookie instance as its first argument and is expected to return a SetCookie instance.

If no cookie by the specified name exists, a new SetCookie instance with a null value will be passed to the callable.

use Dflydev\FigCookies\FigResponseCookies;

$modify = function (SetCookie $setCookie) {
    $value = $setCookie->getValue();

    // ... inspect current $value and determine if $value should
    // change or if it can stay the same. in all cases, a cookie
    // should be returned from this callback...

    return $setCookie
        ->withValue($newValue)
        ->withExpires($newExpires)
    ;
}

$response = FigResponseCookies::modify($response, 'theme', $modify);

Remove a Response Cookie

The remove method removes a cookie from the response if it exists.

use Dflydev\FigCookies\FigResponseCookies;

$response = FigResponseCookies::remove($response, 'theme');

Expire a Response Cookie

The expire method sets a cookie with an expiry date in the far past. This causes the client to remove the cookie.

Note that in order to expire a cookie, you need to configure its Set-Cookie header just like when you initially wrote the cookie (i.e. same domain/path). The easiest way to do this is to re-use the same code for configuring the header when setting as well as expiring the cookie:

use Dflydev\FigCookies\FigResponseCookies;
use Dflydev\FigCookies\SetCookie;

$setCookie = SetCookie::create('ba')
    ->withValue('UQdfdafpJJ23k111m')
    ->withPath('/')
    ->withDomain('.example.com')
;

FigResponseCookies::set($response, $setCookie->expire());

FAQ

Do you call setcookies?

No.

Delivery of the rendered SetCookie instances is the responsibility of the PSR-7 client implementation.

Do you do anything with sessions?

No.

It would be possible to build session handling using cookies on top of FIG Cookies but it is out of scope for this package.

Do you read from $_COOKIES?

No.

FIG Cookies only pays attention to the Cookie headers on PSR-7 Request instances. In the case of ServerRequestInterface instances, PSR-7 implementations should be including $_COOKIES values in the headers so in that case FIG Cookies may be interacting with $_COOKIES indirectly.

License

MIT, see LICENSE.

Community

Want to get involved? Here are a few ways:

  • Find us in the #dflydev IRC channel on irc.freenode.org.
  • Mention @dflydev on Twitter.
  • Join the chat at https://gitter.im/dflydev/dflydev-fig-cookies

dflydev-fig-cookies's People

Contributors

canvural avatar franzliedke avatar gsteel avatar harikt avatar mbakker96 avatar mrdys avatar ocramius avatar pavlakis avatar proton-ab avatar schnittstabil avatar settermjd avatar simensen avatar weierophinney 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

dflydev-fig-cookies's Issues

can not set request cookie

Hello Simense,
I just set in homepage procedure using mezzio framework like this:

FigRequestCookies::set($request, Cookie::create('theme', 'blue'));

the page loading normally but there is no cookie in headers tab.
please advice.
Thank you,
-alfin-

Usage with Slim 3

Hi,
would be greate to have some tutorials or links on how to use this in combination with Slim 3. Their upgrade guide refers to this archive, but I can't find a single tutorial on how to properly implement it with Slim.
Cheers and thanks, Steff

Not work with Slim 3.5

Maybe I'm doing something wrong. Set the Cookie according to the instructions. If you check the Cookie's value to the line below - it turns out that the cookie is set. If you refresh the page then the cookie is null. Could You give a short working manual for Slim?..

Question on Request and Response Cookies

Hi,

I cannot really understand why the only cookie you would want to get would not be just a request cookie, and the only cookie to set, be a response cookie. Is there a reason for this allowing of injecting cookies into a request, and retrieving cookies before they are sent with a response? If so, could you highlight the benefit of getting a cookie from a response before it is sent, or the benefit or use-case for injecting a cookie into a request?

It's an amazing lib, produces incredibly readable code compared with PHP native cookie functions, so I get that it improves code from that perspective; and I found it because @silentworks mentioned this as working with Slim3; which has, or is working towards full PSR7 compliant request response.

Thanks for your time, and for writing the lib, and TIA for any explanation around the injecting to a request, and reading from a response before it has been dispatched.

TypeError on urldecode() with malformed cookie

There is an issue in splitCookiePair where malformed pair is given without =, the result is a call on urldecode with first parameter as NULL.

Example code:

<?php

declare(strict_types=1);

namespace Dflydev\FigCookies;

use function array_filter;
use function assert;
use function explode;
use function is_array;
use function preg_split;
use function urldecode;

class StringUtil
{
    /** @return string[] */
    public static function splitOnAttributeDelimiter(string $string) : array
    {
        $splitAttributes = preg_split('@\s*[;]\s*@', $string);

        assert(is_array($splitAttributes));

        return array_filter($splitAttributes);
    }

    /** @return string[] */
    public static function splitCookiePair(string $string) : array
    {
        $pairParts    = explode('=', $string, 2);
        $pairParts[1] = urldecode($pairParts[1]) ?? '';

        return $pairParts;
    }
}

var_dump(StringUtil::splitCookiePair('cookie=value'));
var_dump(StringUtil::splitCookiePair('cookie='));
var_dump(StringUtil::splitCookiePair('cookie')); // urldecode() expects parameter 1 to be string, null given

While the cookie string is malformed if = is missing before ;, the code should definitely not fail catastrophically in this case.

Why i'm cannot remove (expire) cookie, if use withPath method?..

If i set withPath method, i cannot remove or modify cookie any way.

This code sets a cookie that I can delete:
$setCookie = SetCookie::create('auth')->withValue('1'); $response = FigResponseCookies::set($response, $setCookie);

but this code set cookie that i cannot remove (expire) and modify in Safari and Chrome (other browsers not verified)
$setCookie = SetCookie::create('auth')->withValue('1')->withPath('/'); $response = FigResponseCookies::set($response, $setCookie);

What could it be?.. And how can I fix it?..

Problems with urlencode

I'm writing a proxy script where I have a middleware to alter the domain param of cookies.

The problem is that the urlencode and urldecode calls are altering the cookie name and value sent by the upstream server (which is out of my control).

Maybe an option could be added to work with raw data. What do you think?

Unable to set expiry time...

The expires/max age for the cookie is

2022-02-25T03:18:25.063Z

$setCookie = SetCookie::create('lu2')
			->withValue('aaaa')
			->withExpires(new \DateTime('+5 years'))
			->withMaxAge(500)
			->rememberForever()
			->withPath('/')
			->withDomain('192.168.1.13')
			->withSecure(false)
			->withHttpOnly(true)
			->withSameSite(SameSite::lax());
		echo $setCookie;
		$response = FigResponseCookies::set($response, $setCookie);
		return $response->withStatus(201)
			->withHeader("Content-Type", "application/json")
			->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));

Making this more easily useable

I really like this implementation, but I feel when using something like a Pimple container, it is a little complex to use.

What would make this really user friendly would be if FigRequestCookies and FigResponseCookies had an all method each that did the same as Cookies::fromRequest and Cookies::fromResponse

What do you think?

PHP8 Support

Hello,

One of the libraries I depend on has a dependency on this package. So while trying to upgrade to PHP8 I saw this package is not compatible.

Is there any plan to add support for PHP 8? If not, I can do it. Would you be interested merging that?

As far as I see it's relatively easy upgrade. Only problem I see is tests using prophesize which is deprecated in PHPUnit 9. https://github.com/phpspec/prophecy-phpunit is recommended but that requires min. PHP 7.3 So I don't know any what to do in that case, but open to discussion.

SetCookie::expire does not work: missing Domain part

SetCookie::expire() does not work for response cookies because browsers expect a "Domain" part in the Set-Cookie header when expiring a cookie.

I have tested it with Firefox 49.0.1 and Chrome 53.0.2785.143 and both behave in the same (standard?) way: the minimum requirement for expiring and flushing a cookie is to have the cookie name, the "Expires" part in the past AND also a "Domain" part in the Set-Cookie header.

I could not really make a patch for this, since in response cookies no domain information is present. Any suggestion how to fix it in a canonical way?

`FigRequestCookies::get()` not using `ServerRequesInterface::getCookieParams()` ?

Hello,

I'm trying to understand how FigRequestCookies::get() works.
By looking at the code, I noticed that it is not using ServerRequesInterface::getCookieParams() .

Is there anyone who could explain that?

Basically, what are the differences between:

$cookie = FigRequestCookies::get($request, 'foo');

vs

$cookie = $request->getCookieParams()['foo'];

Thank you.

3.0.0 missing from Changelog

The CHANGELOG.md file only lists version 2.0.0 and no later ones.

It especially does not list any breaking changes in version 3.0.0, so it is impossible for projects to see what they need to do when switching to version 3.

Fig Cookies - Modifying cookie value (JSON array)

I want to be able to store an array of ID's to their cookies. I figured the easiest way is to json_encode the array of save IDs. Code below:

$app->get('/save/{id: [0-9]+}', function($request, $response, $args) {

  // Check if cookie exists
  $cookie = FigRequestCookies::get($request, 'saves');

  if($cookie->getValue() !== "null") {

    // Convert old cookie JSON array to PHP array
    $saves = json_decode($cookie->getValue(), true);

    // Push new save ID onto array
    array_push($saves, $args['id']);

    // Modify cookie
    $response = FigResponseCookies::modify($response, 'saves', function(SetCookie $setCookie) use ($saves)  {
      return $setCookie->withValue(json_encode($saves));
    });
  }
  else {

    // No cookie found set one
    $setCookie = SetCookie::create('saves')
      ->withValue(json_encode([ $args['id'] ]))
      ->rememberForever()
      ->withPath('/')
      ->withDomain('.localhost');

    $response = FigResponseCookies::set($response, $setCookie);
  }

  // Return user to save area
  return $this->view->render($response, 'saves.php');
});

My problem is the array only ever gets two values regardless of how many times I save a page. If I visit the following pages ...

http://example.com/save/22
http://example.com/save/32
http://example.com/save/18

... the array of $saves is only ever set to:

[22, 18]

If I save another page:

http://example.com/save/9

... the array is:

[22, 9]

Not sure what's going on here.

Can SetCookie's constructor take more arguments?

As far as I understand, SetCookie::__construct only takes name and value. If I want to create a cookie with expire, domain, path, etc. I have to chain a bunch of ->withDomain, ->withPath, and so on.

Would it make sense to have SetCookie accept everything (domain, path, expires, etc.) in the constructor as optional arguments, much like PHP's native setcookie method? It would make instantiation much less verbose, and also avoid cloning and throwing away objects.

If it makes sense I can work on a PR for this.

url encoding

Currently the library uses urlencode/urldecode. Could you change it to use rawurlencode/rawurldecode so it encodes spaces as %20 and not +? Since 7.4.3 PHP uses %20 and it would be more consistent with it's setcookie function and $_COOKIE doesn't decode the +. I need to set a cookie that is compatible with that.

facing isssue

I'm facing issue, my code is below

namespace Cookies;
use Dflydev\FigCookies\FigResponseCookies;
use Dflydev\FigCookies\FigRequestCookies;
use Dflydev\FigCookies\Cookie;
use Dflydev\FigCookies\SetCookie;

class Cookies

{
    protected $req;
    protected $res;

    public function __construct($req, $res)
    {
        $this->req = $req;
        $this->res = $res;
    }

    function get($name)
    {
        $response = $this->res;
        $request = $this->req;
        return FigRequestCookies::get($request, 'theme');
    }

    function set($name, $value = "", $expire = 0)
    {
        $response = $this->res;
        $request = $this->req;
        $request = FigRequestCookies::set($request, Cookie::create('theme', 'blue'));
    }
}

When I call get cookies, this returns theme= I'm going to die, if this will not solve

I've try set with response too, but same

Slim 3 Middleware returns null for cookies

Hi, i am using Slim 3 and i have a small issue here, the problem is when i use a middleware to retrieve a specific cookie, it returns an empty cookie but when i use the same method and class with a controller, it retrieves the same cookie without issue. I am using it to retrieve JWT package cookie, i am using a shared class that i inject into the container, which means the code refusing to work when called by a middleware is the same that works when called by a controller.

$jwtPackage = FigRequestCookies::get($request, $authenticationTokenCookieName)->getValue();

Please note i have not injected the dflydev-fig-cookies library into slim 3 containers and am also new to slim. Thanks.

Max-Age=0 doesn't removes a cookie on a client

When setting max-age parameter to zero (0), the cookie is set as session cookie instead of removal. The package treats zero value of max-age as it's not set. But according to RFC-6265 zero value should remove cookie as well as negative value. Here's a quote of the section "5.2.2. The Max-Age Attribute":

If delta-seconds is less than or equal to zero (0), let expiry-time be the earliest representable date and time. Otherwise, let the expiry-time be the current date and time plus delta-seconds seconds.

The workaround of this bug is to use negative values. I can make pull-request if needed.

FigRequestCookies::get doesn't respect Path?

I create two cookies with the same name but for different path.
one for "/" and one for "/admin".
When I read out the cookie in the "/" path or "/admin" I always get the same.
When I use the PHP $_COOKIE variable I get the correct cookie for the correct path,.

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.