php incomplete class object
unserialize
(PHP 4, PHP 5, PHP 7, PHP 8)
unserialize — Создаёт PHP-значение из хранимого представления
Описание
unserialize() принимает одну сериализованную переменную и конвертирует её обратно в значение PHP.
Если вам нужно десериализовать данные из внешних источников, используйте функцию hash_hmac() для проверки этих данных. Убедитесь, что эти данные кроме вас никто не изменял.
Список параметров
Если переменная, требующая десериализации, является объектом, то после успешного восстановления объекта PHP автоматически попытается вызвать магические метод __unserialize() или __wakeup() (если он существует).
Замечание: Директива unserialize_callback_func
Любые опции unserialize() в виде ассоциативного массива.
Возвращаемые значения
Ошибки
Объекты могут выбрасывать Throwable в своих обработчиках десериализации.
Список изменений
Примеры
Пример #1 Пример использования unserialize()
Пример #2 Пример использования unserialize_callback_func
Примечания
Смотрите также
User Contributed Notes 23 notes
Just some reminder which may save somebody some time regarding the `$options` array:
Say you want to be on the safe side and not allow any objects to be unserialized. My first thought was doing the following:
For the people who are getting the error
and are getting the data from a database, Make sure that you have the database set the the correct encoding, I had the database set as latin1_swedish_ci and all of the data looked perfect, Infact when i copied it into a online unserialize it worked fine. I changed the collation to utf8mb4_unicode_ci and all worked fine.
Here’s a simple function to get the class of a serialized string (that is, the type of object that will be returned if it’s unserialized):
The type names are the same format/case as you would see if you did a var_dump().
In the Classes and Objects docs, there is this: In order to be able to unserialize() an object, the class of that object needs to be defined.
Fatal error: Uncaught exception ‘Exception’ with message ‘Serialization of ‘SimpleXMLElement’ is not allowed’ in [no active file]:0 Stack trace: #0
The entire contents of the session will be lost. Hope this saves someone some time!
// AN XML STRING FOR TEST DATA
$xml = ‘
__PHP_Incomplete_Class Object Demystified
1. First take note of the output. A simple example:
__PHP_Incomplete_Class Object (
[__PHP_Incomplete_Class_Name] => SomeObject1
[obj1property1] => somevalue1 [obj1property2] => __PHP_Incomplete_Class Object ( [__PHP_Incomplete_Class_Name] => SomeObject2 [obj2property1] => somevalue1 [obj2property2] => Array (
[‘key1’] => somevalue3, [‘key2’] => somevalue4 ) ) )
2. We analyze this and break it down.
__PHP_Incomplete_Class Object tells you there is an object that needs to be declared somehow.
__PHP_Incomplete_Class_Name simply tells you the expected class name. It is just one of the properties for now.
So we have:
a) an unknown object that has a class name SomeObject1 (first class)
b) it has 2 properties, namely obj1property1 and obj2property2
c) obj2property2 is itself an object whose class name is SomeObject2 (the second class)
d) SomeObject2 has two properties, obj2property1 and obj2property2
e) obj2property2 is an array that contains two elements
3. Now that we have an idea of the structure, we shall create class definitions based from it. We will just create properties for now, methods are not required as a minimum.
SomeObject1 ( [obj1property1] => somevalue1 [obj1property2] => SomeObject2 ( [obj2property1] => somevalue1 [obj2property2] => Array ( [‘key1’] => somevalue3, [‘key2’] => somevalue4 ) ) )
As you will notice, __PHP_Incomplete_Class Object is gone and replaced by the class name. The property __PHP_Incomplete_Class_Name is also removed.
5. As for the array property obj2property2, we can directly access that and just assume that it is an array and loop through it:
?>
Outputs:
key1 : somevalue3
key2 : somevalue4
That’s it. You can add more methods on the class declarations for the given properties, provided you keep your original output as basis for the data types.
forcing access to __PHP_Incomplete_Class object properties
I’m writing a module for a php cms. In a function (a callback) I can access an object that comes from the framework code.
This object is of type __PHP_Incomplete_Class because the needed header file is not included before the session starts. I cannot include it without hacking the core cms code.
8 Answers 8
This issue appends when you un serialize an object of a class that hasn’t been included yet. For exemple, if you call session_start before include the class.
A PHPIncompleteClass object can’t be accessed directly, but it’s ok with foreach, serialize and gettype. Calling is_object with an PHPIncompleteClass object will result false.
So, if you find a ‘__PHP_Incomplete_Class’ object in your session and you’ve included your class after the session_load, you can use this function :
This will results a usable object :
I found this hack which will let you cast an object:
and then access properties as normal.
As an addition here is my version of the fix_object() function: The main change is step 3 in the code: Make all properties public.
var_dump will not display these NULL byte prefixes to you, but you can see them with this code:
None of the above answers actually worked for me, except this solution:
Hope it helps someone
If you just need to access raw data (like class variables) from a PHP_Incomplete_Class object, you can use the foreach hack, or you can also do:
I tried the answer of Tom Haigh here, but discovered 2 problems.
So I rewrote the function handle this:
I switch tha function arguments too, so that you can use
And you get an stdClass object with only public properties. Even if you have objects in childclasses they are converted to stdClass with public properties too.
I’ve read a lot of suggestions on how to fix incomplete classobjects and I actually needed to fix those problems myself, in a ecommerce-project.
It also works recursively, which in my own case is required, to save the whole array.
__PHP_Incomplete_Class_Name wrong
We’re randomly getting some very strange error logs. They don’t happen on every page hit, even with the same parameters/actions/etc, and they don’t seem repeatable, each one is different in its crash location, and context. But almost all have incorrect __PHP_Incomplete_Class_Name as the cause.
main(): The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition «LoginLogging» of the object you are trying to operate on was loaded before unserialize() gets called or provide a __autoload() function to load the class definition
The problem being, there is no «LoginLogging» class. The object it’s referring to was of type ScormElement when it was saved into the session. Doing a dump of the variable gives:
All the properties are retained correctly, and match the class definition for ScormElement. But the class name is wrong. There is no class named LoginLogging.
What is causing this and how do we fix it.
Edit: This is just an example. Other errors are very similar in structure, but affect other class types, and have different incomplete names. However, ALL incomplete names have the same string length of the correct class name.
Edit 2011-10-27: I’m still seeing these error logs, and have had no success in finding a solution. Any help would be appreciated.
Php incomplete class object
В этом разделе перечисляются стандартные предопределённые классы. Разнообразные модули определяют другие классы, которые описаны в соответствующей справочной информации.
Стандартные определённые классы
Эти классы определены вместе со стандартным набором функций, идущим со сборкой PHP.
Специальные классы
Следующие идентификаторы не могут использоваться в качестве имени класса, так как у них есть специальное назначение.
User Contributed Notes 7 notes
If you call var_export() on an instance of stdClass, it attempts to export it using ::__set_state(), which, for some reason, is not implemented in stdClass.
However, casting an associative array to an object usually produces the same effect (at least, it does in my case). So I wrote an improved_var_export() function to convert instances of stdClass to (object) array () calls. If you choose to export objects of any other class, I’d advise you to implement ::__set_state().
/* Output:
(object) array (‘prop1’ => true, ‘prop2’ => (object) array (‘test’ => ‘abc’, ‘other’ => 6.2, ‘arr’ => array (0 => 1, 1 => 2, 2 => 3)), ‘assocArray’ => array (‘apple’ => ‘good’, ‘orange’ => ‘great’))
*/
?>
Note: This function spits out a single line of code, which is useful to save in a cache file to include/eval. It isn’t formatted for readability. If you want to print a readable version for debugging purposes, then I would suggest print_r() or var_dump().
Подробно об объектах и классах в PHP
Сегодня объекты используются очень активно, хотя это трудно было предположить после выхода PHP 5 в 2005 году. Тогда я ещё мало что знал о возможностях этого языка. Пятую версию PHP сравнивали с предыдущей, четвёртой, и главным преимуществом нового релиза стала новая, очень мощная объектная модель. И сегодня, десять лет спустя, около 90% всего PHP-кода содержит объекты, не изменившиеся со времени PHP 5.0. Это убедительно говорит о том, какую роль сыграло внедрение объектной модели, неоднократно улучшавшейся на протяжении последующих лет. В этом посте я хотел бы рассказать о том, как всё устроено «под капотом». Чтобы люди понимали суть процессов — почему сделано так, а не иначе — и лучше, полнее использовали возможности языка. Также я затрону тему использования памяти объектами, в том числе в сравнении с эквивалентными массивами (когда это возможно).
Я буду рассказывать на примере версии PHP 5.4, и описываемые мной вещи справедливы для 5.5 и 5.6, потому что устройство объектной модели там почти не претерпело изменений. Обратите внимание, что в версии 5.3 всё не так хорошо с точки зрения возможностей и общей производительности.
В PHP 7, который пока ещё активно разрабатывается, объектная модель переработана не сильно, были внесены лишь незначительные изменения. Просто потому что всё и так хорошо работает, а лучшее — враг хорошего. Были добавлены возможности, не затрагивающие ядро, но здесь об этом речи не пойдёт.
В качестве демонстрации начну с синтетических бенчмарков:
Здесь объявляется простой класс с тремя атрибутами, а затем в цикле создаётся 1000 объектов этого класса. Обратите внимание, как в этом примере используется память: при создании объекта класса Foo и переменной для его хранения выделяется 262 байт динамической памяти PHP.
Давайте заменим объект на эквивалентный массив:
Вот ещё один пример:
Теперь давайте разберём, как всё это устроено в недрах PHP, подкрепив теорией практические наблюдения.
Всё начинается с классов
Внутри PHP класс представляется с помощью структуры zend_class_entry:
Важно знать ещё об одном моменте, связанном с zend_class_entry — о PHP-комментариях. Они также известны как аннотации. Это строковые переменные (в языке С — буферы char* ), которые тоже надо разместить в памяти. Для языка С, не использующего Unicode, в отличие от PHP, правило очень простое: один символ = один байт. Чем больше у вас в классе аннотаций, тем больше памяти будет использовано после парсинга.
У zend_class_entry поле doc_comment содержит аннотации класса. У методов и атрибутов тоже есть такое поле.
Пользовательские и внутренние классы
Пользовательский класс — это класс, заданный с помощью PHP, а внутренний класс задаётся либо благодаря внедрению исходного кода в сам PHP, либо с помощью расширения. Самое большое различие между этими двумя видами классов заключается в том, что пользовательские классы оперируют памятью, выделяемой по запросу, а внутренние — «постоянной» памятью.
Это означает, что когда PHP заканчивает обработку текущего HTTP-запроса, он убирает из памяти и уничтожает все пользовательские классы, готовясь к обработке следующего запроса. Этот подход известен под названием «архитектура без разделения ресурсов» (the share nothing architecture). Так было заложено в PHP с самого начала, и изменять это пока не планируется.
Итак, каждый раз при формировании запроса и парсинге классов происходит выделение памяти для них. После использования класса уничтожается всё, что с ним связано. Так что обязательно используйте все объявленные классы, в противном случае будет теряться память. Применяйте автозагрузчики, они задерживают парсинг/объявление во время выполнения, когда PHP нужно задействовать класс. Несмотря на замедление выполнения, автозагрузчик позволяет грамотно использовать память, поскольку он не будет запущен, пока действительно не возникнет потребность в классе.
С внутренними классами всё иначе. Они размещаются в памяти постоянно, вне зависимости от того, использовали их или нет. То есть они уничтожаются только тогда, когда прекращается работа самого PHP — после завершения обработки всех запросов (подразумеваются веб SAPI, например, PHP-FPM). Поэтому внутренние классы более эффективны, чем пользовательские (в конце запроса уничтожаются только статические атрибуты, больше ничего).
Обратите внимание, что даже при кешировании опкодов, как OPCache, создание и уничтожение класса осуществляется при каждом запросе, как и в случае с пользовательскими классами. OPCache просто ускоряет оба этих процесса.
Как вы заметили, если активировать много PHP-расширений, каждое из которых объявляет много классов, но при этом использовать лишь небольшое их количество, то теряется память. Помните, что PHP-расширения объявляют классы во время запуска PHP, даже если в последующих запросах эти классы использоваться не будут. Поэтому не рекомендуется держать расширения активными, если они не применяются в данный момент, иначе вы будете терять память. Особенно если эти расширения объявляют много классов — хотя они могут забить память и чем-нибудь другим.
Классы, интерфейсы или трейты — без разницы
Не слишком хорошо, что здесь используется 912 байт всего лишь для декларирования интерфейса BarException.
Привязка класса
Многие разработчики не вспоминают о привязке класса, пока не начинают задавать вопросом, а как же всё устроено на самом деле. Привязку класса можно описать как «процесс, в ходе которого сам класс и все связанные с ним данные подготавливаются для полноценного использования разработчиком». Этот процесс очень прост и не требует много ресурсов, если речь идёт о каком-то одном классе, не дополняющем другой, не использующем трейты и не внедряющим интерфейс. Процесс привязки для таких классов полностью протекает во время компиляции, а в ходе выполнения ресурсы на это уже не тратятся. Обратите внимание, что речь шла привязке класса, задекларированного пользователем. Для внутренних классов тот же самый процесс выполняется, когда классы зарегистрированы ядром или расширениями PHP, как раз перед запуском пользовательских скриптов — и делается это лишь один раз за всё время работы PHP.
Всё сильно усложняется, если речь заходит о внедрении интерфейсов или наследовании классов. Тогда в ходе привязки класса у родительских и дочерних объектов (будь то классы или интерфейсы) копируется абсолютно все.
Тут добавить нечего, простой случай.
Для каждой из таблиц функций (методов) используется do_inherit_method :
Что касается наследования, то здесь, в принципе, всё то же самое, что и при внедрении интерфейса. Только вовлечено ещё больше «участников». Но хочу отметить, что если PHP уже знает о классе, то привязка осуществляется во время компилирования, а если не знает — то во время выполнения. Так что лучше объявлять так:
Кстати, рутинная процедура привязки класса может привести к очень странному поведению:
В первом варианте привязка класса В отложена на время выполнения, потому что когда компилятор доходит до объявления этого класса, он ещё ничего не знает о классе А. Когда начинается выполнение, то привязка класса А происходит без вопросов, потому что он уже скомпилирован, будучи одиночным классом. Во втором случае всё иначе. Привязка класса С отложена на время выполнения, потому что компилятор ещё ничего не знает о В, пытаясь скомпилировать его. Но когда во время выполнения начинается привязка класса С, то он ищет В, который не существует, поскольку не скомпилирован по причине того, что В является дополнением. Вылетает сообщение “Class B doesn’t exist”.
Объекты
Итак, теперь мы знаем, что:
Теперь поговорим об объектах. В первой главе показано, что создание «классического» объекта («классического» пользовательского класса) потребовало очень мало памяти, около 200 байт. Всё дело в классе. Дальнейшая компиляция класса тоже потребляет память, но это к лучшему, потому что для создания одиночного объекта требуется меньше байт. По сути, объект представляет собой крохотный набор из крохотных структур.
Управление методами объекта
Вы могли заметить интересную вещь, посмотрите на первые строки:
Во время компиляции функции/метода происходит немедленный перевод в нижний регистр. Вышеприведённая функция BAR() превращается в bar() компилятором при добавлении метода таблице классов и функций.
В приведённом примере первый вызов статический: компилятор вычислил key для строковой “bar”, а когда приходит время вызова метода, ему нужно делать меньше работы. Второй вызов уже динамический, компилятор ничего не знает о “$b”, не может вычислить key для вызова метода. Затем, во время выполнения, нам придётся перевести строковую в нижний регистр и вычислить её хеш ( zend_hash_func() ), что не лучшим образом сказывается на производительности.
Управление атрибутами объекта
Вот что происходит:
Так что, создавая объект, мы «всего лишь» создаём структуру zend_object весом 32 байта:
Далее движок создаёт вектор признаков нашего объекта:
Вероятно, у вас возникли два вопроса:
Пока вы не пишете в объект, его потребление памяти не меняется. После записи он занимает уже больше места (пока не будет уничтожен), поскольку содержит все записанные в него атрибуты.
Объекты, ведущие себя как ссылки благодаря хранилищу объектов
Объекты не являются ссылками. Это демонстрируется на маленьком скрипте:
Все сейчас скажут, что «в PHP 5 объекты являются ссылками», об этом упоминает даже официальный мануал. Технически это совершенно неверно. Тем не менее, объекты могут вести себя так же, как и ссылки. Например, когда вы передаёте переменную, являющуюся объектом функции, эта функция может модифицировать тот же объект.
object(MyClass)#1 (0) < >/* #1 is the object handle (number), it is unique */
Когда мы вызываем метод, движок изменяет область видимости:
Вот так можно получать доступ к приватным членам объектов, не принадлежащим вам, но являющимся дочерними по отношению к вашей текущей области видимости:
Эта особенность стала причиной большого количества баг-репортов от разработчиков. Но так устроена объектная модель в PHP — на самом деле, мы задаём область видимости на основе не объекта, а класса. В случае с нашим классом “Foo”, вы можете работать с любым приватным Foo любого другого Foo, как показано выше.
О деструкторе
Деструкторы опасны, не полагайтесь на них, поскольку PHP их не вызывает даже в случае фатальной ошибки:
А что насчёт порядка вызова деструкторов в том случае, если они всё-таки вызываются? Ответ хорошо виден в коде:
Здесь продемонстрированы три стадии вызова деструктора:
PHP не вызывает деструкторы в случае возникновения какой-либо фатальной ошибки. Дело в том, что в этом случае Zend работает нестабильно, а вызов деструкторов приводит к выполнению пользовательского кода, который может получить доступ к ошибочным указателям и, в результате, к падению PHP. Уж лучше сохранять стабильность системы — поэтому вызов деструкторов и блокируется. Возможно, в PHP 7 что-то и поменяется.
Суммируя вышесказанное: не доверяйте деструкторам критически важный код, например, управление механизмом блокировки (lock mechanism), поскольку PHP может и не вызвать деструктор или вызвать его в неконтролируемой последовательности. Если всё-таки важный код обрабатывается деструктором, то как минимум самостоятельно контролируйте жизненный цикл объектов. PHP вызовет деструктор, когда refcount вашего объекта упадёт до нуля, а это значит, что объект больше не используется и его можно безопасно уничтожить.