PHP __PHP_Incomplete_Class Объект с моими данными $ _SESSION - PullRequest
38 голосов
/ 06 января 2010

У меня есть настройка сайта, которая при загрузке страницы превращает все пользовательские строки в объекты SafeString. Для тех, кто не знаком с SafeString, он в основном заставляет пользователя выводить очищенные данные, предотвращая XSS и все такое.

В любом случае, есть проблема. Мой массив $ _SESSION заполняется __PHP_Incomplete_Class Object. Из того, что я прочитал, это происходит из-за того, что класс не инициализируется перед сеансом, а затем сохраняется объекты класса в сеансе.

Вот мой код:

require_once __WEBROOT__ . '/includes/safestring.class.php'; 

$temp = array
(
   &$_SERVER, &$_GET, &$_POST, &$_COOKIE,
   &$_SESSION, &$_ENV, &$_REQUEST, &$_FILES,
   &$HTTP_SERVER_VARS, &$HTTP_GET_VARS,
   &$HTTP_POST_VARS, &$HTTP_COOKIE_VARS,
   &$HTTP_POST_FILES, &$HTTP_ENV_VARS
); 

function StringsToSafeString(&$array)
{
   foreach ($array as $key => $value)
   {
      if (is_string($array[$key]))
      {
         $array[$key] = new SafeString($value);
      } 

      if (is_array($array[$key]))
      {
         StringsToSafeString($array[$key]);
      }
   }
}

StringsToSafeString($temp);

unset($temp);

Я не могу придумать способ переписать это, чтобы решить проблему: /

Есть идеи?

Ответы [ 11 ]

63 голосов
/ 06 января 2010

Когда вы получаете доступ к $_SESSION, вы не просто изменяете текущую копию сценария данных, считанных из сеанса, но и записываете объекты SafeString обратно в активный сеанс.

Но помещать пользовательские объекты в сессию довольно сложно, и я бы старался этого избегать. Чтобы это сделать, вы должны определить соответствующий класс перед вызовом session_start; если вы этого не сделаете, обработчик сеанса PHP не будет знать, как десериализовать экземпляры этого класса, и вы получите __PHP_Incomplete_Class Object.

Так что избегайте засорять сессию. Если вы должны использовать этот подход, скопируйте данные из $_SESSION в локальный массив $mysession. Однако я должен сказать, что я думаю, что сама идея SafeString опасна и неосуществима; Я не думаю, что этот подход когда-либо будет водонепроницаемым. Является ли строка исходного текста «безопасной», это не имеет отношения к тому, откуда она взялась, это свойство того, как вы ее кодируете для целевого контекста.

Если вы получаете другую текстовую строку из другого источника, такого как база данных или файл, или рассчитываетесь внутри самого скрипта, она требует точно такой же обработки, что и строка, полученная от пользователя: она должна быть htmlspecialchars Под ред. Вам все равно придется написать этот побег; безопасная строка ничего не дает. Если вам нужно отправить строку в другом формате назначения, вам потребуется другой escape.

Вы не можете заключить все проблемы с обработкой строк в один удобный блок и никогда больше не думать о них; это просто не то, как работают строки.

25 голосов
/ 06 сентября 2015

Я знаю, что прошло много лет с тех пор, как об этом спрашивали, но я публикую свой ответ, потому что ни один из ответов выше не объясняет ОП, что на самом деле не так.

PHP сериализует свои сессии, используя встроенные методы serialize и unserialize. serialize PHP имеет возможность сериализовать объекты PHP (также известные как экземпляры классов) и преобразовывать их в строку. Когда вы unserialize эти строки, он преобразует их обратно те же классы с этими значениями. Классы, которые имеют некоторые частные свойства и хотят кодировать / декодировать это или делать что-то сложное в своей сериализации / десериализации, реализуют класс Serializable и добавляют serialize и unserialize методов к классу.

Когда PHP unserialize пытается десериализовать объект класса, но имя класса не объявляется / не требуется, вместо выдачи предупреждения или выдачи Exception, он преобразует его в объект __PHP_Incomplete_Class.

Если вы не хотите, чтобы ваши объекты сеанса конвертировались в __PHP_Incomplete_Class, вы можете сделать это, либо запросив файлы классов перед вызовом session_start, либо зарегистрировав функцию автозагрузки.

6 голосов
/ 06 января 2010

Вам просто нужно включить safestring.class.php перед вызовом session_start(), когда вы хотите прочитать объекты SafeString из $_SESSION переменной:

<?php

require_once __WEBROOT__ . '/includes/safestring.class.php';    
session_start();

print_r($_SESSION);

и да, если вы используете PHP-фреймворк, который (скорее всего) вызывает session_start() внутри, заранее убедитесь, что вы require_once файл класса (используйте хуки или любые механизмы, которые предоставляет фреймворк).

3 голосов
/ 08 ноября 2012

Я только что имел дело с чем-то вроде этого. У меня ушло несколько часов, чтобы, наконец, найти, как мой заказ облажался.

У меня был файл, вызываемый асинхронно.

myFile.php

этот файл содержал следующее ..

$result = include ("myOtherFile.php");
return $result;

Myotherfile.php имеет что-то вроде этого

require_once "lib/myClassLibs.php";
require_once "webconfig.php";

