How to destroy session in symfony 2.6? - symfony

I tried doing this:
$session->remove();
Also this:
$session->clear();
But it gives following error:
Call to a member function clear() on a non-object

invalidate()
Clears all session data and regenerates session ID. Do not use session_destroy().
This actually does what you want.
Source & more info:
http://symfony.com/doc/current/components/http_foundation/sessions.html
Also such things are easy to check by simply review the source.
For this case you should check Session or SessionInterface source:
http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Session/SessionInterface.html
http://api.symfony.com/2.6/Symfony/Component/HttpFoundation/Session/Session.html
Edit.
Of course this method belongs to Session class so you have to access Session object first in your controller.
So we go to:
http://symfony.com/doc/current/book/controller.html#managing-the-session
and we see how to do that:
public function indexAction(Request $request)
{
$session = $request->getSession();
$session->invalidate(); //here we can now clear the session.
}

Related

Api-Platform: using PUT for creating resources

I would like to use the PUT method for creating resources. They are identified by an UUID, and since it is possible to create UUIDs on the client side, I would like to enable the following behaviour:
on PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 if this resource exists, update it
on PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 if this resource does not exist, create it
It's possible to achieve this by implementing an ItemDataProviderInterface / RestrictedDataProviderInterface.
However, my resource is actually a subresource, so let's say I want to create a new Book which references an existing Author.
My constructor looks like this:
/**
* Book constructor
*/
public function __construct(Author $author, string $uuid) {
$this->author = $author;
$this->id = $uuid;
}
But I don't know how to access the Author entity (provided in the request body) from my BookItemProvider.
Any ideas?
In API Platform many things that should occur on item creation is based on the kind of request it is. It would be complicated to change.
Here are 2 possibilities to make what you want.
First, you may consider to do a custom route and use your own logic. If you do it you will probably be happy to know that using the option _api_resource_class on your custom route will enable some listeners of APIPlaform and avoid you some work.
The second solution, if you need global behavior for example, is to override API Platform. Your main problem for this is the ReadListener of ApiPlatform that will throw an exception if it can't found your resource. This code may not work but here is the idea of how to override this behavior:
class CustomReadListener
{
private $decoratedListener;
public function __construct($decoratedListener)
{
$this->decoratedListener = $decoratedListener;
}
public function onKernelRequest(GetResponseEvent $event)
{
try {
$this->decoratedListener->onKernelRequest($event);
} catch (NotFoundHttpException $e) {
// Don't forget to throw the exception if the http method isn't PUT
// else you're gonna break the 404 errors
$request = $event->getRequest();
if (Request::METHOD_PUT !== $request->getMethod()) {
throw $e;
}
// 2 solutions here:
// 1st is doing nothing except add the id inside request data
// so the deserializer listener will be able to build your object
// 2nd is to build the object, here is a possible implementation
// The resource class is stored in this property
$resourceClass = $request->attributes->get('_api_resource_class');
// You may want to use a factory? Do your magic.
$request->attributes->set('data', new $resourceClass());
}
}
}
And you need to specify a configuration to declare your class as service decorator:
services:
CustomReadListener:
decorate: api_platform.listener.request.read
arguments:
- "#CustomReadListener.inner"
Hope it helps. :)
More information:
Information about event dispatcher and kernel events: http://symfony.com/doc/current/components/event_dispatcher.html
ApiPlatform custom operation: https://api-platform.com/docs/core/operations#creating-custom-operations-and-controllers
Symfony service decoration: https://symfony.com/doc/current/service_container/service_decoration.html

Mocking Symfony2's request and session in PHPUnit

