I have the following code in my custom Wordpress plugin:
add_action('init', 'wwp_StartSession', 1);
function wwp_StartSession() {
if(!session_id()) {
session_start();
}
}
When I edit this in the Wordpress editor it can be saved. However if I want to save again after more edits I get the following error:
Unable to communicate back with site to check for fatal errors, so the PHP change was reverted. You will need to upload your PHP file change by some other means, such as by using SFTP.
When I remove the line
sesion_start();
I am able to save again.
I already Googled for quite a while now and some say that the if(!session_id()) should do the trick, but it seems it doesn't.
Hoping someone has any ideas on this.
It still behaves strange. If I do this:
public function startSession() {
if(!is_admin() && !session_id()) {
echo 'not session id';
session_start();
}
}
Then it does save in the backend....however I don't have my session variables available...even though the echo statement is printed, so session_start() is called.
If I leave out the !is_admin() part I can work with the session....but then I have the backend trouble again.
Finally I found out :). I found the answer here: https://core.trac.wordpress.org/ticket/47320
This is now my code:
class Session {
public static function startSession() {
// This loads variables to $_SESSION for reading
if(!session_id()) {
session_start();
session_write_close(); // Other plugins can restart a session again via session_start()
}
}
public static function endSession() {
session_destroy ();
}
public static function storeData($key, $value) {
session_start();
$_SESSION[$key] = $value;
session_write_close();
}
}
startSession is hooked to init
endSession is hooked to wp_login and wp_logout
and wherever I need to save data call storeData
Related
I have a LoginController where I do my usual login operation with combination of an email address and a password associated with the account.
I have separated my Hybridauth related code into a separate controller named OauthController where I have all my Hybridauth magic and where my callback / endpoint resides.
In the OauthController I check if user's email from the specified provider is already registered, and in either case I try to login that user with $this->Auth->setUser(object).
Whenever, or whatever from the $this->Auth is called, I get a response stating:
Session was already started
I have browser through CakePHP 3 code and found the following statement in:
vendor/cakephp/cakephp/src/Network/Session.php (335)
public function start()
{
if ($this->_started) {
return true;
}
if ($this->_isCLI) {
$_SESSION = [];
$this->id('cli');
return $this->_started = true;
}
if (session_status() === \PHP_SESSION_ACTIVE) {
throw new RuntimeException('Session was already started');
}
...
And that's the point in code where that message is thrown at me.
Now, as I browsed through the Hybridauth code itself, I have found following in:
vendor/hybridauth/hybridauth/src/Storage/Session.php (46)
public function __construct()
{
if (session_id()) {
return;
}
if (headers_sent()) {
throw new RuntimeException('HTTP headers already sent to browser and Hybridauth won\'t be able to start/resume PHP session. To resolve this, session_start() must be called before outputing any data.');
}
if (! session_start()) {
throw new RuntimeException('PHP session failed to start.');
}
}
And both of them call session_start, one before the other, although CakePHP's part is blocking me.
I have tried removing !session_start() check from Hybridauth, but then Hybridauth doesn't know where to read out it's thingies it needs to read.
So, as a demonstrator, I am trying to achieve this in OauthController:
<?php
namespace App\Controller;
use Hybridauth\Hybridauth;
class OauthController extends AppController
{
public function callback($provider)
{
try {
$hybridauth = new Hybridauth($config);
// additional mystery code
$hybridauth->authenticate();
if($everything_okay) {
$this->Auth->setUser($userObject); // and this is the point of failure
return $this->redirect('/account'); // and this never happends... :(
}
}
}
}
Any help, ideas, insights on how to deal with this are all welcome!
Simply start the CakePHP session manually before using the Hybridauth library, so that it bails out at the session_id() check and picks up the existing session.
For example in your controller:
$this->getRequest()->getSession()->start();
// in CakePHP versions before 3.6/3.5
// $this->request->session()->start();
I'm using codeigniter cache on my personal CMS. The problem is, i don't want to show cached pages if the user it's logged on administration.
Saw this tutorial on google:
http://www.badpenguin.org/codeigniter-cache-disable
class MY_Output extends CI_Output {
function _display_cache(&$CFG, &$URI)
{
/* Simple Test for Ip Address */
if ($_SERVER['REMOTE_ADDR'] == NOCACHE_IP )
{
return FALSE;
}
/* Simple Test for a cookie value */
if ( (isset($_COOKIE['nocache'])) && ( $_COOKIE['nocache'] > 0 ) )
{
return FALSE;
}
/* Call the parent function */
return parent::_display_cache($CFG,$URI);
}
}
The problem it's that the session it's on database (ci_sessions), and i can't access it inside MY_Output.
using:
$CI =& get_instance();
$CI->session->userdata('userID')
give me:
Class 'CI_Controller' not found in
As output runs before controller, and session Needs CIcontroller, the only Thing i can think of its disable session storage on database and the encription, and i don't want do that rs.
Someone can give me some light on this? i still can't find the solution to this!
Thanks!
Try this:
#$SESS = & load_class('Session', 'libraries/Session');
The # is there to prevent it from whining about "session already started". Do note: this is for CI3. The code for the original Output class states (lines 407-409):
// Note: We use load_class() because we can't use $CI =& get_instance()
// since this function is sometimes called by the caching mechanism,
// which happens before the CI super object is available.
I've got a php script and I want to call another php script from within it which logs data.
Problem is I need to send variables and include ('log.php?log=a&dob=fromtest'); is not working as it's saying the script is not found. If I take the variables out then the script is found no problem.
What's the proper way to do this?
You could define the variable in your code.
$_GET['log'] = 'a';
$_GET['dob'] = 'fromtest';
include('log.php');
But the proper way to do it is to write a class for it.
Should look like this:
logManager.class.php
class logManager {
public function addLog($log, $dob) {
/* do you logging */
}
// example for get log items
public function getLog($log, $limit) {
/* do your get log item thing */
}
}
LogThis.php
require_once('logManager.class.php');
$log = new LogManager();
$log->addLog('a', 'fromtest');
If you include a file i would recommend you to make a function and then call the function with parameters like this:
log.php:
<?php
function xy($log, $dob) {
//do something
}
?>
in your script:
<?php
require_once("log.php");
xy("a", "fromtest");
?>
Or you use the header(); function like this:
header("Location: log.php?log=a&dob=fromtest");
The parser will interpret that (log.php?log=a&dob=fromtest) as a file name which is very wrong because no such script file exists.I suggest you consider using the header function to access a script sending variables at the same time . Take for example this site when you 're trying to login, the url will be like.
http://so/login/?return_url=http://mycurrentpage/
I am having big time trouble with Codeigniter and Internet Explorer.
Please take a look at the sample page
It is something very simple afflicting me the last couple of days.
By pressing the login-button I do nothing more but calling a function start()
public function start()
{
setcookie('loginstatus', TRUE, time()+7200); // setting a cookie
redirect('stream', 'location'); // redirecting to the index controller
}
In the index-function I do the following:
public function index()
{
if ($this->isuserloggedin() == TRUE)
{
echo "The user is already logged in";
$this->load->view("v_stream_start");
} else {
echo "The user still has to login";
$this->load->view("v_stream_login");
}
}
Here's the userisloggedin() method where I check for an existing cookie:
public function isuserloggedin() {
if (isset($_COOKIE['loginstatus'])) {
return TRUE;
} else {
return FALSE;
}
}
Please check IE vs. FF/Safari/Chrome. It is working in all browsers exept IE and I have no clue why.
Everything runs as expected until I call the redirect() method - after that IE somehow can’t retreive the cookie while all other browsers can.
I already tried changing ci_session to ci_session and also checked the accurate server time.
Thanks so much for any kind of help.
Have you tried using the $this->input->cookie() methods instead of setting it raw through php?
http://codeigniter.com/user_guide/libraries/input.html
Okay the soultion seems to be, that you can not redirect within your own controller to your index-function and retreive the cookies there with Internet Explorer. When you redirect to another method within that controller it is working => redirect('controllername/methodname);
I've one page where i do a long polling i've to use at the begin of this page this
session_start();
session_write_close();
Because :
to prevent concurrent writes only one script may operate on a session at any time
So if i do not and the long polling is running the user will not be able to load another page.
So accessing to my data in session from this polling page is possible but at some point in my script i've to save my session back to the server because i made some change in it.
What's the way to do it?
That will be very nice it'll be a way to do something like
session_write_open();
//do stuff
session_write_close();
But the session_write_open() doesn't exist!
Thanks
Before you make some change to the session, call session_start again. Make the changes, and if you still do not want to exit call session_write_close once more. You can do this as many times as you like.
The previous solution will create a session ids and cookies... I wouldn't use it as is:
Session is created every time you call session_start(). If you want
to avoid multiple cookie, write better code. Multiple session_start()
especially for the same names in the same script seems like a really
bad idea.
see here : https://bugs.php.net/bug.php?id=38104
I am looking for a solution right now too and I can't find one. I agree with those who say this is a "bug".
You should be able to reopen a php session, but as you said session_write_open() does not exist...
I found a workaround in the above thread. It involves sending a header specifying manually the session id's cookie after processing the request. Luckily enough I am working with a home-brewed Front Controller that works so that no sub-controller will ever send data on its own.
In a nutshell, it works perfectly in my case. To use this you might just have to use ob_start() and ob_get_clean(). Here's the magic line:
if (SID) header('Set-Cookie: '.SID.'; path=/', true);
EDIT : see CMCDragonkai's answer below, seems good!?
All of the answers here seem to be saying to use the session methods in ways that they were clearly not intended to be used...namely calling session_start() more than once.
The PHP website offers an example SessionHandlerInterface implementation that will work just like existing sessions but without locking the file. Just implementing their example interface fixed my locking issue to allow for concurrent connections on the same session without limiting my ability to add vars to the session. To prevent some race conditions, since the app's session isn't fully stateless, I did have to make a way to save the session mid-request without closing it so that important changes could save immediately after change and less important session vars could just save at the end of the request. See the below example for usage:
Session::start();
echo("<pre>Vars Stored in Session Were:\n");print_r($_SESSION);echo("</pre>");
$_SESSION['one'] = 'one';
$_SESSION['two'] = 'two';
//save won't close session and subsequent request will show 'three'
Session::save();
$_SESSION['three'] = 'three';
If you replace that Session::start() with session_start() and Session::save() with session_write_close(), you'll notice that subsequent requests will never print out the third variable...it will be lost. However, using the SessionHandler (below), no data is lost.
The OOP implementation requires PHP 5.4+. However, you can provide individual callback methods in older versions of PHP. See docs.
namespace {
class Session implements SessionHandlerInterface {
/** #var Session */
private static $_instance;
private $savePath;
public static function start() {
if( empty(self::$_instance) ) {
self::$_instance = new self();
session_set_save_handler(self::$_instance,true);
session_start();
}
}
public static function save() {
if( empty(self::$_instance) ) {
throw new \Exception("You cannot save a session before starting the session");
}
self::$_instance->write(session_id(),session_encode());
}
public function open($savePath, $sessionName) {
$this->savePath = $savePath;
if (!is_dir($this->savePath)) {
mkdir($this->savePath, 0777);
}
return true;
}
public function close() {
return true;
}
public function read($id) {
return (string)#file_get_contents("$this->savePath/sess_$id");
}
public function write($id, $data) {
return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
}
public function destroy($id) {
$file = "$this->savePath/sess_$id";
if (file_exists($file)) {
unlink($file);
}
return true;
}
public function gc($maxlifetime) {
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
unlink($file);
}
}
return true;
}
}
The other answers here present pretty good solutions. As mentioned by #Jon, the trick is to call session_start() again before you want to make changes. Then, when you are done making changes, call session_write_close() again.
As mentioned by #Armel Larcier, the problem with this is that PHP attempts to generate new headers and will likely generate warnings (e.g. if you've already written non-header data to the client). Of course, you can simply prefix the session_start() with "#" (#session_start()), but there's a better approach.
Another Stack Overflow question, provided by #VolkerK reveals the best answer:
session_start(); // first session_start
...
session_write_close();
...
ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
//ini_set('session.use_trans_sid', false); //May be necessary in some situations
ini_set('session.cache_limiter', null);
session_start(); // second session_start
This prevents PHP from attempting to send the headers again. You could even write a helper function to wrap the ini_set() functions to make this a bit more convenient:
function session_reopen() {
ini_set('session.use_only_cookies', false);
ini_set('session.use_cookies', false);
//ini_set('session.use_trans_sid', false); //May be necessary in some situations
ini_set('session.cache_limiter', null);
session_start(); //Reopen the (previously closed) session for writing.
}
Original related SO question/answer: https://stackoverflow.com/a/12315542/114558
After testing out Armel Larcier's work around. Here's my proposed solution to this problem:
ob_start();
session_start();
session_write_close();
session_start();
session_write_close();
session_start();
session_write_close();
session_start();
session_write_close();
if(SID){
$headers = array_unique(headers_list());
$cookie_strings = array();
foreach($headers as $header){
if(preg_match('/^Set-Cookie: (.+)/', $header, $matches)){
$cookie_strings[] = $matches[1];
}
}
header_remove('Set-Cookie');
foreach($cookie_strings as $cookie){
header('Set-Cookie: ' . $cookie, false);
}
}
ob_flush();
This will preserve any cookies that were created prior to working with sessions.
BTW, you may wish to register the above code as function for register_shutdown_function. Make sure to run ob_start() before the function, and ob_flush() inside the function.