в webconfig.php был вызов session_start ().

В lib / myClassLibs есть вся информация о классе init. Если вы проверите перед вызовом webconfig, вы увидите, что класс доступен.

Если вы проверите перед вызовом webconfig, вы также увидите, что сессия уже началась. Если вы проверите перед lib / myClassLibs.php, вы увидите, что сессия уже началась.

Проверяя myFile.php перед тем, как включить MyOtherFile.php, вы обнаружите, что сессия еще не началась.

Это представлял собой устаревший код, который работал последние 8 лет, и я не стал его трогать. Я вытащил включения из "MyOtherFile.php". Теперь мои сеансы синхронизируются правильно.

3 голосов
/ 14 июля 2010

Ответ Лукмана правильный. Но вы уже упоминали об этом в своем вопросе, так что, очевидно, по какой-то причине вы не можете создать экземпляр класса до начала сеанса.

Вы можете проверить, запускаются ли сеансы автоматически в конфигурации php: http://www.php.net/manual/en/session.configuration.php#ini.session.auto-start

Если они есть, и вы не можете помочь, вы можете проверить, можете ли вы автоматически загружать свои классы до этого: http://php.net/manual/en/language.oop5.autoload.php

Если ничего не помогает, вы все равно можете сериализовать объекты, прежде чем сохранять их в сеансе, и десериализовать их каждый раз, когда вы получаете их: http://php.net/manual/en/function.serialize.php

Я не вижу в вашем коде, где вы храните переменные, но это будет что-то вроде

$mystuff = unserialize($_SESSION["mystuff"]);
$mystuff->dostuff();
$_SESSION["mystuff"] = serialize($mystuff);

Обязательно загрузите определение класса, прежде чем десериализовать свои переменные

$ 2с, * -Pike

2 голосов
/ 06 мая 2015

Я решил проблему, используя функции json_encode и json_decode.

Здесь я хотел присвоить значение сеансу.

$user_json              = json_encode($user);
$_SESSION['user']           = $user_json;

Здесь я показываю пользователю после декодирования JSON

session_start();

$user_json= $_SESSION['user'];
$user = json_decode($user_json);

Это решает мою проблему, но я не уверен в производительности или безопасности. Я их не проверял.

1 голос
/ 22 декабря 2018

Я знаю, что это действительно старый вопрос, но я столкнулся с этой проблемой. После дополнительных исследований и экспериментов я придумал, что я считаю приемлемой альтернативой хранению уроков в сессии. Это может быть немного хакерским, но работает для моего текущего проекта.

ПРИМЕЧАНИЕ: этот обходной путь работает для меня, потому что я начинаю сеанс, когда пользователь входит в систему и не хочет включать все возможные классы, которые пользователь может или не может встретить во время сеанса. Включение всех классов не кажется практичным или эффективным (но, может быть, это не лучше ???).

Во-первых, мой базовый класс содержит следующий код, который автоматически заполняет атрибуты объекта из данного массива.

class BaseClass {

    public function __construct($properties=[]){
        if (!empty($properties)) {
            array_walk($properties, function ($val, $key) {
                $this->fromArray($key, $val);
            });
        }
    }

    public function fromArray($property, $value){
        return (property_exists($this, $property)) ? $this->$property = $value : null;
    }

    public function toArray(){
        return get_object_vars($this);
    }

}

Обходной путь: Я использую метод toArray () для преобразования экземпляра класса в массив перед его передачей в сеанс, а затем создаю новый экземпляр класса при извлечении его из сеанса.

$_SESSION['user'] = $userInstance->toArray();

// ... do stuff ...

$userInstance = new User($_SESSION['user']);

Это также очень удобно для записи классов в базу данных и преобразования в JSON. И то, и другое упрощается при работе с массивом PHP.

Как я уже говорил выше, это может или не может быть наиболее эффективным способом решения этой проблемы. Также возникает вопрос: «Должен ли я использовать классы PHP, если я просто собираюсь преобразовывать в массивы?»

1 голос
/ 17 декабря 2013

Я решил эту проблему, добавив функцию __ autoload вверху моего php-файла. Так это выглядит так:

<?php 
require_once("path/to/include.inc");

//Needed for serialization/deserialization
function __autoload($class_name) {
    include "path/to/". $class_name . '.php';
}

В PHP 5 эта функция не нужна, но я застрял, пока не использовал эту функцию. Надеюсь, это поможет кому-то еще!

0 голосов
/ 15 марта 2019

Я столкнулся с той же проблемой, и решение было вдохновлено ответом @bobince

Чтобы иметь возможность сделать это, вы должны определить соответствующий класс перед вызовом session_start

Сначала моя сессия была настроена так:

$_SESSION["customer"] = $customerObj;

Затем перед вызовом session_start() я должен сначала загрузить или определить класс, импортировав его, а затем вызвать session_start() сразу после

require 'entity/Customer.php';

ob_start();
session_start();
$customer = new Customer();
if (isset($_SESSION["customer"]))
{
    $customer = $_SESSION["customer"];
    echo $customer->getCustomerName();
}
0 голосов
/ 21 февраля 2017

Моя ошибка в том, что я установил для session.auto_start значение on. Затем сеанс будет инициализирован перед вызовом любой строки кода (включая автозагрузчик).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...