Zend_Session_SaveHandler_DbTable is wiping the Session with every refresh? - php

I'm basically encountering the same problem as the poster in this question. My database is initialized properly. I've tried doing the initialization of both the database and the session SaveHandler in the application.ini and in the Bootstrap. Same result no matter how I do it.
Here's what the application.ini initialization looks like:
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "uname"
resources.db.params.password = "******"
resources.db.params.dbname = "dbname"
resources.session.saveHandler.class = "Zend_Session_SaveHandler_DbTable"
resources.session.saveHandler.options.name = "sessions"
resources.session.saveHandler.options.primary = "sessionID"
resources.session.saveHandler.options.modifiedColumn = "lastModifiedTime"
resources.session.saveHandler.options.dataColumn = "data"
resources.session.saveHandler.options.lifetimeColumn = "lifetime"
And here's what the Bootstrap initialization looked like:
protected function _initSession() {
$db = Zend_Db::factory('Pdo_Mysql', array(
'host' =>'localhost',
'username' => 'uname',
'password' => '******',
'dbname' => 'dbname'
));
Zend_Db_Table_Abstract::setDefaultAdapter($db);
$sessionConfig = array(
'name' => 'sessions',
'primary' => 'sessionID',
'modifiedColumn' => 'lastModifiedTime',
'dataColumn' => 'data',
'lifetimeColumn' => 'lifetime'
);
$saveHandler = new Zend_Session_SaveHandler_DbTable($sessionConfig);
Zend_Session::setSaveHandler($saveHandler);
Zend_Session::start();
}
My sessions database table is defined as follows:
create table sesssions (
sessionID char(32) primary key not null,
lastModifiedTime timestamp,
lifetime timestamp,
data text
) engine=innodb;
I have a test action that tests this through a very simple one field form that just dumps its contents into the Session. The action looks like this:
public function addAction()
{
$namespace = new Zend_Session_Namespace();
$form = new Application_Form_AddToSession();
$request = $this->getRequest();
if ($request->isPost()) {
if ($form->isValid($request->getPost())) {
$namespace->content = $request->getParam('toAdd');
}
}
$this->view->form = $form;
}
Here's the form it uses:
class Application_Form_AddToSession extends Zend_Form
{
public function init()
{
$this->setMethod('post');
$this->addElement('text', 'toAdd', array(
'filters' => array('StringTrim', 'StringToLower'),
'validators' => array(
array('StringLength', false, array(0, 256)),
),
'required' => true,
'label' => 'Add:',
));
$this->addElement('submit', 'add', array(
'required' => false,
'ignore' => true,
'label' => 'Add',
));
}
}
The view just shows the form.
To test whether or not the value actually went into the session, I use the index action. This is the index action in question:
public function indexAction()
{
$namespace = new Zend_Session_Namespace();
echo 'Content: '.$namespace->content.'<br>';
echo '<pre>'; print_r($_SESSION); echo '</pre>';
}
Now. If I don't have Session saving configured to use Zend_Session_SaveHandler_DbTable, ie, if I don't have session saving configured at all, then this works fine. I enter a value in the form field, go to the index action and have it output back to me. Session works exactly the way it is supposed to.
If I have Zend_Session_SaveHandler_DbTable configured in either the application.ini or the Bootstrap, then when I enter a value into the test field and go to the index action the value is gone. My database table has a row with the proper sessionID and the sessionID matches a cookie in my browser. But there is no other information in the database. data is NULL and both the TIMESTAMP fields are zeroed out.
I've run out of things to try. I've had the Mysql table as a regular table and an InnoDB table. I've tried every permutation of the database and session configuration I can come up with, including giving the db to the configuration array, and initializing one in the Bootstrap and the other in the .ini. I've scoured the web and StackOverflow for clues. I've seen other people post about similar problems, but none of the answers I've found have worked. What haven't I done? What have I screwed up? How can I make it work?

