I have a shopping cart object that implements serializable, and it works fine on my localhost, but when I upload it to the live server it stops working correctly.
When I add a product to the shopping cart and print_r($_SESSION) I can see the products have been added successfully, but when I refresh the page they disappear. This only happens with objects that implement serializable. These same objects work perfectly on my localhost, which has IDENTICAL code, and it's driving me insane.
Could it be a difference in session handling between php version 5.5 and 5.6?
I am using spl_autoload_register to load my classes if that helps at all (none of them appear in the session as incomplete_class or anything like that).
I also noticed that the session save path is blank on the live version, but is set on my localhost - but the rest of the session functions perfectly - it's only when it contains serializable classes that everything disappears.
Please help before I murder someone...
OK here's the code:
shopping cart class:
class shoppingCart implements Serializable
{
private $products, $error;
function __construct()
{
}
function addProduct($productCode, $quantity = 1)
{
include_once ('include/requests.php');
$productInfo = requestPostData('getProductList');
if (in_array(strtoupper($productCode), $productInfo['results']) == true)
{
if (isset($this->products[strtoupper($productCode)]))
{
$this->products[strtoupper($productCode)] = $this->products[strtoupper($productCode)] + $quantity;
return true;
}
else
{
$this->products[strtoupper($productCode)] = $quantity;
return true;
}
}
else
{
$this->error = 'Product '.$productCode.' could not be found.';
}
}
function editProduct($productCode, $quantity)
{
if (isset($this->products[strtoupper($productCode)]))
{
$this->products[strtoupper($productCode)] = $quantity;
}
}
function removeProduct($productCode)
{
if (isset($this->products[strtoupper($productCode)]))
{
unset($this->products[strtoupper($productCode)]);
}
}
public function getProducts()
{
if (count($this->products) >= 1)
{
return $this->products;
}
else
{
return false;
}
}
public function isInCart($productCode)
{
if (isset($this->products[strtoupper($productCode)]))
{
return true;
}
else
{
return false;
}
}
public function getQuantity($productCode)
{
if (isset($this->products[strtoupper($productCode)]))
{
return $this->products[strtoupper($productCode)];
}
else
{
return 0;
}
}
public function getError()
{
$error = $this->error;
return $error;
}
public function resetError()
{
$this->error = '';
}
public function serialize()
{
$dataArray = array('products' => $this->products, 'error' => $this->error);
return serialize($dataArray);
}
public function unserialize($data)
{
$dataArray = unserialize($data);
$this->products = $dataArray['products'];
$this->error = $dataArray['error'];
}
}
And the code on the page:
if (session_status() !== PHP_SESSION_ACTIVE)
{
session_start();
}
if (!isset($_SESSION['shoppingCart']))
{
$_SESSION['shoppingCart'] = new shoppingCart;
}
if (isset($_POST['cart__add_to_cart']))
{
if (isset($_POST['cart__quantity']))
{
if ($_SESSION['shoppingCart']->addProduct($_POST['cart__add_to_cart'], $_POST['cart__quantity']))
{
header('Location: http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
}
}
else
{
if ($_SESSION['shoppingCart']->addProduct($_POST['cart__add_to_cart']))
{
header('Location: http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
}
}
}
If I print_r($_SESSION) right at the bottom of the page after a product has been added then it appears in the session as it should... But as soon as I refresh it disappears :'(
Related
I'm trying to conditionally load my front-end and admin area code in WordPress plugin, so the file and class that creates admin area will on load on admin side and file and class that is needed to be run on front-end will run only on front-end and won't touch anything on admin area.
I tried to use is_admin() conditional:
if (!is_admin()) {
require_once(plugin_dir_path(dirname(__FILE__)) . 'public/class-public.php');
$this->Public = new Public();
} else {
require_once(plugin_dir_path(dirname(__FILE__)) . 'admin/class-admin.php');
$this->Admin = new Admin();
}
code loading was fine, but AJAX was not working on public side, as AJAX requests bound to either wp_ajax_ or wp_ajax_nopriv_ actions are executed in the WP Admin context. So I decided to create my own isAdmin() function:
public static function isAdmin() {
$currentUrl = set_url_scheme(
sprintf(
'http://%s%s',
$_SERVER['HTTP_HOST'],
$_SERVER['REQUEST_URI']
)
);
$adminUrl = strtolower(admin_url());
$referrer = strtolower(wp_get_referer());
if (strpos($currentUrl, $adminUrl) === 0) {
if (strpos($referrer, $adminUrl) === 0) {
return true;
} else {
if (function_exists('wp_doing_ajax')) {
return !wp_doing_ajax();
} else {
return !(defined('DOING_AJAX') && DOING_AJAX);
}
}
} else {
if (!defined('REST_REQUEST') || !REST_REQUEST) {
return false;
}
return (isset($_REQUEST['context']) && $_REQUEST['context'] === 'edit');
}
}
code loading was still fine, but now AJAX was working on the public side and not working on the admin side.
So, how can I prevent loading public code on admin area code and vice versa with AJAX working on both sides?
I managed to resolve this issue by also checking for public interface. I created new function isPublic() to check if it is public. So here is my final code:
if ($this->isPublic()) {
require_once(plugin_dir_path(dirname(__FILE__)) . 'public/class-public.php');
$this->Public = new Public();
} elseif ($this->isAdmin()) {
require_once(plugin_dir_path(dirname(__FILE__)) . 'admin/class-admin.php');
$this->Admin = new Admin();
}
and here are helper isPublic() and isAdmin() functions:
public static function isAdmin() {
if (function_exists('is_admin') && is_admin()) {
return true;
} else {
if (strpos($_SERVER['REQUEST_URI'], 'wp-admin') !== false) {
return true;
} else {
return false;
}
}
}
public static function isPublic() {
if (function_exists('is_admin') && is_admin()) {
if (function_exists('wp_doing_ajax') && wp_doing_ajax()) {
return true;
} else {
return false;
}
} else {
if (strpos($_SERVER['REQUEST_URI'], 'wp-admin') !== false) {
if (strpos($_SERVER['REQUEST_URI'], 'admin-ajax.php') !== false) {
return true;
} else {
return false;
}
} else {
return true;
}
}
}
I have multiple nested methods inside a PHP class. What I want to do is, based on certain circumstances, I want to exit from NOT JUST the current method, but 2 above it, then the leftover code should continue running. Now the issue with die(), exit() is that they end the full script and I don't want that. I simply want to go a few methods up and continue the script.
Of course, there's the old school method of returning a value in each method and check if it's false for example. But that way I'll have to write tons of additional code if I have like 50 nested methods. Here's what I have right now - it's a very basic usage here, I'm using it in a lot more complicated scenarios (using PHP 7.2.4):
class Sites
{
public function __construct()
{
$this->fn1();
}
public function fn1()
{
$fn2 = $this->fn2();
echo 'I want this to be displayed no matter what!';
}
public function fn2()
{
$fn3 = $this->fn3();
if ($fn3)
{
return true;
}
}
public function fn3()
{
$fn4 = $this->fn4();
if ($fn4)
{
return true;
}
}
public function fn4()
{
$random = rand(1, 100);
if ($random > 50)
{
return true;
}
else
{
// I want to exit/break the scirpt to continue running after
// the $fn2 = $this->fn2() call in the $this->fn1() function.
exit();
echo "This shouldn't be displayed.";
}
}
}
Just as mentioned in the code comments, I want to break the script - if the random number is below 50 and go back to fn1() but continue executing the echo function there.
Is this possible somehow? Please let me know if you need more information and I'll provide.
You can use Exceptions to do this, not particularly elegant, but this should do what your after, replace these methods...
public function fn1()
{
try {
$fn2 = $this->fn2();
}
catch ( Exception $e ) {
}
echo 'I want this to be displayed no matter what!';
}
public function fn4()
{
$random = rand(1, 100);
if ($random > 50)
{
return true;
}
else
{
// I want to exit/break the scirpt to continue running after
// the $fn2 = $this->fn2() call in the $this->fn1() function.
//exit();
throw new Exception();
echo "This shouldn't be displayed.";
}
}
How about regular function call with a flag?
class Sites
{
protected $flag = false;
public function __construct()
{
$this->fn1();
}
public function fn1()
{
if ($this->flag) {
$this->flag = true;
} else {
echo 'I want this to be displayed no matter what!';
$fn2 = $this->fn2();
}
}
public function fn2()
{
$fn3 = $this->fn3();
if ($fn3)
{
return true;
}
}
public function fn3()
{
$fn4 = $this->fn4();
if ($fn4)
{
return true;
}
}
public function fn4()
{
$random = rand(1, 100);
if ($random > 50)
{
return true;
}
else
{
// I want to exit/break the scirpt to continue running after
// the $fn2 = $this->fn2() call in the $this->fn1() function.
//exit();
$this->flag = true;
$this->fn1();
exit();
echo "This shouldn't be displayed.";
}
}
}
$sites = new Sites;
I hope this helps!
Hy,
i started learning PHP and i created a simple MVC Style Codebase.
The Script just generates a random number and displays this numer. I also write a function to display the number shown before but it does not work. The value is empty. Can you help me out, i have no clue whats wrong and there is no php error thrown.
view.php
<?php
class View
{
private $model;
private $view;
public function __construct()
{
$this->model = new Model();
}
public function output()
{
echo 'Current Entry: ';
echo $this->model->getData();
echo '<br />';
echo 'Update';
echo '<br />';
echo 'Last';
}
public function getModel()
{
return $this->model;
}
}
controller.php
<?php
class Controller
{
private $model;
private $view;
public function __construct($view)
{
$this->view = $view;
$this->model = $this->view->getModel();
}
public function get($request)
{
if (isset($request['action']))
{
if ($request['action'] === 'update')
{
for ($i = 0; $i<6; $i++)
{
$a .= mt_rand(0,9);
}
$this->model->setData($a);
}
elseif ($request['action'] === 'preview')
{
$this->model->setLast();
}
else
{
$this->model->setData('Wrong Action');
}
}
else
{
$this->model->setData('Bad Request');
}
}
}
model.php
<?php
class Model
{
private $data;
private $last;
public function __construct()
{
$this->data = 'Default';
}
public function setData($set)
{
if ( ! (($set == 'Wrong Action') && ($set == 'Bad Request')))
{
$this->last = $this->data;
}
$this->data = $set;
}
public function getData()
{
return $this->data;
}
public function setLast()
{
$this->data = $this->last;
}
public function getLast()
{
return $this->last;
}
}
index.php
<?php
require_once 'controller.php';
require_once 'view.php';
require_once 'model.php';
$view = new View();
$controller = new Controller($view);
if (isset($_GET) && !empty($_GET)) {
$controller->get($_GET);
}
$view->output();
Are there any other, bad mistakes in the Script?
Any input very welcome! :)
The problem with your code is that PHP does not preserve variable values between requests, therefore, when you set your $model->last value here:
$this->last = $this->data;
It gets reset on your next request.
You may want to store $last value in a session or a cookie instead. Something like:
$_SESSION['last'] = $this->data;
And then when you are instantiating your model you could initialize it with a value stored in a session if available:
index.php - add session_start() at the beginning
model.php:
public function __construct()
{
$this->data = isset($_SESSION['last']) ? $_SESSION['last'] : 'Default';
}
public function setData($set)
{
$this->data = $set;
if ( ! (($set == 'Wrong Action') && ($set == 'Bad Request')))
{
$_SESSION['last'] = $this->data;
}
}
controller.php
elseif ($request['action'] === 'preview')
{
//Remove this
//$this->model->setLast();
}
I have a system that was designed to do a kind of cascading - get the sequence of methods called upon success of the previous condition.
The example is the below code, which I presume it's not a best practice for doing this, so would be great if I could get some suggestions to refactor this, probably using a design pattern or a different than this system.
<?php
class Model
{
public function isOk()
{
return true;
}
}
class OtherClass
{
public function isOk()
{
return true;
}
}
class AnotherClass
{
public function verifies()
{
return true;
}
}
class Sequence
{
public function fire()
{
$model = new Model();
if($model->isOk()) {
$otherclass = new OtherClass();
if($otherclass->isOk()) {
$anotherclass = new AnotherClass();
if($anotherclass->verifies()) {
echo "We're done with the sequence.";
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
}
$sequence = new Sequence();
echo $sequence->fire();
?>
I would avoid deep nesting of if/else statements to enhance the readability. One way is to use early return:
class Test1
{
public function isOk()
{
echo 'Test1';
return true;
}
}
class Test2
{
public function isOk()
{
echo 'Test2';
return true;
}
}
class Sequence
{
public function fire()
{
$test1 = new Test1();
if (!$test1->isOk()) {
return false;
}
$test2 = new Test2();
if (!$test2->isOk()) {
return false;
}
echo "We're done with the sequence.";
return true;
}
}
If you need it more dynamically you could use call_user_func or call_user_func_array.
class Sequence
{
protected $sequence = array(
array('Test1', 'isOk'),
array('Test2', 'isOk'),
);
public function fire()
{
foreach ($this->sequence as $callback) {
if (!call_user_func(array(new $callback[0], $callback[1]))) {
return false;
}
}
echo "We're done with the sequence.";
return true;
}
}
So I have a class I'm working on to manage PHP sessions, here's the class:
class SessionManagement {
public static function sessionStarted() {
if(session_id() == '') {
return false;
} else {
return true;
}
}
public static function sessionExists($session) {
if(sessionStarted() == false) {
session_start();
}
if(isset($_SESSION[$session])) {
return true;
} else {
return false;
}
}
public static function setSession($session, $value) {
if(sessionStarted() != true) {
session_start();
}
$_SESSION[$session] = $value;
if(sessionExists($session) == false) {
throw new Exception('Unable to Create Session');
}
}
public static function getSession($session) {
if(isset($_SESSION[$session])) {
return $_SESSION[$session];
} else {
throw new Exception('Session Does Not Exist');
}
}
}
Now trying this...
try {
SessionManagement::setSession('Foo', 'Bar');
echo SessionManagement::sessionStarted();
echo SessionManagement::getSession('Foo');
echo SessionManagement::sessionExists('Foo');
} catch(Exception $e) {
echo $e->getMessage();
}
...produces no output...I'm not sure where we're breaking here...any helpful eyes is greatly appreciated...
Unlike other OO languages, like C++, in your class PHP needs to know that the static methods called are from this object. For an instantiated class, that would be through $this, and in your case, static methods, this is done via self:
class SessionManagement {
public static function sessionStarted() {
if(session_id() == '') {
return false;
} else {
return true;
}
}
public static function sessionExists($session) {
if(self::sessionStarted() == false) {
session_start();
}
if(isset($_SESSION[$session])) {
return true;
} else {
return false;
}
}
public static function setSession($session, $value) {
if(self::sessionStarted() != true) {
session_start();
}
$_SESSION[$session] = $value;
if(self::sessionExists($session) == false) {
throw new Exception('Unable to Create Session');
}
}
public static function getSession($session) {
if(isset($_SESSION[$session])) {
return $_SESSION[$session];
} else {
throw new Exception('Session Does Not Exist');
}
}
}
Prepending self:: to all internal calls to the SessionManagement static methods should solve your problem.