I have a class that requires the Symfony2 service #request_stack which returns an instance of Symfony\Component\HttpFoundation\RequestStack. I use it to retrieve POST and GET values.
And also my class uses Symfony\Component\HttpFoundation\Session from Request->getSession() which it calls to get the current session.
Right now my class has a method that looks something like this:
class MyClass {
public function doSomething() {
//Get request from request stack.
$Request = $this->RequestStack->getCurrentRequest();
//Get a variable from request
$var = $Request->request->get('something');
//Processes $var into $someprocessedvar and lets say it's equal to 3.
//Set value to session.
$this->Request->getSession()->set('somevar', $someprocessedvar);
}
}
I need to be able to:
Mock RequestStack.
Get Request from RequestStack
Get Session from Request;
With all that said how can I test that MyClass successfully set the expected value in the session?
Not all code is worth unit testing. Usually this is an indicator that your code could be simplified. When you unit test code that is somewhat complex the tests can become a burden and normally it would be better to do an integration of edge-to-edge test in these cases. It's also not clear in your example how your class gets the RequestStack so I will assume that it has been injected in __construct.
With that said here's how you would test that code:
protected function setUp()
{
$this->requestStack = $this->getMock('Fully-qualified RequestStack namespace');
$this->SUT = new MyClass($this->requestStack);
}
/** #test */
public function it_should_store_value_in_the_session()
{
$value = 'test value';
$request = $this->getMock('Request');
$request->request = $this->getMock('ParameterBag');
$session = $this->getMock('Session');
$this->requestStack
->expects($this->atLeastOnce())
->method('getCurrentRequest')
->will($this->returnValue());
$request->request
->expects($this->atLeastOnce())
->method('get')
->with('something')
->will($this->returnValue($value));
$request
->expects($this->once())
->method('getSession')
->will($this->returnValue($session));
$session
->expects($this->once())
->method('set')
->with('somevar', $value);
$this->SUT->doSomething();
}
This should give you a starting point but beware having a wall-of mocks in your tests because very small changes to the implementation details can cause your tests to fail even though the behaviour is still correct and this is something you want to avoid as much as possible so the tests aren't expensive to maintain.
Edit: I thought some more about your question and realized that typically you can inject the Session as a dependency. If that's possible in your use case it would simplify the tests a lot.
You don't need to mock RequestStack, it's a super simple class. You can just create a fake request and push it to it. You can also mock the session.
// you can overwrite any value you want through the constructor if you need more control
$fakeRequest = Request::create('/', 'GET');
$fakeRequest->setSession(new Session(new MockArraySessionStorage()));
$requestStack = new RequestStack();
$requestStack->push($fakeRequest);
// then pass the requestStack to your service under test.
But in terms of testing, having to mess around with the internals of a class is not a good sign. Maybe you can create a handler class to encapsulate the logic you need from the request stack so you can test more easily.
It's difficult to imagine a situation where you'd have to be dealing with GET/POST parameters inside a unit-tested class. Have the Controller deal with HTTP requests and sessions (that's pretty much what they're there for), and pass the variables down into the relevant classes to deal with the rest.
That being said, Kevin's response is a possible solution if you want to go down that route.
According to this: http://api.symfony.com/2.4/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.html
I got to work something like the following:
public function testCompanySession()
{
$Request = new Request();
$Request->setSession(
new Session(new MockArraySessionStorage())
);
$CompanySessionMapper = new CompanyMapper($Request);
$Company = new Company();
$Company->setName('test');
$CompanySessionMapper->set($Company);
$Company = new Company();
$CompanySessionMapper->get($Company);
$this->assertEquals($Company->getName(), 'test');
}
Only one test per object type in my case since I'm only testing if the session name is correct and retrieving/storing the object properly in the session. CompanyMapper class uses the session to store the company object among other session/application related functions.
Anyone coming from Google like me wants to know how to mock request content, it is as simple as:
use AppBundle\Controller\DefaultController;
use Symfony\Component\HttpFoundation\Request;
use PHPUnit\Framework\TestCase;
class DefaultControllerTest extends TestCase
{
//#dataProvider?
public function testWithMockedRequest()
{
//create a request mock
$request = $this
->getMockBuilder(Request::class)
->getMock();
//set the return value
$request
->expects($this->once())
->method('getContent')
->will($this->returnValue('put your request data here'));
//create your controller
$controller = new DefaultController();
//get the Response based on your Request
$response = $controller->myRequestAction($request);
//assert!
$this->assertEquals(200, $response->getStatusCode());
}
}
As you can see you can execute a real controller which uses $request->getContent()
I hope this helps someone.

Symfony/DRY - check if user is granted in every action

I'm using this code to check if a user is granted in my Symfony application :
$securityContext = $this->container->get('security.context');
if($securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED') ){
$user = $this->get('security.context')->getToken()->getUser()->getId();
} else {
return $this->render('IelCategoryBundle:Category:home.html.twig');
}
I have to ckeck this in almost every CRUD action that I'm writing (edit, delete, ...).
I feel not DRY at all (no play on words ;-)). Is there a better way to check this in many actions ?
JMSSecurityExtraBundle provides the #Secure annotation which eases checking for a certain user-role before invoking a controller/service method.
use JMS\SecurityExtraBundle\Annotation as SecurityExtra;
/** #SecurityExtra\Secure(roles="IS_AUTHENTICATED_REMEMBERED") */
public function yourAction()
{
// ...
}
Your best bet would be to rely on Kernel event listeners: http://symfony.com/doc/current/cookbook/service_container/event_listener.html.
Implement your listener as a service and then you could set $this->user to desired value if isGranted results TRUE. Later on, you could easily retrieve the value within the controller using:
$myServiceListener->getUser();
On the other hand, if check fails you could easily redirect user to your home.html.twig.

Auto starting session in Symfony2

It seems Symfony 2.1 does not auto start sessions. Is there a way for me to auto start them?
I have an application that requires user's session id in several actions, but there is no way to know which one will need it first, so I can not start it on demand, I need it to be there when I request it.
As I understand, you need user's session id in some place from already started session, and if there is no session started yet, you want to do it
In this case try:
use Symfony\Component\HttpFoundation\Session\Session; // use sessions
class yourController extends Controller
{
public function yourAction()
{
$session = $this->getRequest()->getSession(); // Get started session
if(!$session instanceof Session)
$session = new Session(); // if there is no session, start it
$value = $session->getId(); // get session id
return $this->render('YourSampleBundle:your:your.html.twig',array(
'session_id' => $value
));
}
}
You can tell the framework session configuration (defined in your config.xml) to auto start the session.
However, this is depecated. Sessions by design are started on demand.
<framework:config>
<framework:session auto-start="true"/>
</framework:config>
My question would be why would you want to initialize a session unless you're using it?

sfUser equivalent in Symfony 2

What is the sfUser equivalent in Symfony 2?
sfUser allowed getting user session attributes using getAttribute() method.
Is Symfony\Component\Security\Core\User\User its equivalent? But it doesn't have getAttribute() method in it.
To get session attributes, get the session service from the container. This example is in a controller, where the session is available via a helper method:
public function fooAction()
{
$session = $this->getRequest()->getSession();
// Setting
$session->set("foo", "bar");
// Retrieving
$foo = $session->get("foo");
}
See the documentation for details. You can also retrieve the session explicitly from the container should you need it, via $container->get("session");
If you need the User object, you can get it via:
public function fooAction()
{
// Get user from security token (assumes logged in/token present)
$user = $this->get("security.context")->getToken()->getUser();
}
Again, see the documentation for further details.

Resources