?
Solved

incomplete object error - followup to Q_21299176

Posted on 2005-03-16
28
Medium Priority
?
687 Views
Last Modified: 2008-01-09
original: http://www.experts-exchange.com/Web/Web_Languages/PHP/Q_21299176.html
got that to work on Windows server and PHP 5
moved to production FreeBSD box and php4 and it died
session.auto_start = 0

test1.php:
   require_once("section.class.php");
   session_start();
   $_SESSION['section'] = new section();
   echo "<pre>".print_r($_SESSION['section'],true)."</pre><hr>";

= cool and groovy

test2.php:
   require_once("section.class.php");
   session_start();
   echo "<pre>".print_r($_SESSION['section'],true)."</pre><hr>";

= section Object
(
    [_db_type] => mysql
    [_db_host] => localhost
    [_db_user] => *****
    [_db_password] => *****
    [_db_name] => cc_section
    [_db] =>
    [db] => __PHP_Incomplete_Class Object
        (
            [__PHP_Incomplete_Class_Name] => adodb_mysql
            [dataProvider] => mysql

[section] object is using adoDB class for persistent connection which I think is broken if I serialize/unserialize. So [section] object survives but not DB object within [section] object

what am I doing wrong?

thanks,
cd
0
Comment
Question by:christiandillon
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 14
  • 9
  • 5
28 Comments
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13557648
Have you not seen the __sleep and __wakeup functions? They allow you to recreate dynamic properties (such as DB connections) on serialize/unserialize. The actual particular db resource doesn't matter - as long as there is a working one around. They're documented here:

http://www.php.net/serialize

Given that ado is now PHP5 aware, I would expect it to be dealing with this for you, but I guess it may not be.
0
 

Author Comment

by:christiandillon
ID: 13558043
same result

test1.php
   require_once("section.class.php");
   session_start();
   $section = new section();
   $_SESSION['section'] = serialize($section);

test2.php
   require_once("section.class.php");
   session_start();
   $section = unserialize($_SESSION['section']);
   echo "<pre>".print_r($section,true)."</pre><hr>";

= section Object
(
    [_db_type] => mysql
    [_db_host] => localhost
    [_db_user] => *****
    [_db_password] => *****
    [_db_name] => cc_section
    [_db] =>
    [db] => __PHP_Incomplete_Class Object
        (
            [__PHP_Incomplete_Class_Name] => adodb_mysql
            [dataProvider] => mysql

I know the adoDB works separately and the phpGACL works (a very cool class by the way), just not together in my code.
0
 
LVL 17

Expert Comment

by:davebytes
ID: 13560415
Obviously, you should REALLY be running similar configurations for development and production.  If you are using different PHP versions, let alone different OSes, hard to say what might be going wrong...

From a discussion on a blog:
"...if you do a print_r() on it, you will find that it is of class type  __PHP_Incomplete_Class instead of whatever it should be. This is because WP/PHP has not yet loaded the class definition for that object, which causes it to become that type..."

Basically, this would indicate to me (I've never tried storing a class in a session before!) that the class adodb_mysql hasn't yet been defined, possibly needing to be done before the session_start tries to retrieve session state.

Two things you can try:
1. Easier: switch you dev box to the SAME php4 version as the server (same extensions, etc., etc.!).
2. Harder: switch the production box up to php5, as there were some known issues in php4 that might have been fixed.

Either one will help prove if there's some easier fix under the covers.

-d
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:christiandillon
ID: 13560736
I understand that my [section] object is incomplete because it can't load the adoDB class. I tried include'ing adoDB before the session_start() on both pages but same result.

Switched dev machine to php4 (xampp makes it easy and I'm lazy) and it worked as it should. I'm a rookie in object handling so if there's better way please share but it makes sense to me that I should be able to build a class from another class - then create on object and pass it from page to page.

sysadmin thought the problem might be related to caching on BSD box?

<project name="crunchtime" value="on">
0
 
LVL 17

Expert Comment

by:davebytes
ID: 13562193
quick things to check would be the exact versions of php4 in use on both systems, and maybe compare the phpinfo() dump from the two machines, comparing the PHP Core sections and the Session sections, see if any differences jump out at you.

I also assume you have the same copy of adodb_mysql code on both machines.  Just sanity checking!

Everything I've read (I too don't have a lot of experience with saving serialized objects in sessions...) points to the class not being instantiated prior to the session unserializing the object.

The reason to check versions and phpinfo is because as we've all learned, all it takes is one little option to throw a bunch of things out of whack.  I could see one session INI setting screwing things up, or something like register globals...

I've been reading all the samples, examples, suggestions, etc., between php.net man pages and various php sites, and haven't seen anything specific.  So best to try step by step to isolate differences between the two machines, hope something sticks out ...

-d
0
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13563398
How about trying to store the ado object separately, just for testing purposes, as in:

   require_once 'section.class.php';
   session_start();
   $section = new section();
   $_SESSION['section'] = serialize($section);
   $_SESSION['ado'] = serialize($section->db);

and see if survives by itself.
0
 

Author Comment

by:christiandillon
ID: 13563810
Squinky,
same result:

1:
      $_SESSION['section'] = serialize($section);
      $_SESSION['ado'] = serialize($section->db);

2:
      $section = unserialize($_SESSION['section']);
      $ado = unserialize($_SESSION['section->db']);

= Incomplete Object error
Then I require'd the ado class in the first page and same result.

davebytes,
My sysadmin thought so too and he was comparing the two last night when I left. At first glance, nothing jumped out related to sessions. We'll see what fresh eyes find today.

From your posts and others elsewhere, I'm confident that the code *should* work so it seems I'm looking for some difference between the two machines. Dang free software.

cd
0
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13563934
How about generating some debug output in the __wakeup() handler of your object so you can see if it's even being called? You could probably introspect (in PHP5) to see explicitly if the class is defined before you call unserialize.
0
 

Author Comment

by:christiandillon
ID: 13563943
CORRECTION: it doesn't work on my machine this way either, that's why I had to come up with the approach from my original question Q_21299176:

index:

   require_once("mygacl.php");
   require_once("mygacl_api.inc.php");
   session_start();
   $gacl = new mygacl();
   $_SESSION['gacl'] = $gacl;
   $gacl_api = new mygacl_api();
   $_SESSION['gacl_api'] = $gacl_api;

subsequent:

   require_once("mygacl.inc.php");
   require_once("mygacl_api.inc.php");
   session_start();
   //  print_r :   [gacl] => __PHP_Incomplete_Class Object
   $gacl = new mygacl();
   $gacl = $_SESSION['gacl'];
   $gacl_api = new mygacl_api();
   $gacl_api = $_SESSION['gacl_api'];


So maybe the question needs to be how to instantiate three objects from three classes that each instantiate an object (adoDB) and have them all survive across pages?
0
 

Author Comment

by:christiandillon
ID: 13564052
OK, this works on production box:

1:
   require_once("section.class.php");
   session_start();
   $section = new section();
   $_SESSION['section'] = serialize($section);

2:
   require_once("section.class.php");
   session_start();
   $section = new section();   // instantiates adoDB object
   $section = unserialize($_SESSION['section']);

So it seems the need to instantiate the 'section' object again in page 2 is to instantiate the adoDB again before restoring its values. Now I need to check if the persistent DB connection remains alive.
0
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13564143
I think that includes inside class definitions don't happen unless the code is actually run, so perhaps it doesn't know about ADO at unserialize time - how about requiring it as well as the other classes? Ugly solution though!
0
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13564190
To clarify, this include won't happen until you instantiate the class:

<?php
class thing {
  include 'someclass.php';
}
?>

though this one will:
<?php
include 'someclass.php';
class thing {
}
?>

Because ADO will be a property of your class rather than its parent, it's probably arranged like the former case.
0
 

Author Comment

by:christiandillon
ID: 13564492
I tried to include the ADO with the other classes but no effect.

So I've managed to keep the objects alive in the session but the persistent DB connection dies. Do you think that's related to the serialize/unserialize (which I'm not doing on my dev machine where this all worked) or might that be a config issue on production?
0
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13564729
Well, I've not checked if it's mean to do it anyway, but ADO would definitely need a __wakeup function that re-creates a connection on unserialize - there's no point in preserving an existing connection resource as the other end (over which you have no control) may have died by the time you wake up again.
0
 

Author Comment

by:christiandillon
ID: 13564756
I think this is it though I don't understand how to use it. Can you shed any light?
From php.net:

unserialize_callback_func directive:  It's possible to set a callback-function which will be called, if an undefined class should be instantiated during unserializing. (to prevent getting an incomplete object "__PHP_Incomplete_Class".) Use your php.ini, ini_set() or .htaccess to define 'unserialize_callback_func'. Everytime an undefined class should be instantiated, it'll be called. To disable this feature just empty this setting.

Production server php.ini:
; The unserialize callback function will be called (with the undefined class'
; name as parameter), if the unserializer finds an undefined class
; which should be instanciated.
; A warning appears if the specified function is not defined, or if the
; function doesn't include/implement the missing class.
; So only set this entry, if you really want to implement such a
; callback-function.
unserialize_callback_func=

My dev php.ini has nothing set either though.

cd
0
 

Author Comment

by:christiandillon
ID: 13564772
Sorry, we were posting at the same time. You're saying I need to add the __wakeup function to the ADO class or my class that uses ADO?
0
 

Author Comment

by:christiandillon
ID: 13565130
I added a simple wakeup function to my 'section' class and I got farther than before so I think you're right. Now I need to figure out what needs to be in sleep and wakeup. I'm trying to distill the php.net page on these magic functions.
0
 
LVL 25

Accepted Solution

by:
Marcus Bointon earned 1400 total points
ID: 13565342
It's only the __wakeup that really matters in this case - you don't really care what happens to the old database resource, so you don't need to do anything special on __sleep. I'm guessing here, but I'd assume that the __wakeup should be in whichever class establishes (and thus maintains) the connection in the first place, which probably is ADO. In your class, it's sufficient to restore the ADO instance as is, as long as the ADO instance takes care of re-establishing the connection. I'm surprised that ADO doesn't do this already. __sleep and __wakeup don't seem to have much in the way of documentation. This looks promising:

http://www.xisc.com/documentation/manual/System.Data/TAdodb.html
0
 
LVL 17

Assisted Solution

by:davebytes
davebytes earned 600 total points
ID: 13566514
However, also need to watch out for duplicate __sleep/__wakeup on the same object, as that can cause issues.  So make sure section doesn't have multiple references to the same adodb object, no back refs, no recursive refs, etc.  That stuff can be a mess.

I'm also seeing a lot of references to persisting connections.  They don't actually physically persist the way I was assuming -- that is, I think they open up a TON of connections to the server, and hand back one when a persistent connection is requested.  I could be wrong, but that seems like an 'optimization', not actually that it is saving the state.  In fact, nothing in the persist docs has anything that makes a given DSN (the persisting identifier) unique between different PHP sessions.

Do you NEED to session-ize the adodb object?  Is that buying you something in particular over creating a new object and telling it to use the persist flag?

Obviously, that IS the trick -- you'd want, in your __wakeup handler to recreate the connection object with either PConnect or using a DSN -- or if you are using the ADOConnection object, you maybe want to call _pconnect().  But, really, in that case, why bother storing the ado object in a session -- you are doing a ton of work to bring it back up, and unless there is particular state you want retained (I don't know, last query/table, something like that), re-creation would seem adequate.

-d
0
 

Author Comment

by:christiandillon
ID: 13566787
I'm using phpGACL for access control within my app and it uses ADO so I adopted ADO within my class. One ACL object for checking access to various components, another ACL object for the API get/set's, and my object represents the user.

I tried a simple db->Pconnect in the __wakeup but it did not seem to work. The notes on php.net make it sound like there's more that needs to be included. I'm still digesting.

I will look into the TAdodb wrapper, Squinky.

Still trying to figure out why it works in dev but not production because I don't want to face this or something similar again.
0
 
LVL 17

Expert Comment

by:davebytes
ID: 13567888
I'm wondering if it could be some other difference with the FreeBSD configuration.  I'm wracking my brain trying to guess at other areas that might be different, certainly the PHP code/build itself is physically different.

Did you go and explicitly check the version of PHP4 up on the FreeBSD box?  And what modules are compiled in, etc?  Not trying to beat a dead horse there... ;)

-d
0
 

Author Comment

by:christiandillon
ID: 13568082
both 4.3.10

what would I be looking for?

should I post the differences?
0
 
LVL 17

Expert Comment

by:davebytes
ID: 13569589
No clue... I'm throwing darts here, as if the two machines really were identical setups, they should perform identical.  Obviously, there are two OSes involved, and how php gets invoked, the actual php.exe for the two platforms, etc., could all make subtle differences.  You are sure that all PHP-based code is identical on the two boxes?  We're just looking at OS/version/config possible issues?

-d
0
 

Author Comment

by:christiandillon
ID: 13569655
Let's backtrack and make sure I'm using __wakeup properly. What specifically should be in the __wakeup function and in which class since they all use ADOdb? Remember, I'm not using the __wakeup on my dev machine and I don't think my dev code is proper, though it works.

cd
0
 

Author Comment

by:christiandillon
ID: 13570580
I think I figured out what goes in the __wakeup function. A single $section->db survives across pages on both machines. It's when I add another object that also uses ADOdb that both objects fail to fully instantiate on either machine.
0
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13570992
Have you checked the ADODB class to see if it does anything on wakeup itself? I don't think it really matters whether you use persistent connections or not - if you request a new connection on wakeup you don't really care if it re-uses an existing one or makes a new one. I don't see why having connections shared by different object should be an issue - unless the wakeup of one somehow messes up the other?

One little test you could try to spot differences between your servers: capture a php -i on a command line on each, then do a diff between them. Better than trying to spot differences yourself...
0
 

Author Comment

by:christiandillon
ID: 13571295
ADOdb has no wakeup. I've stopped trying to figure out why it works on dev but not production because the original dev code was a hack (see previous question - link at top) due to my lack of experience with objects. What difference a day makes. Now it's working with simple test code and three classes all instantiating ADOdb objects:

To reiterate:

page 1
      require_once("section.class.php");
      require_once("mygacl.inc.php");
      require_once("mygacl_api.inc.php");
      session_start();
      $section = new section();
      $gacl = new mygacl();
      $gacl_api = new mygacl_api();
      echo "<pre>".print_r($section,true)."</pre><hr>";
      echo "<pre>".print_r($gacl,true)."</pre><hr>";
      echo "<pre>".print_r($gacl_api,true)."</pre><hr>";
      $_SESSION['section'] = serialize($section);
      $_SESSION['gacl'] = serialize($gacl);
      $_SESSION['gacl_api'] = serialize($gacl_api);


page 2

      require_once("section.class.php");
      require_once("mygacl.inc.php");
      require_once("mygacl_api.inc.php");
      session_start();
      $section = unserialize($_SESSION['section']);
      $gacl = unserialize($_SESSION['gacl']);
      $gacl_api = unserialize($_SESSION['gacl_api']);
      echo "<pre>".print_r($section,true)."</pre><hr>";
      echo "<pre>".print_r($gacl,true)."</pre><hr>";
      echo "<pre>".print_r($gacl_api,true)."</pre><hr>";

      $r = $section->str_rand();
      echo "str_rand = $r<br>";

      $r = print_r($section->getUserList(),true);
      echo "getUserList = <pre>$r</pre><br>";

      $merchantAccess = $gacl->getMerchantAccess('enterprises', 'chris.dillon@checkcare.com');
      echo "merchantAccess = $merchantAccess<br>";

      $r = print_r($gacl_api->get_group_children(14,'ARO'),true);
      echo "get_group_children = <pre>$r</pre><br>";


all works fine. But the actual application is losing an object definition somewhere so I need to look for places where the object may be accessed by I haven't unserialized it. Or something like that.

Bottom line, with your all's help I ran up the learning curve and got it working. I will split the points for your effort and insights.

Anybody recommend a good book on PHP Development for the intermediate?

Thanks,
cd
0
 
LVL 25

Expert Comment

by:Marcus Bointon
ID: 13572664
Glad it turned out to be something simple! Thanks for the points. I can recommend 'PHP 5 power programming'  (I've taken up using single quotes when I don't want to embed anything ;^)) by Andi, Stig & Derick (who should know about this stuff!). It's a really good overview of PHP5 with some great advice, and it's definitely not for beginners. I don't have them, but I've seen nothing but good things about Harry Fuecks' PHP anthology: http://www.sitepoint.com/books/phpant1/ 

Nice working with you Dave.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Things That Drive Us Nuts Have you noticed the use of the reCaptcha feature at EE and other web sites?  It wants you to read and retype something that looks like this. Insanity!  It's not EE's fault - that's just the way reCaptcha works.  But it i…
Introduction This article is intended for those who are new to PHP error handling (https://www.experts-exchange.com/articles/11769/And-by-the-way-I-am-New-to-PHP.html).  It addresses one of the most common problems that plague beginning PHP develop…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to count occurrences of each item in an array.
Suggested Courses

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question