3939#include <Zend/zend_exceptions.h>
4040#include <ext/spl/spl_iterators.h>
4141#include <ext/spl/spl_exceptions.h>
42+ #include <ext/standard/php_var.h>
43+
44+ #if PHP_VERSION_ID >= 70000
45+ # include <Zend/zend_smart_str.h>
46+ #else
47+ # include <ext/standard/php_smart_str.h>
48+ #endif
49+
4250/* Stream wrapper */
4351#include <main/php_streams.h>
4452#include <main/php_network.h>
@@ -1378,6 +1386,84 @@ static void php_phongo_free_ssl_opt(mongoc_ssl_opt_t *ssl_opt)
13781386 efree (ssl_opt );
13791387}
13801388
1389+ /* Creates a hash for a client by concatenating the URI string with serialized
1390+ * options arrays. On success, a persistent string is returned (i.e. pefree()
1391+ * should be used to free it) and hash_len will be set to the string's length.
1392+ * On error, an exception will have been thrown and NULL will be returned. */
1393+ static char * php_phongo_manager_make_client_hash (const char * uri_string , zval * options , zval * driverOptions , size_t * hash_len TSRMLS_DC )
1394+ {
1395+ char * hash = NULL ;
1396+ smart_str var_buf = {0 };
1397+ php_serialize_data_t var_hash ;
1398+
1399+ #if PHP_VERSION_ID >= 70000
1400+ zval args ;
1401+
1402+ array_init_size (& args , 3 );
1403+ ADD_ASSOC_STRING (& args , "uri" , (char * ) uri_string );
1404+
1405+ if (options ) {
1406+ ADD_ASSOC_ZVAL_EX (& args , "options" , options );
1407+ Z_ADDREF_P (options );
1408+ } else {
1409+ ADD_ASSOC_NULL_EX (& args , "options" );
1410+ }
1411+
1412+ if (driverOptions ) {
1413+ ADD_ASSOC_ZVAL_EX (& args , "driverOptions" , driverOptions );
1414+ Z_ADDREF_P (driverOptions );
1415+ } else {
1416+ ADD_ASSOC_NULL_EX (& args , "driverOptions" );
1417+ }
1418+
1419+ PHP_VAR_SERIALIZE_INIT (var_hash );
1420+ php_var_serialize (& var_buf , & args , & var_hash );
1421+ PHP_VAR_SERIALIZE_DESTROY (var_hash );
1422+
1423+ if (!EG (exception )) {
1424+ * hash_len = ZSTR_LEN (var_buf .s );
1425+ hash = pestrndup (ZSTR_VAL (var_buf .s ), * hash_len , 1 );
1426+ }
1427+
1428+ zval_ptr_dtor (& args );
1429+ #else
1430+ zval * args ;
1431+
1432+ MAKE_STD_ZVAL (args );
1433+ array_init_size (args , 3 );
1434+ ADD_ASSOC_STRING (args , "uri" , (char * ) uri_string );
1435+
1436+ if (options ) {
1437+ ADD_ASSOC_ZVAL_EX (args , "options" , options );
1438+ Z_ADDREF_P (options );
1439+ } else {
1440+ ADD_ASSOC_NULL_EX (args , "options" );
1441+ }
1442+
1443+ if (driverOptions ) {
1444+ ADD_ASSOC_ZVAL_EX (args , "driverOptions" , driverOptions );
1445+ Z_ADDREF_P (driverOptions );
1446+ } else {
1447+ ADD_ASSOC_NULL_EX (args , "driverOptions" );
1448+ }
1449+
1450+ PHP_VAR_SERIALIZE_INIT (var_hash );
1451+ php_var_serialize (& var_buf , & args , & var_hash TSRMLS_CC );
1452+ PHP_VAR_SERIALIZE_DESTROY (var_hash );
1453+
1454+ if (!EG (exception )) {
1455+ * hash_len = var_buf .len ;
1456+ hash = pestrndup (var_buf .c , * hash_len , 1 );
1457+ }
1458+
1459+ zval_ptr_dtor (& args );
1460+ #endif
1461+
1462+ smart_str_free (& var_buf );
1463+
1464+ return hash ;
1465+ }
1466+
13811467static mongoc_client_t * php_phongo_make_mongo_client (const mongoc_uri_t * uri , mongoc_ssl_opt_t * ssl_opt TSRMLS_DC ) /* {{{ */
13821468{
13831469 const char * mongoc_version , * bson_version ;
@@ -1419,10 +1505,38 @@ static mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, mo
14191505
14201506void phongo_manager_init (php_phongo_manager_t * manager , const char * uri_string , zval * options , zval * driverOptions TSRMLS_DC ) /* {{{ */
14211507{
1508+ char * hash = NULL ;
1509+ size_t hash_len = 0 ;
14221510 bson_t bson_options = BSON_INITIALIZER ;
14231511 mongoc_uri_t * uri = NULL ;
14241512 mongoc_ssl_opt_t * ssl_opt = NULL ;
14251513
1514+ #if PHP_VERSION_ID >= 70000
1515+ zval * client_ptr ;
1516+ zval new_client_ptr ;
1517+ #else
1518+ mongoc_client_t * * client_ptr ;
1519+ #endif
1520+
1521+ if (!(hash = php_phongo_manager_make_client_hash (uri_string , options , driverOptions , & hash_len TSRMLS_CC ))) {
1522+ /* Exception should already have been thrown and there is nothing to free */
1523+ return ;
1524+ }
1525+
1526+ #if PHP_VERSION_ID >= 70000
1527+ if ((client_ptr = zend_hash_str_find (& MONGODB_G (clients ), hash , hash_len )) && Z_TYPE_P (client_ptr ) == IS_PTR ) {
1528+ MONGOC_DEBUG ("Found client for hash: %s\n" , hash );
1529+ manager -> client = (mongoc_client_t * )Z_PTR_P (client_ptr );
1530+ goto cleanup ;
1531+ }
1532+ #else
1533+ if (zend_hash_find (& MONGODB_G (clients ), hash , hash_len + 1 , (void * * ) & client_ptr ) == SUCCESS ) {
1534+ MONGOC_DEBUG ("Found client for hash: %s\n" , hash );
1535+ manager -> client = * client_ptr ;
1536+ goto cleanup ;
1537+ }
1538+ #endif
1539+
14261540 if (options ) {
14271541 phongo_zval_to_bson (options , PHONGO_BSON_NONE , & bson_options , NULL TSRMLS_CC );
14281542 }
@@ -1455,9 +1569,22 @@ void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string,
14551569
14561570 if (!manager -> client ) {
14571571 phongo_throw_exception (PHONGO_ERROR_RUNTIME TSRMLS_CC , "Failed to create Manager from URI: '%s'" , uri_string );
1572+ goto cleanup ;
14581573 }
14591574
1575+ MONGOC_DEBUG ("Created client hash: %s\n" , hash );
1576+ #if PHP_VERSION_ID >= 70000
1577+ ZVAL_PTR (& new_client_ptr , manager -> client );
1578+ zend_hash_str_update (& MONGODB_G (clients ), hash , hash_len , & new_client_ptr );
1579+ #else
1580+ zend_hash_update (& MONGODB_G (clients ), hash , hash_len + 1 , & manager -> client , sizeof (mongoc_client_t * ), NULL );
1581+ #endif
1582+
14601583cleanup :
1584+ if (hash ) {
1585+ pefree (hash , 1 );
1586+ }
1587+
14611588 bson_destroy (& bson_options );
14621589
14631590 if (uri ) {
@@ -1723,22 +1850,22 @@ zend_object_iterator *php_phongo_cursor_get_iterator(zend_class_entry *ce, zval
17231850/* {{{ Memory allocation wrappers */
17241851static void * php_phongo_malloc (size_t num_bytes ) /* {{{ */
17251852{
1726- return emalloc (num_bytes );
1853+ return pemalloc (num_bytes , 1 );
17271854} /* }}} */
17281855
17291856static void * php_phongo_calloc (size_t num_members , size_t num_bytes ) /* {{{ */
17301857{
1731- return ecalloc (num_members , num_bytes );
1858+ return pecalloc (num_members , num_bytes , 1 );
17321859} /* }}} */
17331860
17341861static void * php_phongo_realloc (void * mem , size_t num_bytes ) { /* {{{ */
1735- return erealloc (mem , num_bytes );
1862+ return perealloc (mem , num_bytes , 1 );
17361863} /* }}} */
17371864
17381865static void php_phongo_free (void * mem ) /* {{{ */
17391866{
17401867 if (mem ) {
1741- efree (mem );
1868+ pefree (mem , 1 );
17421869 }
17431870} /* }}} */
17441871
@@ -1869,6 +1996,18 @@ PHP_GINIT_FUNCTION(mongodb)
18691996}
18701997/* }}} */
18711998
1999+ #if PHP_VERSION_ID >= 70000
2000+ static void php_phongo_client_dtor (zval * zv )
2001+ {
2002+ mongoc_client_destroy ((mongoc_client_t * ) Z_PTR_P (zv ));
2003+ }
2004+ #else
2005+ static void php_phongo_client_dtor (void * client )
2006+ {
2007+ mongoc_client_destroy (* ((mongoc_client_t * * ) client ));
2008+ }
2009+ #endif
2010+
18722011/* {{{ PHP_MINIT_FUNCTION */
18732012PHP_MINIT_FUNCTION (mongodb )
18742013{
@@ -1890,6 +2029,9 @@ PHP_MINIT_FUNCTION(mongodb)
18902029 /* Initialize libbson */
18912030 bson_mem_set_vtable (& MONGODB_G (bsonMemVTable ));
18922031
2032+ /* Initialize HashTable for persistent clients */
2033+ zend_hash_init (& MONGODB_G (clients ), 0 , NULL , php_phongo_client_dtor , 1 );
2034+
18932035 /* Prep default object handlers to be used when we register the classes */
18942036 memcpy (& phongo_std_object_handlers , zend_get_std_object_handlers (), sizeof (zend_object_handlers ));
18952037 phongo_std_object_handlers .clone_obj = NULL ;
@@ -1956,6 +2098,10 @@ PHP_MSHUTDOWN_FUNCTION(mongodb)
19562098{
19572099 (void )type ; /* We don't care if we are loaded via dl() or extension= */
19582100
2101+ /* Destroy HashTable for persistent clients. The HashTable destructor will
2102+ * destroy any mongoc_client_t objects contained within. */
2103+ zend_hash_destroy (& MONGODB_G (clients ));
2104+
19592105 bson_mem_restore_vtable ();
19602106 /* Cleanup after libmongoc */
19612107 mongoc_cleanup ();
0 commit comments