Order of execution in CodeIgniter - php

class Index extends CI_Controller {
private $data = array();
private $content_data = array();
public function __construct() {
parent::__construct();
if (isset($_GET['m2w'])) {
$stw = switch_to_web();
}
if (isset($_GET['w2m'])) {
$stm = switch_to_mobile();
}
// load mobile or desktop view
}
// Called within an helper function
function switch_to_web() {
return set_cookie('load-web', 'true', '86500');
}
function switch_to_mobile() {
return delete_cookie('load-web');
}
function is_mobile() {
// return true;
$CI = & get_instance();
if ($CI->input->cookie('load-web'))
return false;
$CI->load->library('user_agent');
if ($CI->agent->is_mobile()) {
return true;
}else
return false;
}
I have the above block of code to determine if to load the mobile or web view.
Expected order of execution:
if m2w is set, set the load-web cookie (this is done before the is_mobile function is called
is_mobile function sees the load-web cookie has been set and loads the desktop version
Actual order of execution:
if m2w is set, load-web cookie is called to be set, however the is_mobile function doesn't see it as set hence the desktop version is not loaded
the cookie is set after the is_mobile function has returned true, i check my browser cookies and observed that the cookie was actually set but not at when expected
What am I not getting right?

CI's cookies uses the native setcookie() method of PHP. Cookies requires to be sent in the HTTP headers before being available in the native $_COOKIE variable.
From the PHP doc :
Once the cookies have been set, they can be accessed on the next page load with the $_COOKIE or $HTTP_COOKIE_VARS arrays.
Here's the cookie() method from CI :
function cookie($index = '', $xss_clean = FALSE)
{
// Simply fetch from the $_COOKIE array and do XSS_Clean if needed.
return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
}
In short, you set your cookie correctly, but it won't be available until your next request. It's all because the new value isn't in $_COOKIE array.
You have multiple alternatives to fix that.
You could extend the CI_Input class and modify the set_cookie method to also add your new value to the $_COOKIE array.
You could edit your is_mobile function to also check for the $_GET['m2w'] value. (Not just the cookie.)
Personalty, I think the 2nd solution is the cleanest and easiest to do, but I don't know the full scope of your project.
Hope this helps!

Related

Laravel allow route to be accessible only from another route

I have /signup/select-plan which lets the user select a plan, and /signup/tos which displays the terms of services. I want /signup/tos to be only accessible from /signup/select-plan. So if I try to go directly to /signup/tos without selecting a plan, I want it to not allow it. How do I go about this?
In the constructor, or the route (if you are not using contructors), you can check for the previous URL using the global helper url().
public function tos() {
if ( !request()->is('signup/tos') && url()->previous() != url('signup/select-plan') ) {
return redirect()->to('/'); //Send them somewhere else
}
}
In the controller of /signup/tos which returns the tos view just add the following code:
$referer = Request::referer();
// or
// $referer = Request::server('HTTP_REFERER');
if (strpos($referer,'signup/select-plan') !== false) {
//SHOW THE PAGE
}
else
{
dd("YOU ARE NOT ALLOWED")
}
What we are doing here is checking the HTTP referrer and allowing the page access only if user comes from select-plan
You are need of sessions in laravel. You can see the following docs to get more info: Laravel Sessions
First of all you need to configure till how much time you want to have the session variable so you can go to your directory config/sessions.php and you can edit the fields 'lifetime' => 120, also you can set expire_on_close by default it is being set to false.
Now you can have following routes:
Route::get('signup/select-plan', 'SignupController#selectPlan');
Route::post('signup/select-token', 'SignupController#selectToken');
Route::get('signup/tos', 'SignupController#tos');
Route::get('registered', 'SignupController#registered');
Now in your Signupcontroller you can have something like this:
public function selectPlan()
{
// return your views/form...
}
public function selectToken(Request $request)
{
$request->session()->put('select_plan_token', 'value');
return redirect('/signup/tos');
}
Now in signupController tos function you can always check the session value and manipulate the data accordingly
public function tos()
{
$value = $request->session()->get('select_plan_token');
// to your manipulation or show the view.
}
Now if the user is registered and you don't need the session value you can delete by following:
public function registered()
{
$request->session()->forget('select_plan_token');
// Return welcome screen or dashboard..
}
This method will delete the data from session. You can manipulate this. You won't be able to use in tos function as you are refreshing the page and you want data to persist. So its better to have it removed when the final step or the nextstep is carried out. Hope this helps.
Note: This is just the reference please go through the docs for more information and implement accordingly.

Trying to set a session variable with Zend_Session_Namespace, everytime NULL

I'm running an application where in the controller I'm trying to set session variables using Zend_Session and Zend_Session_Namespace:
Bootstrap.php
protected function _initSession()
{
Zend_Session::start();
}
SomeController.php
protected function updateQuestionViewsTotal($question)
{
$userSession = new Zend_Session_Namespace('QA_Session');
if (! is_array($userSession->questionViews)) {
$userSession->questionViews = array();
}
// create session array to contain the questions this
// user has viewed.
if(array_search($question->id, $userSession->questionViews) === false) {
$question->views_total++;
$question->save();
}
// ensure that this page is in the array
array_push($userSession->questionViews, $question->id);
$userSession->questionViews = array_unique($userSession->questionViews);
}
As you can see from above, I have within one of my controllers a method with an attempt to use session variables via Zend_Session_Namespace.
However, when I insert a var_dump on the second page load (refresh):
protected function updateQuestionViewsTotal($question)
{
$userSession = new Zend_Session_Namespace('QA_Session');
var_dump($userSession));
if (! is_array($userSession->questionViews)) {
$userSession->questionViews = array();
}
..Please note: this is AFTER I've run it once, so I'm expecting that the session variable has been set. Anyway on every occasion, it is NULL. So it would seem that the variable isn't being written to $_SESSION? What am I doing wrong?

How to use a SOAP API with login and session codeigniter

I have a set of soap APIs which can perform actions like login,logout,keepalive,access other several resources.Inorder to access the other resources,I have to pass a session id which I got from the login api.The session gets time out in 5minutes.
I am confused on how to make this working.
I am using codeigniter for my project,and I have built one library with the set of soap api requests defined in it.
class Soap_api
{
function __construct()
{
define("UID", "myuser");
define("PWD", "34rf3a45575");
define("API_ENDPOINT", "http://uat-api.testingsoapapi.in/services/smp");
define("PRODUCT_CODE", "24");
$resp = $this->keepAliveLib();
if($resp['ResponseCode'] == '0')
{
define("SessionID",$resp['SessionID']);
}
}
function keepAliveLib()
{
$resp = $this->login();
return $resp;
}
function one
{
//This function needs the sessionID receieved from login function
}
function two
{
//This function needs the sessionID receieved from login function
}
So when ever any of the functions from this class is accessed,the constructor calls the keepAliveLib which calls the login function residing in this class and return the session id to the constructor function and set it as global constant sessionID .So the function which I called will be using the that session ID which is made as a constant.
Is this the standard way of calling APIs which relay on sessions?The login function is called when ever a function is called and creates a different session ID.There is a function keepAlive in the library which can be used to maintaining the session,but instead of using keepAlive , Im logging each time a function in this is accessed.
Is there anything wrong in this flow?Can this be done in some other ways?
ok i'm probably not understanding this fully but would this work?
$resp = $this->keepAliveLib();
if($resp['ResponseCode'] == '0') {
$this->sessionid = $resp['SessionID']); }
else { // fail gracefully }
$this->sessionid is now available to any method in the controller.

how do i check if a method has already been called?

I have a class that loads certain parts of my page. What I want to do is this
index.php
$obj->load('admin');
In my class in the constructor I have a check to see if the URI contains the segment /admin/ and if it does then it auto loads the admin page:
$this->load('admin');
now both work fine, I just realized that if I have both entries in my project then it will load the admin initialization twice.
Is there a way for my to check if the load() method has already been called and if the parameter is == to admin?
One way is to use static cache:
function load($what) {
static $loaded = array();
if (!isset($loaded[$what])) {
// Load..
// Mark as loaded
$loaded[$what] = true;
}
}
class Foo {
private $loaded = false;
function someFunc() {
$this->loaded = true;
}
function loaded() {
return $this->loaded;
}
}
May be Keep a object member variable variable and keep a flag when the load admin is called for first time. Check the flag for during the second call.
Easiest - create additional variable called say '$invokedAlready'. Set it first to 'false'. In your method place this:
if ( $invokedAlready )
{
// do sth
}
else
{
// first invocaion
// do sth else
$invokedFirst = true;
}

How should I implement lazy session creation in PHP?

By default, PHP's session handling mechanisms set a session cookie header and store a session even if there is no data in the session. If no data is set in the session then I don't want a Set-Cookie header sent to the client in the response and I don't want an empty session record stored on the server. If data is added to $_SESSION, then the normal behavior should continue.
My goal is to implement lazy session creation behavior of the sort that Drupal 7 and Pressflow where no session is stored (or session cookie header sent) unless data is added to the $_SESSION array during application execution. The point of this behavior is to allow reverse proxies such as Varnish to cache and serve anonymous traffic while letting authenticated requests pass through to Apache/PHP. Varnish (or another proxy-server) is configured to pass through any requests without cookies, assuming correctly that if a cookie exists then the request is for a particular client.
I have ported the session handling code from Pressflow that uses session_set_save_handler() and overrides the implementation of session_write() to check for data in the $_SESSION array before saving and will write this up as library and add an answer here if this is the best/only route to take.
My Question: While I can implement a fully custom session_set_save_handler() system, is there an easier way to get this lazy session creation behavior in a relatively generic way that would be transparent to most applications?
Well, one option would be to use a session class to start/stop/store data in the session. So, you could do something like:
class Session implements ArrayAccess {
protected $closed = false;
protected $data = array();
protected $name = 'mySessionName';
protected $started = false;
protected function __construct() {
if (isset($_COOKIE[$this->name])) $this->start();
$this->data = $_SESSION;
}
public static function initialize() {
if (is_object($_SESSION)) return $_SESSION;
$_SESSION = new Session();
register_shutdown_function(array($_SESSION, 'close'));
return $_SESSION;
}
public function close() {
if ($this->closed) return false;
if (!$this->started) {
$_SESSION = array();
} else {
$_SESSION = $this->data;
}
session_write_close();
$this->started = false;
$this->closed = true;
}
public function offsetExists($offset) {
return isset($this->data[$offset]);
}
public function offsetGet($offset) {
if (!isset($this->data[$offset])) {
throw new OutOfBoundsException('Key does not exist');
}
return $this->data[$offset];
}
public function offsetSet($offset, $value) {
$this->set($offset, $value);
}
public function offsetUnset($offset) {
if (isset($this->data[$offset])) unset($this->data[$offset]);
}
public function set($key, $value) {
if (!$this->started) $this->start();
$this->data[$key] = $value;
}
public function start() {
session_name($this->name);
session_start();
$this->started = true;
}
}
To use, at the start of your script call Session::initialize(). It will replace $_SESSION with the object, and setup the lazy loading. Afterward, you can just do
$_SESSION['user_id'] = 1;
If the session isn't started, it will be, and the user_id key would be set to 1. If at any point you wanted to close (commit) the session, just call $_SESSION->close().
You'll probably want to add some more session management functions (such as destroy, regenerate_id, the ability to change the name of the session, etc), but this should implement the basic functionality you're after...
It's not a save_handler, it's just a class to manage your sessions. If you really wanted to, you could implement ArrayAccess in the class, and on construct replace $_SESSION with that class (The benefit of doing that, is that way legacy code can still use session as they used to without calling $session->setData()). The only downside is that I'm not sure if the serialization routine that PHP uses would work properly (You'd need to put back the array into $_SESSION at some point... Probably with a register_shutdown_function()...
I have developed a working solution to this problem that uses session_set_save_handler() and a set of custom session storage methods that check for content in the $_SESSION array before writing out session data. If there is no data to write for the session, then header('Set-Cookie:', true); is used to prevent PHP's session-cookie from being sent in the response.
The latest version of this code as well as documentation and examples are available on GitHub. In the code below, the important functions that make this work are lazysess_read($id) and lazysess_write($id, $sess_data).
<?php
/**
* This file registers session save handlers so that sessions are not created if no data
* has been added to the $_SESSION array.
*
* This code is based on the session handling code in Pressflow (a backport of
* Drupal 7 performance features to Drupal 6) as well as the example code described
* the PHP.net documentation for session_set_save_handler(). The actual session data
* storage in the file-system is directly from the PHP.net example while the switching
* based on session data presence is merged in from Pressflow's includes/session.inc
*
* Links:
* http://www.php.net/manual/en/function.session-set-save-handler.php
* http://bazaar.launchpad.net/~pressflow/pressflow/6/annotate/head:/includes/session.inc
*
* Caveats:
* - Requires output buffering before session_write_close(). If content is
* sent before shutdown or session_write_close() is called manually, then
* the check for an empty session won't happen and Set-Cookie headers will
* get sent.
*
* Work-around: Call session_write_close() before using flush();
*
* - The current implementation blows away all Set-Cookie headers if the
* session is empty. This basic implementation will prevent any additional
* cookie use and should be improved if using non-session cookies.
*
* #copyright Copyright © 2010, Middlebury College
* #license http://www.gnu.org/copyleft/gpl.html GNU General Public License (GPL), Version 3 or later.
*/
/*********************************************************
* Storage Callbacks
*********************************************************/
function lazysess_open($save_path, $session_name)
{
global $sess_save_path;
$sess_save_path = $save_path;
return(true);
}
function lazysess_close()
{
return(true);
}
function lazysess_read($id)
{
// Write and Close handlers are called after destructing objects
// since PHP 5.0.5.
// Thus destructors can use sessions but session handler can't use objects.
// So we are moving session closure before destructing objects.
register_shutdown_function('session_write_close');
// Handle the case of first time visitors and clients that don't store cookies (eg. web crawlers).
if (!isset($_COOKIE[session_name()])) {
return '';
}
// Continue with reading.
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
return (string) #file_get_contents($sess_file);
}
function lazysess_write($id, $sess_data)
{
// If saving of session data is disabled, or if a new empty anonymous session
// has been started, do nothing. This keeps anonymous users, including
// crawlers, out of the session table, unless they actually have something
// stored in $_SESSION.
if (empty($_COOKIE[session_name()]) && empty($sess_data)) {
// Ensure that the client doesn't store the session cookie as it is worthless
lazysess_remove_session_cookie_header();
return TRUE;
}
// Continue with storage
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
if ($fp = #fopen($sess_file, "w")) {
$return = fwrite($fp, $sess_data);
fclose($fp);
return $return;
} else {
return(false);
}
}
function lazysess_destroy($id)
{
// If the session ID being destroyed is the one of the current user,
// clean-up his/her session data and cookie.
if ($id == session_id()) {
global $user;
// Reset $_SESSION and $user to prevent a new session from being started
// in drupal_session_commit()
$_SESSION = array();
// Unset the session cookie.
lazysess_set_delete_cookie_header();
if (isset($_COOKIE[session_name()])) {
unset($_COOKIE[session_name()]);
}
}
// Continue with destruction
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
return(#unlink($sess_file));
}
function lazysess_gc($maxlifetime)
{
global $sess_save_path;
foreach (glob("$sess_save_path/sess_*") as $filename) {
if (filemtime($filename) + $maxlifetime < time()) {
#unlink($filename);
}
}
return true;
}
/*********************************************************
* Helper functions
*********************************************************/
function lazysess_set_delete_cookie_header() {
$params = session_get_cookie_params();
if (version_compare(PHP_VERSION, '5.2.0') === 1) {
setcookie(session_name(), '', $_SERVER['REQUEST_TIME'] - 3600, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
}
else {
setcookie(session_name(), '', $_SERVER['REQUEST_TIME'] - 3600, $params['path'], $params['domain'], $params['secure']);
}
}
function lazysess_remove_session_cookie_header () {
// Note: this implementation will blow away all Set-Cookie headers, not just
// those for the session cookie. If your app uses other cookies, reimplement
// this function.
header('Set-Cookie:', true);
}
/*********************************************************
* Register the save handlers
*********************************************************/
session_set_save_handler('lazysess_open', 'lazysess_close', 'lazysess_read', 'lazysess_write', 'lazysess_destroy', 'lazysess_gc');
While this solution works and is mostly transparent to applications including it, it requires rewriting the entire session-storage mechanism rather than relying on the built-in storage mechanisms with a switch to save or not.
I created a lazy session proof of concept here:
it uses the native php session handler and _SESSION array
it only starts the session if a cookie has been sent or
it starts the session if something has been added the $_SESSION array
it removes the session if a session is started and $_SESSION is empty
Will extend it in the next days:
https://github.com/s0enke/php-lazy-session
this topic is under discussion for a future php version
https://wiki.php.net/rfc/session-read_only-lazy_write

Categories