Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I implemented a straightforward cookie collection mechanism #66

Closed
wants to merge 11 commits into from
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,46 @@ $cookie = (new \Yiisoft\Cookies\Cookie('cookieName'))
->withRawValue('ebaKUq90PhiHck_MR7st-E1SxhbYWiTsLo82mCTbNuAh7rgflx5LVsYfJJseyQCrODuVcJkTSYhm1WKte-l5lQ==')
```

Work with cookie request collection from any place in you project. Add in config you app in middleware block
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Work with cookie request collection from any place in you project. Add in config you app in middleware block
### Request cookies collection
You can work with cookie request collection from any place in you project. Add in config you app in middleware block

```php
'middlewares' => [
\Yiisoft\Cookies\RequestCookies\RequestCookiesCollectorMiddleware::class,
...
],
```

Initial provider in di-web config
```php
return [
...,
\Yiisoft\Cookies\RequestCookies\RequestCookiesProviderInterface::class => [
'class' => Yiisoft\Cookies\RequestCookies\RequestCookiesProvider::class,
],

```

Use as dependency in any part of ur code

```php

use Yiisoft\Cookies\RequestCookies\RequestCookiesProviderInterface;

final class MyService
{
public function __construct(
private RequestCookiesProviderInterface $requestCookieProvider
)
{
}

public function doIt()
{
$cookieCollection = $this->requestCookieProvider->get();
// ...
}
}
```
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
```

## Documentation

- [Internals](docs/internals.md)
Expand Down
9 changes: 9 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"roave/infection-static-analysis-plugin": "^1.16",
"spatie/phpunit-watcher": "^1.23",
"vimeo/psalm": "^4.30|^5.21",
"yiisoft/di": "1.1",
"yiisoft/test-support": "^1.3"
},
"autoload": {
Expand All @@ -58,6 +59,14 @@
"Yiisoft\\Cookies\\Tests\\": "tests"
}
},
"extra": {
"config-plugin-options": {
"source-directory": "config"
},
"config-plugin": {
"di-web": "di-web.php"
}
},
"config": {
"sort-packages": true,
"allow-plugins": {
Expand Down
14 changes: 14 additions & 0 deletions config/di-web.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

/** @var array $params */

use Yiisoft\Cookies\RequestCookies\RequestCookiesProvider;
use Yiisoft\Cookies\RequestCookies\RequestCookiesProviderInterface;

return [
RequestCookiesProviderInterface::class => [
'class' => RequestCookiesProvider::class,
],
Comment on lines +11 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RequestCookiesProviderInterface::class => [
'class' => RequestCookiesProvider::class,
],
RequestCookiesProviderInterface::class => RequestCookiesProvider::class,

];
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Cookies\RequestCookies\Exception;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
namespace Yiisoft\Cookies\RequestCookies\Exception;
namespace Yiisoft\Cookies\RequestCookies;


use LogicException;
use Throwable;

/**
* Thrown when Request cookie collect isn't set before.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Thrown when Request cookie collect isn't set before.
* Thrown when request cookies isn't set before.

*/
final class RequestCookieCollectionNotSetException extends LogicException
{
public function __construct(int $code = 0, ?Throwable $previous = null)
{
parent::__construct('Request cookie collect is not set.', $code, $previous);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parent::__construct('Request cookie collect is not set.', $code, $previous);
parent::__construct('Request cookies is not set.', $code, $previous);

}
}
58 changes: 58 additions & 0 deletions src/RequestCookies/RequestCookies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Cookies\RequestCookies;

/**
* A RequestCookies helps to work with many cookies at once and to read request cookies.
*/
final class RequestCookies
{
/**
* @var array The cookies in this collection (indexed by the cookie name).
*
* @psalm-var array<string, string>
*/
private array $cookies = [];

/**
* RequestCookies constructor.
*
* @param array<string, string> $cookies The cookies that this collection initially contains.
*/
public function __construct(array $cookies = [])
{
foreach ($cookies as $name => $value) {
$this->cookies[$name] = $value;
}
}

/**
* Returns the cookie with the specified name.
*
* @param string $name The cookie name.
*
* @return ?string The cookie with the specified name. Null if the named cookie does not exist.
*
* @see getValue()
Comment on lines +37 to +38
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*
* @see getValue()

*/
public function get(string $name): ?string
{
return $this->cookies[$name] ?? null;
}

/**
* Returns whether there is a cookie with the specified name.
*
* @param string $name The cookie name.
*
* @return bool Whether the named cookie exists.
*
* @see remove()
Comment on lines +51 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
*
* @see remove()

*/
public function has(string $name): bool
{
return isset($this->cookies[$name]);
}
}
47 changes: 47 additions & 0 deletions src/RequestCookies/RequestCookiesCollectorMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Cookies\RequestCookies;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

/**
* Stores cookies request {@see RequestCookiesProviderInterface}.
* You need to add this into your application middleware stack.
*/
final class RequestCookiesCollectorMiddleware implements MiddlewareInterface
{
private RequestCookiesProviderInterface $cookieProvider;

public function __construct(RequestCookiesProviderInterface $cookieProvider)
{
$this->cookieProvider = $cookieProvider;
}

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$cookies = $this->collectCookies($request);
$this->cookieProvider->set(new RequestCookies($cookies));
Comment on lines +27 to +28
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$cookies = $this->collectCookies($request);
$this->cookieProvider->set(new RequestCookies($cookies));
$this->cookieProvider->set(new RequestCookies($request->getCookieParams()));

Seems, getCookieParams always contains array<string,string>, isn't he?

return $handler->handle($request);
}

/**
* @param ServerRequestInterface $request
* @return array<string, string>
*/
private function collectCookies(ServerRequestInterface $request): array
{
$collection = [];
foreach ($request->getCookieParams() as $name => $value) {
if (!is_string($name) || !is_string($value)) {
continue;
}
$collection[$name] = $value;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I access expires, domain and other parameters later?

}
return $collection;
}
}
32 changes: 32 additions & 0 deletions src/RequestCookies/RequestCookiesProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Cookies\RequestCookies;

use Yiisoft\Cookies\RequestCookies\Exception\RequestCookieCollectionNotSetException;

/**
* Stores request for further consumption by attribute handlers.
*/
final class RequestCookiesProvider implements RequestCookiesProviderInterface
{
/**
* @var RequestCookies|null The collection.
*/
private ?RequestCookies $cookieCollection = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private ?RequestCookies $cookieCollection = null;
private ?RequestCookies $cookies = null;


public function set(RequestCookies $cookieCollection): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have an empty collection in this class and merge two collections instead.
Moreover it can be used in two ways:
The first is as you did
The second is the similar that error-handler has: set parameters in the app and add them back to request in a post-middleware later. Wdyt?

{
$this->cookieCollection = $cookieCollection;
}

public function get(): RequestCookies
{
if ($this->cookieCollection === null) {
throw new RequestCookieCollectionNotSetException();
}

return $this->cookieCollection;
}
}
27 changes: 27 additions & 0 deletions src/RequestCookies/RequestCookiesProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Cookies\RequestCookies;

use Yiisoft\Cookies\RequestCookies\Exception\RequestCookieCollectionNotSetException;

/**
* Provides a way to set the current cookie collection and then get it when needed.
*/
interface RequestCookiesProviderInterface
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CookiesProvider sounds better than RequestCookiesProvider

{
/**
* Set the current cookie request collection.
*
* @param RequestCookies $cookieCollection The collection to set.
*/
public function set(RequestCookies $cookieCollection): void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function set(RequestCookies $cookieCollection): void;
public function set(RequestCookies $cookies): void;


/**
* Get the current request cookie collection.
*
* @throws RequestCookieCollectionNotSetException
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd return an empty collection instead

*/
public function get(): RequestCookies;
}
38 changes: 38 additions & 0 deletions tests/ConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Cookies\Tests;

use PHPUnit\Framework\TestCase;
use Yiisoft\Cookies\RequestCookies\RequestCookiesProvider;
use Yiisoft\Cookies\RequestCookies\RequestCookiesProviderInterface;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;

use function dirname;

final class ConfigTest extends TestCase
{
public function testDi(): void
{
$container = $this->createContainer();

$this->assertInstanceOf(
RequestCookiesProvider::class,
$container->get(RequestCookiesProviderInterface::class)
);
}

private function createContainer(): Container
{
return new Container(
ContainerConfig::create()->withDefinitions($this->getContainerDefinitions())
);
}

private function getContainerDefinitions(): array
{
return require dirname(__DIR__) . '/config/di-web.php';
}
}
Loading
Loading