hansott/psr7-cookies
Add and manage HTTP cookies on PSR-7 responses with a simple SetCookie helper. Create custom cookies, delete cookies, set long-lived cookies, or set cookies that expire at a specific time, then attach them to any Psr\Http\Message\ResponseInterface.
Installation:
composer require hansott/psr7-cookies
Add to composer.json if not auto-loaded:
"autoload": {
"psr-4": {
"App\\": "app/",
"Hansott\\Psr7Cookies\\": "vendor/hansott/psr7-cookies/src/"
}
}
Run composer dump-autoload.
First Use Case: Create a PSR-7 request with cookies:
use Hansott\Psr7Cookies\Cookie;
use Psr\Http\Message\RequestInterface;
use Nyholm\Psr7\Factory\Psr17Factory;
$factory = new Psr17Factory();
$request = $factory->createRequest('GET', '/');
// Add a cookie
$cookie = new Cookie('user_id', '123', [
'expires' => time() + 3600,
'path' => '/',
'domain' => 'example.com',
'secure' => true,
'httponly' => true,
]);
$request = $cookie->addToRequest($request);
// Send the request (e.g., via Guzzle or Laravel HTTP client)
Where to Look First:
src/Cookie.php for core functionality.tests/ for usage examples and edge cases.Cookie Creation and Attachment:
// Create a cookie
$cookie = new Cookie('theme', 'dark', [
'max_age' => 30 * 24 * 60 * 60, // 30 days
'path' => '/',
]);
// Attach to a PSR-7 request
$request = $cookie->addToRequest($request);
// Attach to a PSR-7 response (for Set-Cookie headers)
$response = $cookie->addToResponse($response);
Reading Cookies from Requests:
use Hansott\Psr7Cookies\CookieJar;
$jar = new CookieJar();
$cookies = $jar->extractFromRequest($request);
// Access a specific cookie
$userId = $cookies->get('user_id')?->getValue();
Integration with Laravel:
use Hansott\Psr7Cookies\CookieJar;
use Psr\Http\Message\ResponseInterface;
public function handle($request, Closure $next): ResponseInterface
{
$jar = new CookieJar();
$cookies = $jar->extractFromRequest($request);
// Modify cookies or add new ones
$response = $next($request);
if ($cookies->has('temp_token')) {
$response = $cookies->get('temp_token')->removeFromResponse($response);
}
return $response;
}
public function register()
{
$this->app->singleton(CookieJar::class, function ($app) {
return new CookieJar();
});
}
Cookie Manipulation:
$cookie = $cookies->get('user_id');
$cookie->setValue('456');
$response = $cookie->addToResponse($response);
$cookie = $cookies->get('session_id');
$cookie->setExpires(time() - 1); // Expire in the past
$response = $cookie->addToResponse($response);
Batch Operations:
$jar = new CookieJar();
$jar->addCookie(new Cookie('cookie1', 'value1'));
$jar->addCookie(new Cookie('cookie2', 'value2'));
$request = $jar->addToRequest($request);
PSR-7 Compliance:
$request->withHeader()) is safe, but avoid in-place changes.// ❌ Avoid this (mutates the object)
$request->headers->set('Cookie', '...');
// ✅ Correct (returns a new object)
$request = $request->withHeader('Cookie', '...');
Cookie Expiration:
expires and max_age are mutually exclusive. Using both may lead to unexpected behavior. Prefer max_age for simplicity:
// ✅ Use max_age
$cookie = new Cookie('token', 'abc', ['max_age' => 3600]);
// ❌ Avoid mixing expires and max_age
$cookie = new Cookie('token', 'abc', [
'expires' => time() + 3600,
'max_age' => 3600,
]);
Domain and Path Scope:
domain or path attributes must match the request URL exactly. For example:
path=/admin will not be sent to /dashboard.domain=example.com will not be sent to sub.example.com unless domain=.example.com is used.Secure and HttpOnly Flags:
secure cookies are only sent over HTTPS. Test locally with HTTPS or disable the flag during development:
$cookie = new Cookie('token', 'abc', [
'secure' => false, // Disable for local testing
'httponly' => true,
]);
httponly cookies are inaccessible via JavaScript, which can cause issues if you need client-side access (e.g., for analytics).Cookie Size Limits:
Inspect Cookies in Requests/Responses:
var_dump() or dd() to inspect headers:
dd($request->getHeader('Cookie'));
dd($response->getHeader('Set-Cookie'));
dd($request->cookies->all()) (if using Laravel's cookie system in parallel).Logging Cookie Operations:
$jar = new CookieJar();
$jar->addCookie($cookie);
\Log::debug('Added cookie', ['cookie' => $cookie->toString()]);
Testing Cookie Behavior:
public function testCookieAttachment()
{
$request = $this->createRequest();
$cookie = new Cookie('test', 'value');
$requestWithCookie = $cookie->addToRequest($request);
$this->assertEquals(
['test=value'],
$requestWithCookie->getHeader('Cookie')
);
}
Browser Developer Tools:
Custom Cookie Attributes:
Cookie class to support additional attributes (e.g., same_site for CSRF protection):
class CustomCookie extends Cookie
{
public function __construct(string $name, string $value, array $attributes = [])
{
$attributes['same_site'] = $attributes['same_site'] ?? 'Lax';
parent::__construct($name, $value, $attributes);
}
public function getSameSite(): string
{
return $this->attributes['same_site'] ?? 'Lax';
}
}
Cookie Serialization:
toString() to customize cookie output (e.g., for non-standard formats):
class CustomCookie extends Cookie
{
public function toString(): string
{
return sprintf(
'%s=%s; %s',
$this->name,
$this->value,
implode('; ', $this->getAttributesAsString())
);
}
}
Integration with Laravel:
use Illuminate\Support\Facades\Facade;
class CookieFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'psr7.cookies';
}
}
CookieJar in a service provider:
$this->app->bind('psr7.cookies', function () {
How can I help you explore Laravel packages today?