The problem is that you defined lastModifiedTime and lifetime columns as timestamp. They should be INT instead:
CREATE TABLE `sessions` (
`sessionID` char(32) NOT NULL,
`lastModifiedTime` INT,
`lifetime` INT,
`data` text,
PRIMARY KEY (`sessionID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
After this small modification it should work.

Related

Correct way to edit a database service after first initiation

I use two different database services in my Phalcon application.
db: Global database with system-wide data, including accounts and users
db_data: This database hosts customer-specific data. The database-name is different for each customer/account. This is determined when the customer sign in.
This works in the web application, when I have one account active. Now I have a cronjob, that is going to loop through a table in the global "db" database. And after it is going to connect to the specific "db_data" database.
This works for the first account, but after this it will not connect to the new database. It still use the first initiated database.
The db_data service is a shared service in the services.php:
$di->set('db_data', function () use ($config, $di) {
if(!$di->getCore()->getAccount()) {
throw new \MyNamespace\Exception(_('Account is not set. Can not load account database.'));
}
$eventsManager = $di->getShared('eventsManager');
$dbListener = new \MyNamespace\Module\Core\Helper\Model\DatabaseListener();
$eventsManager->attach('db_data', $dbListener);
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array(
'host' => $config->database->host,
'username' => $config->database->username,
'password' => $config->database->password,
'dbname' => $config->database->data_dbname_prefix.$di->getCore()->getAccount()->id,
'name' => 'data',
));
$connection->setEventsManager($eventsManager);
return $connection;
}, true);
In the first model in "db" i have the following in the initialize() function:
$this->setConnectionService('db');
In the second model in "db_data" i have the following in the initialize() function:
$this->setConnectionService('db_data');
Here is an example of the cron PHP-file:
$screens = \MyNamespace\Module\DigitalSignage\Model\Screen::find(array(
'conditions' => 'deleted_at IS NULL',
));
foreach($screens as $screen) {
$console->getDi()->getCore()->setAccount(\MyNamespace\Module\Core\Model\Account::findFirst('id='.$screen->account_id));
$campaign = \MyNamespace\Module\DigitalSignage\Model\Campaign::findFirst(array(
'conditions' => 'id = :id: AND account_id = :account_id: AND deleted_at IS NULL',
'bind' => array(
'id' => $screen->digitalsignage_campaign_id,
'account_id' => $console->getDi()->getCore()->getAccount()->id,
),
));
var_dump($campaign);
}
What is the correct way to change the database service parameteres after first initiation?
Phalcon version: 3.2.2 PHP version: 7.0.22

A row associates with session is deleted when user logout in YII

I have a site that uses YII. One thing that it does is storing session information into a MySql database table YiiSession. Also in a separate table (users_sessions), a new row is inserted with the following information:
session id
user id
online status of a user
I create another table for session because YiiSession is part of YII. Also users_session keeps track online status of a user, whereas YiiSession doesn't.
I only make an insertion into users_session during user login.
But I don't understand that when user logs out, the session that associates with the user got deleted.
Note that the session_id in the users_session is a foreign key to the one in YiiSession. But the one in YiiSession still exists even though it has an expiration date.
What mechanism that possibly deletes the row? Please help.
You should extend CDbHttpSession and overwrite the method destroySession($id). There you append the code and delete your entry also.
Configure Yii to use your session class instead of it's own in config/main.php:
'session' => array(
'autoStart' => true,
'class' => 'application.components.<your session class name>',
'connectionID' => 'db',
'sessionTableName' => 'session',
'autoCreateSessionTable' => false,
'timeout' => 3600, // 60 min
),
However I would not do this with a separate table, just add some fields to the YiiSession table. That's what I did and it works pretty well.
EDIT:
You should create a file named MyDbHttpSession.php in protected/components.
In this file extend CDbHttpSession like so:
class MyDbHttpSession extends CDbHttpSession
{
protected function createSessionTable($db, $tableName)
{
// basically this is a copy of CDbHttpSession
$db->createCommand()->createTable($tableName, array(
'id' => 'CHAR(32) PRIMARY KEY',
'expire' => 'integer',
'data' => $blob,
// your additional fields
'idUser' => 'INT(10) NULL',
'online' => 'TINYINT(1)',
// more of your fields here if you have
));
}
// this sets the user ID by session ID
public function setIdUser($idUser)
{
$db = $this->getDbConnection();
$db->setActive(true);
$db->createCommand()->update($this->sessionTableName, array('idUser' => (int) $idUser),
'id=:id', array(':id' => $this->sessionId)
);
}
// and this sets the online status, pass true or false to $online
public function setOnline($online)
{
$db = $this->getDbConnection();
$db->setActive(true);
$db->createCommand()->update($this->sessionTableName, array('online' => (int) $online),
'id=:id', array(':id' => $this->sessionId)
);
}
}
Set Yii to use your session class instead of it's own like I wrote above.
You should already have a class WebUser. If not, create one in protected/components and extend CWebUser.
In this class add a method afterLogin:
public function afterLogin($fromCookie)
{
// store user ID and online status in session table
Yii::app()->session->setIdUser($this->id);
Yii::app()->session->setOnline(true);
return parent::afterLogin($fromCookie);
}
You can from wherever you are in Yii use Yii::app()->session->setOnline(true); or Yii::app()->session->setOnline(false);

Zend Framework 2 Setup default value if invalid input

So I created a input filter (see below) but I have to test 24 fields to make sure they are all valid (only 1 listed below to keep this simple). In this case, the input is coming from an e-mail server, not a user, so I need accept the input, and not send an error back. However, I still need to check the data to ensure no one is messing with the headers / fields trying to mess everything up.
So my question is, how can I sent a default value for each input? For example blow, if the mailbox is length 0, something is wrong, so I just want to set the value to be something like 'InvalidMailbox' so I can still store this in the database,
public function getInputFilter()
{
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'mailbox',
'required' => true,
'filters' => array(
array('name' => 'StripTags'),
array('name' => 'StripNewLines'),
array('name' => 'StringToLower'),
array('name' => 'StringTrim'),
),
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 200,
),
),
),
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
Calling it...
$mail = new SMail();
$inputFilter = $mail->getInputFilter;
$inputFilter->setData($data);
if ($inputFilter->isValid()) {
//echo "The form is valid\n";
} else {
// Maybe set the default here?
// but with 24 different fields, how can I know which one caused the error?
//echo "The form is not valid\n";
}
Okay, sorry that i didn't get the quest right first time. Going by the Source of the BaseInputFilter, there is a function called getInvalidInput(), so my assumption is, that you can do the following:
$defaultData = array(
'elementName' => 'Default Value'
);
$returnData = array();
if (false === $inputFilter->isValid()) {
$falseInputs = $inputFilter->getInvalidInput();
foreach ($falseInputs as $input) {
$returnData[$input->getName()] = $defaultData[$input->getName()];
}
}
$goodInputs = $inputFilter->getValidInput();
$finalData = array_merge($goodInputs, returnData);
This however is no tested code. I'm not sure if $input->getName() is available. You may need to adjust that part accordingly. I'm quite certain though that this should be able to get you started, hopefully ;)

Initialize an Associative Array with Key Names but Empty Values

I cannot find any examples, in books or on the web, describing how one would properly initialize an associative array by name only (with empty values) - unless, of course, this IS the proper way(?)
It just feels as though there is another more efficient way to do this:
config.php
class config {
public static $database = array (
'dbdriver' => '',
'dbhost' => '',
'dbname' => '',
'dbuser' => '',
'dbpass' => ''
);
}
// Is this the right way to initialize an Associative Array with blank values?
// I know it works fine, but it just seems ... longer than necessary.
index.php
require config.php
config::$database['dbdriver'] = 'mysql';
config::$database['dbhost'] = 'localhost';
config::$database['dbname'] = 'test_database';
config::$database['dbuser'] = 'testing';
config::$database['dbpass'] = 'P#$$w0rd';
// This code is irrelevant, only to show that the above array NEEDS to have Key
// names, but Values that will be filled in by a user via a form, or whatever.
Any recommendations, suggestions, or direction would be appreciated. Thanks.
What you have is the most clear option.
But you could shorten it using array_fill_keys, like this:
$database = array_fill_keys(
array('dbdriver', 'dbhost', 'dbname', 'dbuser', 'dbpass'), '');
But if the user has to fill the values anyway, you can just leave the array empty, and just provide the example code in index.php. The keys will automatically be added when you assign a value.
First file:
class config {
public static $database = array();
}
Other file:
config::$database = array(
'driver' => 'mysql',
'dbhost' => 'localhost',
'dbname' => 'test_database',
'dbuser' => 'testing',
'dbpass' => 'P#$$w0rd'
);

Storing Sessions in DB Table Not Working (using Zend_Session_SaveHandler_DbTable)

This is my table:
CREATE TABLE `Sessions` (
`id` varchar(32) NOT NULL,
`modified` int(11) default NULL,
`lifetime` int(11) default NULL,
`data` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
This is in my bootstrap:
$sessionConfig = array(
'name' => 'Sessions', //table name as per Zend_Db_Table
'primary' => 'id', //the sessionID given by php
'modifiedColumn' => 'modified', //time the session should expire
'dataColumn' => 'data', //serialized data
'lifetimeColumn' => 'lifetime' //end of life for a specific record
);
$saveHandler = new Zend_Session_SaveHandler_DbTable($sessionConfig);
//cookie persist for 30 days
Zend_Session::rememberMe($seconds = (60 * 60 * 24 * 30));
//make the session persist for 30 days
$saveHandler->setLifetime($seconds)
->setOverrideLifetime(true);
//similarly,
//$saveHandler->setLifetime($seconds, true);
Zend_Session::setSaveHandler($saveHandler);
Zend_Session::start();
When I log in, nothing ever gets written to the Sessions table and I am logged out on the very next pageview.
Any ideas? I'm trying to have my users be perpetually logged in. Am I missing something in my login controller possibly?
I just managed to get this working:
My application.ini:
resources.db.isDefaultTableAdapter = true
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.dbname = "dbname"
resources.db.params.username = "username"
resources.db.params.password = "password"
my bootstrap.php:
protected function _initSession() {
$resource = $this->getPluginResource('db');
$dbAdapter = $db = $resource->getDbAdapter();
Zend_Registry::set("db", $dbAdapter);
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);
$config = array(
'name' => 'session',
'primary' => 'id',
'modifiedColumn' => 'modified',
'dataColumn' => 'data',
'lifetimeColumn' => 'lifetime',
'db' => $dbAdapter
);
Zend_Session::setSaveHandler(new Zend_Session_SaveHandler_DbTable($config));
Zend_Session::start();
}
This function was placed as first function in the bootstrap.php, because sessions are started, when you construct a Zend_Session_Namespace object for the first time. If you do this, before the _initSession()-function got called, a standard file-based session may be started.
Finally, the session.sql:
DROP TABLE IF EXISTS `session`;
CREATE TABLE `session` (
`id` char(32) NOT NULL DEFAULT '',
`modified` int(11) DEFAULT NULL,
`lifetime` int(11) DEFAULT NULL,
`data` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Somewhere i read that the table must be InnoDB.
You have to initialize your DB handler before telling Zend_Session to use the DB, either by setting a default adapter for Zend_Db, or passing your adapter in the config array as 'db'.
Maybe you have to put Zend_Session::start(); before anything else on the page... ?
Had the same problem with an implementation of redis as session handler.
For me, putting the method _initSession as first method in my bootstrap class works.
Hope it will help someone.

Categories