Ryan Fisch
Ryan Fisch

Reputation: 2654

Error saving object with private member to MongoDb with PHP Driver

Can anyone tell me if there is a resolution to saving PHP classes with private members to MongoDb? I keep getting the following error

zero-length keys are not allowed, did you use $ with double quotes?'

I see there at least two existing question pertaining to the same question with no real answers.
Question 1 Question 2

all of my persistence classes have a private member that I need available but I'm not interested in creating a function to avoid the necessity for the private member since this is an instance based class that has multiple function utilizing the private member.

Web server Apache/2.2.22

PHP version PHP 5.4.6

PHP extension mongo/1.2.6

This would be a sample implementation, please don't critique the code itself it is just to illustrate the type of behavior which is the Save of $this and the $private member in the base type:

<?php

class PersistableObject extends AbstractBasePersistableObject
{
   public  $PublicSubTypeProperty;

    public function __construct()
    {
       parent::__construct();
    }

    public function GetDalConfigurationFromSubType()
    {
        //This object is just a wrapper DAL implemented based on
        //the php Mongo, MongoDb and MongoCollection objects
        return new MongoBasedDal();
    }
}


abstract class AbstractBasePersistableObject
{
   private $dalRef = null;
   public $PublicBaseProperty;

   public abstract function GetDalConfigurationFromSubType();

   public function __construct()
   {
      $this->dalRef = $this->GetDalConfigurationFromSubType();
   }

   public function Save()
   {
       $this->dalRef->Save($this);
   }
}

?>

Upvotes: 2

Views: 1091

Answers (2)

Sammaye
Sammaye

Reputation: 43884

Can anyone tell me if there is a resolution to saving PHP classes with private members to MongoDb?

The problem, as you know from Google Groups, is that the PHP driver actually omits private and protected variables from save. This is a fundamental problem within the core of the driver itself so there is no easy way around it. As you also know Derick created a JIRA for this: https://jira.mongodb.org/browse/PHP-624

@harald does actually state the way around, to create your own _to_array() and feed that into the save function, unfortunately this derives a fundamental problem again with how you have built your classes. You must use reflection for this like so:

$reflect = new ReflectionClass(get_class($this));
$class_vars = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE);

foreach ($class_vars as $prop) {
    $doc[$prop->getName()] = $this->{$prop->getName()};
}

And then use $doc within the save.

You can however change your programming a bit to avoid the reflection (or just cache the result of the reflection to save the resource hog) by making a pre-defined array of $schema which will house all the fields you wish to save. When you wish to add new fields on-demand you can just make your __set and __get update the in memory schema array. This way you can avoid the reflection altogether and just go for grabbing the schema array and it's values.

This is the method a lot of frameworks use to get around the problem, including Lithium, Yii, Kohona, CakePHP and more.

Upvotes: 5

aurora
aurora

Reputation: 9627

The problem might be, that Mongo is trying to cast your object to an array. If you are viewing the output of ...

class Address{
    private $name;
    private $company;
    private $zip;

    public function __construct($name,$company,$zip){
        $this->name = $name;
        $this->company = $company;
        $this->zip = $zip;
    }
}

print_r((array)new Address('name', 'company', 'zip'));

... with hexdump, it will reveal the following:

harald:$ ~/test.php | hexdump -C
00000000  41 72 72 61 79 0a 28 0a  20 20 20 20 5b 00 41 64  |Array.(.    [.Ad|
00000010  64 72 65 73 73 00 6e 61  6d 65 5d 20 3d 3e 20 6e  |dress.name] => n|
00000020  61 6d 65 0a 20 20 20 20  5b 00 41 64 64 72 65 73  |ame.    [.Addres|
00000030  73 00 63 6f 6d 70 61 6e  79 5d 20 3d 3e 20 63 6f  |s.company] => co|
00000040  6d 70 61 6e 79 0a 20 20  20 20 5b 00 41 64 64 72  |mpany.    [.Addr|
00000050  65 73 73 00 7a 69 70 5d  20 3d 3e 20 7a 69 70 0a  |ess.zip] => zip.|
00000060  29 0a                                             |).|
00000062

Have a look at those dots in .Address.name, they are null bytes, which are probably not allowed in keys and therefore lead to the somehow strange error message.

As PHP does not (yet) have a magic method __to_array, you will probably need your own helper method to "cast" your object to an array. Other possibility would be to just use "public" properties, but this is not a nice solution either.

Upvotes: 1

Related Questions