I'm experiencing a problem with symfony's session values handling.
Basically, the problem is this, I have a action filter activated that takes the values of the module and action executed and stores them in the session superglobal.
This is my filter's code:
<------BEGIN CODE---------------->
class getPrevModuleActionFilter extends sfFilter
{
public function execute ($filterChain)
{
//---------------Code to execute *BEFORE THE ACTION* execution---------------
if ($this->isFirstCall()) # Execute this filter only once
{
// Filters don't have direct access to request & user objects => Use context object to get them
$request = $this->getContext()->getRequest();
$user = $this->getContext()->getUser();
if($request->getParameter('action') !== "setCulture")
{
$_SESSION['prev_module'] = "M=".$request->getParameter('module');
$_SESSION['prev_action'] = "A=".$request->getParameter('action');
}
}
//---------------Execute next filter in the chain---------------
$filterChain->execute();
//---------------Code to execute *AFTER THE ACTION* execution, before the rendering---------------
//(...)
}
}
<------END CODE---------------->
The weird thing is that if I do a print_r on the front web controller at the very last minute I see that:
When an action that's not 'setCulture' all goes well (ie, the session gets previous module and action as it should)
When action 'setCulture' gets executed: Symfony stores following values in session:
Array (
[prev_module] => M=
[prev_action] => A=
(etc)
)
ie, it looses the values of session for those 2 entries.
I tried using different namespaces, I tried using symfony's setAttribute from sfUser to handle session values. At the end I tried the raw session handling of PHP. Apparently it seems that the shutdown methods of the factories related to user and storage of session values mess up the session values!
I need your help, please.
SPECS:
Symfony version: 1.4.6
PHP: 5.3
I have Symfony's cache disabled
I'm running the code with the frontend_dev.php controller
Well, I guess Symfony messes up SESSION and COOKIES when used in filters.
I ended up creating my own filter mechanism that performs actions for an entire app.
So, to clarify, my choice was:
create a class autoloaded in root lib folder, that has a static method called 'fe_app_init'
add a preExecute method to the actions of each module in FE app that uses fe_app_init from that class
Now the fe_app_init() handles the values in SESSION rightfully.
It's a shame that Symfony 1.4 has a tool such as filters but then messes up SESSION and COOKIES handling in those.
Related
I am a Java developer (I often used Spring MVC to develop MVC web app in Java) with a very litle knowledge of PHP and I have to work on a PHP project that use CodeIgniter 2.1.3.
So I have the following doubt about how exactly work this controller method:
So I have this class:
class garanzieValoreFlex extends CI_Controller {
.....................................................
.....................................................
.....................................................
public function index() {
$this->load->model('Direct');
$flagDeroga = "true" ;
$this->session->userdata("flagDeroga");
$data = $this->session->userdata("datiPreventivo");
$this->load->model('GaranzieValoreFlexModel');
$data = $this->session->userdata("datiPreventivo");
$this->load->model('GaranzieValoreFlexModel');
$this->load->view('garanziavalore/index_bootstrap',$data);
}
}
I know that the index() method of the garanzieValoreFlex controller class handle HTTP Request toward the URL: http://MYURL/garanzieValoreFlex and show the /views/garanzievalore/index_bootstrap.php page.
It works fine. The only think that I can't understand is what exactly does this code line:
$data = $this -> session -> userdata("datiPreventivo");
Can you help me what exactly is doing? I think that it is putting something into the HttpSession or something like this but I am absolutly not sure about it and I can't understand the logic.
session is a Codeigniter (CI) library (class) that allows data to persist across multiple page calls from a browser. In the version of CI you are using "native" PHP session functionality is not used. But CI's session class does mimic PHP's session in that data is stored in a PHP associative array.
The class has many different methods to store and retrieve user defined data. The function userdata("index_to_data") is one of the main class methods. It is used to retrieve data that has been stored in the session class.
The argument passed to userdata() is the key to a value in the session class array $userdata. So, $this->session->userdata("datiPreventivo"); returns the value stored at $userdata["datiPreventivo"]. If the key (in this case "datiPreventivo") does not exist then $this->session->userdata("datiPreventivo") returns FALSE.
Somewhere in the code you are working with you will find a line where data is stored in the session. The line of code might look something like this.
$newdata = array("datiPreventivo" => $something_value);
$this->session->set_userdata($newdata);
Searching your code for "$this->session->set_userdata" might be helpful to understand what exactly is being saved for future page loads.
It is important to know that CI's session class was completely rewritten in versions > 3.0 so the current documentation may not be very helpful to you. You will need to find the documentation for the version you are using to learn more about the session library. I believe that documentation is included in the download for your version which can be found here.
Example of my code:
class SiteController extends Controller {
/**
* This is the default 'index' action that is invoked
* when an action is not explicitly requested by users.
*/
public function actionIndex() {
$_SESSION['test'] = 'testdata';
var_dump($_SESSION); exit;
}
Example on the second request:
class SiteController extends Controller {
/**
* This is the default 'index' action that is invoked
* when an action is not explicitly requested by users.
*/
public function actionIndex() {
var_dump($_SESSION);exit;
}
I have a project in yii. Project not mine - I'm just trying to fix errors.
My problem is:
first var_dump() shows that $_SESSION variable HAS the "test" index with "testdata". On the second request though I get an empty array of $_SESSION variable. Meaning, that every request session gets cleaned up. I've checked - the session ID stays the same. I've also checked this projects config - i can't find any references to extending SESSION component and changing it's behaviors. Also, when logging in yii DOES save states into SESSION, but the login fails because of SESSION being cleaned after redirect. That is to say COOKIE BASED authentication works which just proves the root of the problem.
Would very much appreciate help.
UPDATE
I've narrowed it down. This is the code for FRONT YII FRONT CONTROLLER(index.php):
<?php
#session_start(); // this line is at cwebuser.php at init() method and it is called at every request. should work properly.
var_dump($_SESSION);
$_SESSION['test'] = 'asdasd';
var_dump($_SESSION);exit;
It still prints empty $_SESSION on the second REQUEST. So the problem is probably not with the framework.
You can set session details in your /protected/config/main.php (this is the default unless you have changed it in index.html)
'session' => array(
'autostart' => true,
'timeout' => 1440, // time in seconds
),
Read about Session on CHttpSession
My problem was that I accessed my website via server ip : 123.123.123.123/site/index and this has conflicts with accessing and saving the session. Though I don't know the details. If someone has knowledge on this stuff I will gladly accept his(her) answer as the right one.
There is a file called controller.php under protected/components/controller.php which will be called before any action get called .. u can check that file and see... is there done any logout function calledthere...
//It clears all the sesstion data... or any php way
Yii::app()->user->logout();
Yes if u are in moule then u can also check ModuleName.php under module directopry ... if there is any session clearing script...
which clears the session... And yes this is not the right way of using session in Yii yes it is PHP but YII .... u can use folowing sytax dfor sessions..
//to set a session variable mnamed test
Yii::app()->user->setState('test',$test);
//to get a session variable named tets
Yii::app()->user->getState('test');
I am building an API in CakePHP. I have a function that as part of its execution first destroys the cookies associated with the session. I am using the following code to do this.
public function new_thing () {
// I first call another controller to use functions from that controller
App::import('Controller', 'Person');
$PersonsController = new PersonsController;
// This function call is the problem
// This does not throw any errors but does not destroy the cookie as requested
$PersonsController->_kill_auth_cookie()
}
// This is from the Person controller, these are the functions used in the API
// This is the function that sets the cookies
public function _set_auth_cookie( $email ) {
setcookie(Configure::read('auth_cookie_name'), $email);
}
// this is the function that does not properly destroy the cookie from the API
// interestingly, it does work when called from in this controller
public function _kill_auth_cookie() {
setcookie(Configure::read('auth_cookie_name'), 'xxx', time()-7200);
}
I cannot get the API to properly expire the cookie that is created earlier in the session, I am not sure why. Additionally—what is maddening—is that the logs are empty and no error is being thrown of any kind, so I am not sure what to do next.
There is so much wrong in this code and concept…
DON'T instantiate controllers anywhere. It is plain wrong, broken by design and violates the MVC pattern. Only one controller should be dispatched by the framework itself based on the request; you don’t instantiate them manually.
An API using cookies? Well, not impossible but definitely not nice to work with. It’s possible but I’ve never seen one in the wild. I feel sorry for the person who has to implement it. See this question.
Why are you not using the CookieComponent? It has a built-in destroy() method to remove a cookie.
If you have an “auth” cookie, why are you not using CakePHP’s built-in Auth system? It will deal with all of that.
Use App::uses() not App::import() here
By convention, only protected functions should be prefixed with _
The first point is very likely the reason why cookie and sessions are messed up because the second controller instance initiates components again, and by this cookie and session maybe a second time as well. However, this can lead to “interesting” side effects.
I first call another controller to use functions from that controller
This is the evidence that your architecture is broken by design. The code that needs to be executed somewhere else; should be in a model method in this case. Or at least a component if there are controller-related things to be shared between different controllers.
I am currently reading "Zend Framework 1.8
Web Application Development" written by "Keith Pope". In that he tells us to use 'ActionStack' so that the Controller for the category top-level menu will be called on every request. The source code for the the plugin is :
class SF_Plugin_Action extends Zend_Controller_Plugin_Abstract
{
protected $_stack;
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$stack = $this->getStack();
// category menu
$categoryRequest = new Zend_Controller_Request_Simple();
$categoryRequest->setControllerName('category')
->setActionName('index')
->setParam(
'responseSegment',
'categoryMain'
);
// push requests into the stack
$stack->pushStack($categoryRequest);
}
public function getStack()
{
if (null === $this->_stack) {
$front = Zend_Controller_Front::getInstance();
if (!$front->hasPlugin(
'Zend_Controller_Plugin_ActionStack'
)) {
$stack = new Zend_Controller_Plugin_ActionStack();
$front->registerPlugin($stack);
} else {
$stack = $front->getPlugin('ActionStack');
}
$this->_stack = $stack;
}
return $this->_stack;
}
}
I have read the code for 'ActionStack' plugin. In the 'postDispatch' function it saves the current request and then in the 'forward' function it changes the current request's controller, action and also set parameters. Then what will happen to the current request ? How it will be executed ?
Also I heard ActionStack is evil. As I am a newbie I didn't understand most of it, as he did not explained it(for newbies). Why ActionStack is evil ?
ActionStack is evil as it promotes a bad practice: tying view-related logic to controllers. Additionally, it has a huge, negative impact on performance.
Typically, ActionStack is used to develop "widgetized" sites. You set up a list of widgets you need, and map them to individual controller actions, and then loop through the stack. The design flaw with this is that you're now executing N different controllers -- when, really, ONE controller is all you should use. Each individual controller should be able to map the incoming request to the necessary view and models. Instead, you're now basically executing an MVC triad simply to get back a bit of content.
The performance implications come from the fact that you now have to store the previous results in memory, and then re-dispatch -- which means running all pre/post dispatch plugins again, potentially having conflicts in state, and more.
The better approach is to have model-aware view helpers. You can use action helpers to marshal the appropriate models and inject the helpers, and then in your view scripts and/or layout, you simply invoke them. This honors an appropriate separation of concerns, and does not have the same performance implications.
(In ZF2, this marshaling is far easier, as you can define factories for individual helpers -- as such, you can simply use them in your view scripts, and not have to do anything special in the controllers at all in order to deliver widgetized content.)
It is the answer to my first question. As the action stack will be executed last (in the post dispatch) the current response object will be holding all content that got rendered for the request the user made, and the action stack will append the data to it. Hence the user will get content that he asked for + content that got rendered due to action stack
In your example. The front controller will start the execution of the current request and fire the routeStartup, routeShutdown and dispatchLoopStartup events. The dispatchLoopStartup event will call your plugin and your plugin will add a request object to the action stack.
Now the front controller will dispatch the current request, set the isDispatched flag of the current request to true and fire the postDispatch event. Now the Action stack plugin will get called. The front controller will pass the current request object to the Action Stack plugin as an argument and the Action stack plugin will update the controller, module, action properties of the current request object and set its isDispatched flag to false (Forward method).
Now the front controller will check the isDispatched flag of the current request object and since it was reset by the Action Stack plugin start the dispatch process again. And now your new request will get executed.
In short the front controller dispatches the current request, the action stack plugin resets the values of current request and the dispatch loop stars again.
I’m trying to better understand what the best method would be to persist data between requests in this scenario (using Zend Framework):
Say I have an Events controller and the default (index) view displays any existing Announcements (if there are any), and a link to Add a new Announcement (Both Event and Announcement are arbitrary objects). I’m trying to retrieve the eventId so I can associate the new Announcement with it when saving it to the database. Compositionally, an Event consists of 0 to many Announcements. From my limited understanding of the Zend Framework, I see two main options.
Option one: Make the URL something like ‘/event/addAnnouncement/eventId/5’, which makes retrieving the eventId easy via route/path parameters.
Option two: In the indexAction of the controller, save the eventId to a session variable, which can then be retrieved in the addAnnouncementAction of the Event controller. This way the Add Announcement link would simply be ‘/event/addAnnouncement/’.
Can anyone shed some light on which of these two ways is better, or if there is another way I’m not aware of?
As always, any help is much appreciated. Thanks.
The question to ask yourself is, how long do you need to persist the data? If you only need to save the data to pass it to the next action you can use POST or GET, the GET would pass through the url and the POST would not(typically).
The example you presented would suggest that you need to persist the data just long enough to validate, filter and process the data. So you would likely be very satisfied passing the few pieces of data around as parameters(POST or GET). This would provide the temporary persistence you need and also provide the added benefit of the data expiring as soon as a request was made that did not pass the variables.
A quick example (assume your form passes data with the POST method):
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost()){
$data = $form->getValues();//filtered values from form
$model = new Appliction_Model_DbTable_MyTable();
$model->save($data);
//but you need to pass the users name from the form to another action
//there are many tools in ZF to do this with, this is just one example
return $this->getHelper('Redirector')->gotoSimple(
'action' => 'newaction',
array('name' => $data['name'])//passed data
);
}
}
if you need to persist data for a longer period of time then the $_SESSION may come in handy. In ZF you will typically use Zend_Session_Namespace() to manipulate session data.
It's easy to use Zend_Session_Namespace, here is an example of how I often use it.
class IndexController extends Zend_Controller_Action {
protected $_session;
public function init() {
//assign the session to the property and give the namespace a name.
$this->_session = new Zend_Session_Namespace('User');
}
public function indexAction() {
//using the previous example
$form = new Application_Form_MyForm();
if ($this->getRequest()->isPost()) {
if ($form->isValid($this->getRequest()->getPost()){
$data = $form->getValues();//filtered values from form
//this time we'll add the data to the session
$this->_session->userName = $data['user'];//assign a string to the session
//we can also assign all of the form data to one session variable as an array or object
$this->_session->formData = $data;
return $this->getHelper('Redirector')->gotoSimple('action'=>'next');
}
}
$this->view->form = $form;
}
public function nextAction() {
//retrieve session variables and assign them to the view for demonstration
$this->view->userData = $this->_session->formData;//an array of values from previous actions form
$this->view->userName = $this->_session->userName;//a string value
}
}
}
any data you need to persist in your application can sent to any action, controller or module. Just remember that if you resubmit that form the information saved to those particular session variables will be over written.
There is one more option in ZF that kind of falls between passing parameters around and storing data in sessions, Zend_Registry. It's use is very similar to Zend_Session_Namespace and is often used to save configuration data in the bootstrap (but can store almost anything you need to store) and is also used by a number of internal Zend classes most notably the flashmessenger action helper.
//Bootstrap.php
protected function _initRegistry() {
//make application.ini configuration available in registry
$config = new Zend_Config($this->getOptions());
//set data in registry
Zend_Registry::set('config', $config);
}
protected function _initView() {
//Initialize view
$view = new Zend_View();
//get data from registry
$view->doctype(Zend_Registry::get('config')->resources->view->doctype);
//...truncated...
//Return it, so that it can be stored by the bootstrap
return $view;
}
I hope this helps. Pleas check out these links if you have more questions:
The ZF Request Object
Zend_Session_Namespace
Zend_Registry
Option 1 is better, although in your example this is not a POST (but it could be done with a POST).
The problems with option 2 are:
If a user had multiple windows or tabs open at the same time, relating to different events, how would you track which event ID should be used?
If a user bookmarked the add event page and came back later, the session var may not be set
Option 2 is also a little more complicated to implement, and adds a reliance on sessions.