php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #61103 Unable to update field '0' using an array.
Submitted: 2012-02-16 10:46 UTC Modified: 2012-11-29 22:33 UTC
From: leight at gmail dot com Assigned:
Status: Not a bug Package: mongo (PECL)
PHP Version: 5.3.10 OS: OSX
Private report: No CVE-ID:
 [2012-02-16 10:46 UTC] leight at gmail dot com
Description:
------------
Trying to update a collection that has a field with the name '0' fails when using an array, but succeeds when using a stdClass object with a property named '0'

Test script:
---------------
$m = new Mongo;
$collection = $m->testdb->testcollection;

$collection->insert(array(
    0, 1, 1, 2, 3, 5
));

echo "<h2>Original object:</h2>";

$obj = $collection->findOne();
var_dump($obj);

$updates = array(
	array(0 => 'zero int'), 
	array('0' => 'zero str'),
	array(1 => 'one int'),
	array('2' => 'two str'),
	// This multiple field update is particularly noteworthy.
	// NO FIELDS ARE UPDATED with the inclusion of a '0' field
	array(
		'0' => 'multi 0',
		'1' => 'multi 1',
		'2' => 'multi 2'
	)
);

$updClass = new stdClass;

$updClass->{0} = 'class 0';
$updClass->{1} = 'class 1';
$updClass->{2} = 'class 2';

$updates[] = $updClass;

foreach ($updates as $update) {	
	echo "<h2>Updating with:</h2>";
	var_dump($update);

	$collection->update(
		array('_id' => $obj['_id']),
		array('$set' => $update)
	);
	
	echo "<h3>Result:</h3>";
	$obj = $collection->findOne();
	var_dump($obj);
}

echo "<h2>BSON equality test:</h2>";

$updArr = array(
	'class 0',
	'class 1',
	'class 2'
);

var_dump(bson_encode($updClass) === bson_encode($updArr));

Expected result:
----------------
All updates with a field name of '0' using an array to pass the updates should process the same as any other update.

Actual result:
--------------
Using a field name of '0' prevents ALL updates in the query from being applied. This happens only when using an array of field => value.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-11-29 17:35 UTC] derick@php.net
This is not something that we can address, but first of all, you are making a few incorrect assumptions:

<?php
$m = new MongoClient();
$c = $m->phpunit->test;
$c->remove();
$c->insert( array( 0, 1, 1, 2, 3, 5 ) );
$c->update( array(), array( '$set' => array( "0" => 'zero int' ) ) );

This does not work - but you only see that when you have safe mode turned on:

MongoCursorException: localhost:27017: Invalid modifier specified: $set in /tmp/php-606.phpt on line 7

What happens is that when serializing arrays from PHP to BSON, the driver checks the field names. As you know, in PHP you can have numerically indexed array keys, and associative array keys. These types need to be converted differently into BSON.

A BSON array has *no* keys, and in PHP we will only create a BSON array out of a PHP array if:

 - There are no string keys, or:
 - The first key in the array is != 0, or:
 - The keys in the array are not consecutive (ie, have gaps), such as in
   array( 0, 1, 3, 4 ).

In all other cases, we create a BSON object.

If you supply an object from PHP (any class), then we will always convert it to a BSON object.

Now lets have a look at your updates:

array( '$set' => ( 0 => 'zero int' ) );

This gets converted into the following JSON/BSON if we follow the above guidelines.

{ '$set' : [ 'zero int'] }

 - The first level is an object {} because there is a string key ($set).
 - The nested PHP array is an array [] because: there are no string keys, the first key is a 0 and the keys are consecutive.

Conclusion: This creates an incorrect $set for MongoDB.

The same is true for the array('0' => 'zero str') case, as PHP converts string keys that look like numbers to numbers.

These two cases will work:

	array(1 => 'one int'),
	array('2' => 'two str'),

Both get converted to a BSON object.

Then the last array method:

	array(
		'0' => 'multi 0',
		'1' => 'multi 1',
		'2' => 'multi 2'
	)

Will create in JSON/BSON:

{ $set: [ 'multi 0', 'multi 1', 'multi 2' ] }

Which again is incorrect.

When you use $updClass in your example, then things work because objects are always converted to BSON objects.

Conclusion:
 - Don't use numerical field names (BSON requires *string* field names, but in PHP, you can't have one that looks like a number due to syntax restrictions.
 - If you do use numerical field names, you *have* to use an object from the PHP side to do updates, just like you do with:

$updClass = new stdClass;
$updClass->{0} = 'class 0';
$c->update( array(), array( '$set' => $updClass ) );
 [2012-11-29 17:35 UTC] derick@php.net
-Status: Open +Status: Not a bug
 [2012-11-29 22:33 UTC] leight at gmail dot com
Thanks Derick,

You already gave me an answer here: http://stackoverflow.com/q/9297495/599857 (I 
even discussed this with you quickly at PHPNW!), you're a busy guy, so I'll let 
you off for not remembering :)
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Sun Apr 20 21:01:56 2014 UTC