I'm having troubles with PHP MongoCursor since I upgraded to Mongo PHP Driver from 1.5.8 to 1.6.0
The following code works well with version 1.5.8, but crashes with version 1.6
PHP version is 5.5.21., Apache version is Apache/2.4.10 (Ubuntu)
$mongoClient = new \MongoClient($serverUrl, ['readPreference'=>\MongoClient::RP_NEAREST]);
$database = $mongoClient->selectDB($dbName);
$collection = $database->selectCollection($collectionName);
// count() works fine and returns the right nb on documents
echo '<br/>count returned '.$collection->count();
// find() exectues with no error...
$cursor = $collection->find();
$documents = [];
// ...and hasNext() crashes with the Excetion below
while($cursor->hasNext()){$documents[] = $cursor->getNext();}
return $documents;
And so the hasNext() call crashes with this message :
CRITICAL: MongoException: The MongoCursor object has not been correctly initialized by its constructor (uncaught exception)...
Am I doing something wrong ?
Thanks for you help !
This may be related to a bug that was introduced in 1.6.0 regarding iteration with hasNext() and getNext(): PHP-1382. A fix has since been merged to the v1.6 branch and should be released later this week as 1.6.1.
That said, the bug regarding hasNext() was actually that the last document in the result set would be missed while iterating. If I run your original script against 1.6.0, the array contains a null value as its last element. With the fix in place, the array will contain all documents as is expected. I cannot reproduce the exception you're seeing with either version.
That exception is actually thrown from an internal checks on the C data structures, to ensure that the cursor object is properly associated with a MongoClient and socket connection. See the MONGO_CHECK_INITIALIZED() macro calls in this file. Most all of the cursor methods check that a MongoClient is associated, but hasNext() is unique in that it also checks for the socket object (I believe other methods just assume a cursor with a MongoClient also has a socket). If that exception is truly reproducible for you and you're willing to do some debugging with the extension, I'd be very interested to know which of the two checks is throwing the error.
As a side note, you should also be specifying the "replicaSet" option when constructing MongoClient. This should have the replica set name, which ensures that the driver can properly ignore connections to hosts that are not a member of the intended replica set.
I just encountered the same issue; I refactored my code to use the cursor iterator instead, ie:
foreach( $cursor as $doc ) {
$documents[] = $doc;
}
I was looking for a code example of how to implement a tailable cursor and found this question. The following code is a simple example of a tailable cursor (via the $cursor variable) which you provide on a capped mongodb collection.
$cursor->tailable(true);
$cursor->awaitData(true);
while (true) {
if ($cursor->hasNext()) {
var_dump($cursor->getNext());
} else {
if ($cursor->dead()) {
break;
}
}
}
Related
I've moved an application between servers.
From:
Windows 2008 R2
ISS 6.1
PHP 5.4.11
SQL Server 10
ZF 2.4
To:
Windows 10
PHP built-in server
PHP 7.2.5
SQL Server 14
ZF 2.4
Below the TableGateway selectWith() returns an empty resultset but when the same $select is queried via Adapter->query() it works as expected. It worked fine on the previous server. The TableGateway selectWith() is the original code and needs to work. Apart from the version numbers above, nothing else has changed.
$select = $this->tableGateway->getSql()->select()->where(array(
'col1 = ?' => $input
));
// via selectWith
$resultSet = $this->tableGateway->selectWith($select);
// via query
$selectString = $this->tableGateway->getSql()->buildSqlString($select);
$adapter = $this->tableGateway->getAdapter();
$results = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE);
// works
var_dump($results->current());
// doesn't work
var_dump($resultSet->current());
Update:
$select = $this->tableGateway->getSql()->select()->where(array(
'col1 = \''.$input.'\''
));
The above code works. So the problem must be with the way TableGateway Select placeholder and replacements. I've tried all the variations in the docs and they should work but don't.
Further update:
Obviously one of these uses sqlsrv_query and the other uses sqlsrv_execute. I've been through the library and there must be a problem with the way it binds params to sqlsrv_prepare but I haven't got to the bottom of it yet. sqlsrv_prepare requires pass by reference. I suspect the bug is an incompability with this breaking change between 5.6 and 7.
The issue here is that ZF2 is not compatible with PHP > 5.6. In this instance the API for sqlsrv has been made stricter due to deprecation of "call-time pass-by-reference". The solution is to stick with PHP 5.6 or refactor a large amount of the code.
My application need to use database instead of file for the session management.
My Application is based on Zend Framework 1.12.17, php 5.6.25 and actualy on wampserver
That is my config.ini
resources.session.use_only_cookies = true
resources.session.gc_maxlifetime = 864000
resources.session.remember_me_seconds = 864000
resources.session.gc_probability = 1
resources.session.gc_divisor = 100
resources.session.saveHandler.class = "Zend_Session_SaveHandler_DbTable"
resources.session.saveHandler.options.name = "app_session"
resources.session.saveHandler.options.primary = "id"
resources.session.saveHandler.options.modifiedColumn = "modified"
resources.session.saveHandler.options.dataColumn = "data"
resources.session.saveHandler.options.lifetimeColumn = "lifetime"
When I want to upgrade php to php 7.0.10, a warning occurred
Warning: session_write_close(): Failed to write session data (user). Please verify that the current setting of session.save_path is correct (D:\wamp\www\myapp\top\session) in D:\wamp\www\myapp\top\library\versions\ZendFramework-1.12.17-minimal\library\Zend\Session.php on line 732
I'm looking for the origin of this problem. Do you have an idea?
Thanks a lot
On September 2016, Zend Framework 1 is reached EOL (end-of-life). This means it will not be improved anymore. The codebase is too old to work well with PHP 7.
Anyway, you have at least two option:
Downgrade to or run in parallel PHP 5.6 on your server to support ancient ZF1 app.
Write your own session save handler by extending the DbTable handler as suggested here.
The issue is written here: https://github.com/zendframework/zf1/issues/665#issue-127528467
Since an update that returns 0 but doesn't throw an exception was still a successful query with no error
Hence the function write will return false instead of true, and PHP 7.0 requires a true result.
You can fix this by changing, in Zend/Session/SaveHandler/DbTable.php:
if ($this->update($data, $this->_getPrimary($id, self::PRIMARY_TYPE_WHERECLAUSE))) {
To:
if (is_int($this->update($data, $this->_getPrimary($id, self::PRIMARY_TYPE_WHERECLAUSE)))) {
Or you can also remove the if, turn it into an instruction, and keep the $return = true;. Because on error, the query should raise an Exception, so any update() without Exception is good.
In addition to edigus answer here is such a simple implementation of the extended save handler:
<?php
require_once 'Zend/Session/SaveHandler/DbTable.php';
// NOTE: To fix an issue with Zend_Session_SaveHandler_DbTable on PHP 7 this class extends it and overrides the write
// method to simply always return true.
//
// See: https://stackoverflow.com/a/44985594/1510754
// See: https://github.com/zendframework/zf1/issues/665
// See: https://github.com/zendframework/zf1/pull/654
class My_Session_SaveHandler_DbTable extends Zend_Session_SaveHandler_DbTable
{
public function write($id, $data)
{
parent::write($id, $data);
return true;
}
}
I'm a bit stumped here.
I've upgraded an Ubuntu 13.10 dev machine to PHP 5.5.3 with the PECL Mongo driver 1.5.5.
Previously on 5.3.6 with 1.3.2.
All of my objects store their MongoId() on $class->_id;
Randomly, I'm getting that class variable now return as NULL, such as:
$log = new EmailEventLog;
$log->data = $event;
$log->email = $email->_id;
$log->save();
print_r(var_dump($log)); // contains correct ->_id variable
print_r(var_dump($log->_id)); // NULL
Nothing happens in between those two printouts, but I cannot access it!
FYI the class EmailEventLog extends M which has a public $_id; set on it. I've tried bringing that up to the natural class and it's not working still.
Thanks
EDIT - weirdly, closing the object and then getting the _id from the clone returns it as expected!
PHP's Mongo driver lacks a renameCommand function. There is reference to do this through the admin database. But it seems more recent versions of the Mongo driver don't let you just "use" the admin database if do don't have login privileges on that database. So this method no longer works. I've also read this doesn't work in sharded environments although this isn't a concern for me currently.
The other suggestion people seem to have is to iterate through the "from" collection and insert into the "to" collection. With the proper WriteConcern (fire and forget) this could be fairly fast. But it still means pulling down each record over the network into the PHP process and then uploading it back over the network back into the database.
I ideally want a way to do it all server-side. Sort of like an INSERT INTO ... SELECT ... in SQL. This way it is fast, network efficient and a low load on PHP.
I have just tested this, it works as designed ( http://docs.mongodb.org/manual/reference/command/renameCollection/ ):
$mongo->admin->command(array('renameCollection'=>'ns.user','to'=>'ns.e'));
That is how you rename an unsharded collection. One problem with MR is that it will change the shape of the output from the original collection. As such it is not very good at copying a collection. You would be better off copying it manually if your collection is sharded.
As an added note I upgraded to 1.4.2 (which for some reason comes out from the pecl channel into phpinfo() as 1.4.3dev :S) and it still works.
Updates:
Removed my old map/reduce method since I found out (and Sammaye pointed out) that this changes the structure
Made my exec version secondary since I found out how to do it with renameCollection.
I believe I have found a solution. It appears some versions of the PHP driver will auth against the admin database even though it doesn't need to. But there is a workaround where the authSource connection param is used to change this behavior so it doesn't auth against the admin database but instead the database of your choice. So now my renameCollection function is just a wrapper around the renameCollection command again.
The key is to add authSource when connecting. In the below code $_ENV['MONGO_URI'] holds my connection string and default_database_name() returns the name of the database I want to auth against.
$class = 'MongoClient';
if( !class_exists($class) ) $class = 'Mongo';
$db_server = new $class($_ENV['MONGO_URI'].'?authSource='.default_database_name());
Here is my older version that used eval which should also work although some environments don't allow you to eval (MongoLab gives you a crippled setup unless you have a dedicated system). But if you are running in a sharded environment this seems like a reasonable solution.
function renameCollection($old_name, $new_name) {
db()->$new_name->drop();
$copy = "function() {db.$old_name.find().forEach(function(d) {db.$new_name.insert(d)})}";
db()->execute($copy);
db()->$old_name->drop();
}
you can use this. "dropTarget" flag is true then delete exist database.
$mongo = new MongoClient('_MONGODB_HOST_URL_');
$query = array("renameCollection" => "Database.OldName", "to" => "Database.NewName", "dropTarget" => "true");
$mongo->admin->command($query);
When ever I try to call store procedure in mysql that sends back a result set, it keeps saying me that "can't return a result set in the given context".
I've google it and some said it's mysql bug, some said you should change your mysqli driver and ....
Situation :
Using mysqli driver Client API library version 5.0.51a , PHP Version 5.2.4-2ubuntu5.6, Using Zend 1.9 RC 1 Mysqli adapter.
What should I do!?
The answer is to upgrade your php, I've just upgraded mine to 5.3.0, and it's works likes Candy!
Not sure this is the solution to your problem, but what about trying with a more recent version of PHP ?
PHP 5.2.4 is definitly quite old -- so, if it's a bug in PHP's mysqli driver, it might have been corrected since...
Actually, after a quick search, it seems a problem like the one you are witnessing has been introduced between PHP 5.2.3 and PHP 5.2.4 (and was still here in PHP 5.2.5).
See bug #42548 : PROCEDURE xxx can't return a result set in the given context (works in 5.2.3!!)
Are you able to test with something like PHP 5.2.9 or 5.2.10 ?
I know these are not provided by Ubuntu, even in the last Ubuntu stable version :-( You might have to compile from sources :-(
Yet another idea would be to try mith PDO_MySql adapter : maybe it would work with that one ?
It might be possible to change Adapter without causing too much trouble / without taking hours to test ?
As you are working with Zend Framework 1.9, here's another post that might interest you, and might be easier to test : stored procedure error after upgrade to 1.8
An easy solution to try that would be to go back to Zend Framework 1.7 ; would it be possible for you, just to test ?
Anyway... Good luck !
And, if you find the solution, don't forget to indicate what the problem was, and how you solved it ;-)
I had this problem recently on a contract. The client was using a codebase on windoze and php 5.2.6 and my installation was linux and php 5.3.1 Whatever we did, they wouldn't co-operate so in the end they gave me a windoze vista machine and we installed php 5.2.6 and off we went. Moral of the story: version matching counts. Weird cus I never had this ever before in any other job. But hey, you can't know everything. Very definitely not a MySql issue, just PHP.
It works perfectly with PHP 5.2.10 as well.
From an earlier version, I've successfully used mysqli::multi_query to call a problematic procedure and get the right results.
I know this question is ancient, but for those still working with 5.2.4 and getting this error, you may consider creating a new mysql PDO object to work around this problem.
I still use 5.2.4 on my dev server to ensure backward compatibility for the WordPress plugins I develop.
Below is a wrapper around procedural calls that I use to successfully call procedures in both 5.2.4 (run on my dev server) , which would normally give me the error, and my production server (which runs a newer version that doesn't give the error) .
Its WordPress specific, but it wouldn't be difficult to modify it using straight php.
/*
* Need to cache connection so we don't keep creating connections till we hit max.
*/
private $_dbhCache=null;
/**
* mySQL Call Proc
*
* Provides a wrapper around calling a mySQL stored procedure to ensure against a 5.2.4 bug that
* causes procedure calls to fail.
* Error:'can't return a result set in the given context'
*
* references:
* http://stackoverflow.com/questions/1200193/cant-return-a-result-set-in-the-given-context
* http://php.net/pdo_mysql#69592 //i got empty result set but pointed me in the right direction
* http://php.net/pdo_mysql#80306 //this worked, but returned 0-indexed and assoc, i edited it so it only returns assoc mimicking $wpdb->get_results(
* http://www.php.net/manual/en/pdo.connections.php
* http://www.php.net/manual/en/pdostatement.fetch.php explains about FETCH_ASSOC
*
* #param string $proc The mySQL stored procedure string, including paramaters, but without the call statement. e.g.: "my_procedure_name('my_paramater')";
* #return string The results of the procedure call
*/
public function mySQLCallProc( $proc ) {
global $wpdb;
$query = "call $proc";
try {
/*
* Attempt to call the procedure normally.
*
*/
$query_result = $wpdb->get_results( $query, ARRAY_A );
/*
* Check for a database error
* and throw an exception if one is found.
* We can then attempt it again using a workaround.
*/
if ( $wpdb->last_error !== '' ) {
throw new Exception( 'Database Error While Calling Procedure' );
}
} catch ( Exception $e ) {
try {
/*
* Create a PDO Object for the connection
*/
if ( is_null($this->_dbhCache)) {
$dbh = new PDO( 'mysql:host=' . DB_HOST . ';port=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASSWORD, array( PDO::ATTR_PERSISTENT => true ) );
$this->_dbhCache=$dbh ;
}else{
$dbh = $this->_dbhCache;
}
/*
* Prepare and call the procedure.
*/
$stmt = $dbh->prepare( "call $proc" );
$stmt->execute();
/*
* fetch all rows into an associative array.
*/
$query_result = $stmt->fetchAll( PDO::FETCH_ASSOC ); //FETCH_ASSOC gets results as an assoc array. without it, you'll receive both assoc and 0-indexed array
} catch ( PDOException $e ) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}
return ($query_result);
}