class = get_class($this); static::$cfgDdb = null; static::$cfgBroker = null; static::$cfgLogger = null; static::$brokerAvailable = false; static::$segundoAvailable = false; static::$terceroAvailable = false; static::$adminAvailable = false; // copy the resource configuration blocks into their respective containers if (!empty(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_DDB][CONFIG_DATABASE_DDB_APPSERVER])) { static::$cfgDdb = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_DDB][CONFIG_DATABASE_DDB_APPSERVER]; } else { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_CONFIG_RESOURCE_404 . RESOURCE_DDB)); return; } if (!empty(gasConfig::$settings[CONFIG_SECURITY])) { static::$cfgSecurity = gasConfig::$settings[CONFIG_SECURITY]; } if (!empty(gasConfig::$settings[CONFIG_CACHE])) { static::$cfgCache = gasConfig::$settings[CONFIG_CACHE]; } if (!empty(gasConfig::$settings[CONFIG_BROKER_SERVICES])) { static::$cfgBroker = gasConfig::$settings[CONFIG_BROKER_SERVICES]; static::$certPath = static::$cfgBroker[CONFIG_BROKER_CERT_PATH]; static::$certFile = static::$certPath . DIR_SSL_CLIENT . FILE_LOCAL_KEYCERT; static::$caFile = static::$certPath . DIR_SSL_RMQ . FILE_CA_CERT; } if (!empty(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_PDO])) { static::$cfgPDO = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_PDO]; static::$PDOEnabled = static::$cfgPDO[CONFIG_DATABASE_PDO_ENABLED]; } if (!empty(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_MONGODB])) { static::$cfgMongo = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_MONGODB]; static::$mongoEnabled = static::$cfgMongo[CONFIG_DATABASE_MONGODB_ENABLED]; } if (!empty(gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_ADMIN])) { static::$cfgAdmin = gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_ADMIN]; } if (!empty(gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_BROKER_WH])) { static::$cfgSegundo = gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_BROKER_WH]; } // checkpoint static::internalLog(INFO_CKP_CONFIG_LOADED); try { // all configs are loaded - instantiate the logger class for resourceCheck() static::$logger = new gacErrorLogger(); // have to init the cache resource b/c PHP's faux statics... @gasResourceManager::fetchResource(RESOURCE_CACHE); // meat-n-taters part -- get/assign links to the external resources if (!static::$resourcesChecked) { static::resourceCheck(); static::$resourcesChecked = true; } static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, 'resources checked and loaded')); } catch (Throwable $t) { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage())); return; } // validate the resource acquisitions and output the status of each // DDB: static::internalLog(sprintf(basename(__FILE__), __LINE__, 'Validating resources...')); if (static::$cfgDdb[CONFIG_DATABASE_DDB_ENABLED] and !static::$ddbAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_DDB); static::internalLog($msg); return; } elseif (static::$cfgDdb[CONFIG_DATABASE_DDB_ENABLED]) { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, RESOURCE_DDB . STUB_VALIDATED)); } // Memcached: if (!static::$cacheAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_CACHE); static::internalLog($msg); } else { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, RESOURCE_CACHE . STUB_VALIDATED)); } // namaste primary "broker" is always required if (!static::$brokerAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_BROKER); static::internalLog($msg); return; } else { static::internalLog(sprintf(basename(__FILE__), __LINE__, RESOURCE_BROKER . STUB_VALIDATED)); } // namaste admin broker is always required if (!static::$adminAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_ADMIN); static::internalLog($msg); return; } else { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, RESOURCE_ADMIN . STUB_VALIDATED)); } // namaste secondary service is optional if (gasConfig::$settings[RESOURCE_SEGUNDO] === 1 and !static::$segundoAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_SEGUNDO); static::internalLog($msg); return; } elseif (static::$segundoAvailable) { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, RESOURCE_SEGUNDO . STUB_VALIDATED)); } if (gasConfig::$settings[RESOURCE_TERCERO] === 1 and !static::$terceroAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_TERCERO); static::internalLog($msg); return; } elseif (static::$terceroAvailable) { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, RESOURCE_TERCERO . STUB_VALIDATED)); } // for PDO, it's required only if PDO driver for mariaDB has been enabled if (static::$PDOEnabled and !static::$PDOAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_PDO_MASTER); static::internalLog($msg); return; } elseif (static::$PDOEnabled) { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, RESOURCE_PDO_MASTER . STUB_VALIDATED)); } // same for mongo master - it's required only if it's been enabled for the current configuration if (static::$mongoEnabled and !static::$mongoMasterAvailable) { static::$IPL = false; $msg = sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, ERROR_RESOURCE_404 . RESOURCE_MONGO_MASTER); static::internalLog($msg); return; } elseif (static::$mongoEnabled) { static::internalLog(sprintf(INFO_CKP_REACHED, basename(__FILE__), __LINE__, RESOURCE_MONGO_MASTER . STUB_VALIDATED)); } static::internalLog('Resource load completed successfully......'); } /** * resourceCheck() -- private static method * * this method is called from the constructor function (only) and validates all that all of the necessary resources * are available by instantiation. * * by default, all of the resources are initialized to false (not available) -- once a resource is instantiated, * then we toggle the class static resource-availability variable to true. * * there are no input parameters or returns. * resources are immediately (and properly) de-allocated. * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 06-12-17 mks original coding * 06-29-17 mks CORE-458: mysql resource support * 07-02-17 mks CORE-464: mongo resource support * 09-12-17 mks CORE-561: PDO resource support to replace mysql resource support * 05-31-18 mks CORE-1011: update for new XML broker services configuration * 07-17-18 mks _INF-134: added console log messages to help with deployment/first-run diagnostics * 07-24-18 mks CORE-1099: deprecating mongo slave resource use * 07-27-18 mks CORE-1108: console logging alternative (use logging if available) * */ private static function resourceCheck() { try { if (intval(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_DDB][CONFIG_DATABASE_DDB_ENABLED]) == 1) { static::internalLog(INFO_FETCHING_RESOURCE . RESOURCE_DDB); @static::fetchResource(RESOURCE_DDB); } static::internalLog(INFO_FETCHING_RESOURCE . RESOURCE_CACHE); /** @var memcached $c */ @static::fetchResource(RESOURCE_CACHE); if (!static::$cacheAvailable) { consoleLog(static::$myID, CON_ERROR, ERROR_RESOURCE_404 . RESOURCE_CACHE); } if (static::$cfgPDO[CONFIG_DATABASE_PDO_ENABLED]) { static::internalLog(INFO_FETCHING_RESOURCE . RESOURCE_PDO_MASTER); @static::fetchResource(RESOURCE_PDO_MASTER); if (!static::$PDOAvailable) { consoleLog(static::$myID, CON_ERROR, ERROR_RESOURCE_404 . RESOURCE_PDO_MASTER); } } if (static::$cfgMongo[CONFIG_DATABASE_MONGODB_ENABLED]) { static::internalLog(INFO_FETCHING_RESOURCE . RESOURCE_MONGO_MASTER); @static::fetchResource(RESOURCE_MONGO_MASTER); if (!static::$mongoMasterAvailable) consoleLog(static::$myID, CON_ERROR, ERROR_RESOURCE_404 . RESOURCE_MONGO_MASTER); } foreach (static::$brokerServices as $brokerService) { if (isset(gasConfig::$settings[CONFIG_BROKER_SERVICES][$brokerService]) and is_array(gasConfig::$settings[CONFIG_BROKER_SERVICES][$brokerService])) { static::internalLog(INFO_FETCHING_RESOURCE . $brokerService); $r = static::fetchResource($brokerService); if (!is_object($r)) { $msg = ERROR_RESOURCE_404 . $brokerService; if (!is_null(static::$logger) and static::$logger->available) { static::$logger->fatal($msg); } consoleLog(static::$myID, CON_ERROR, '(' . __LINE__ . ')' . $msg); } unset($r); } } } catch (Throwable $t) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage(); static::internalLog($hdr . $msg); } } /** * fetchResource() -- public static method * * this is the method used to fetch resources objects for the various end-points (nosql, rabbit, cache, etc.) * * the input parameter to the method defines, by constant, which resource to fetch. * * Because the various resources are varying types, (resource, object, http-connection, etc.), the responsibility * on validating the return type is on the client. If the resource could not be attained, or the request was * improper, then we're going to return a null value to the calling client. * * Programmer's Notes: * ------------------- * This method is called from the constructor so, at first glance, it may seem odd that we're explicitly returning * static variables within the class -- however, this method is also used within the framework for ad-hoc resource * requests which is why we're explicitly returning the static. That, and also if PHP ever does truly support * true/pure static classes, then we'll be ready. * * @author mike@givingassistant.org * @version 1.0 * * @param string $_resourceType * @param string $_location -- only used by gacLogger class * @return null|object * * HISTORY: * ======== * 06-12-17 mks original coding (estimate) * 07-13-17 mks updated for service locations * 09-12-17 mks CORE-561: PDO driver replaces mysqli * 04-11-18 mks _INF_188: refactored for segundo broker (warehousing), typeError trapping, better logging * 07-24-18 mks CORE-1099: deprecated mongodb slave resource use * 08-28-18 mks DB-50: lite initialization support * 07-29-20 mks DB-156: tercero support * */ public static function fetchResource(string $_resourceType, string $_location = ENV_APPSERVER): ?object { try { switch ($_resourceType) { case RESOURCE_DDB : // we do not return a resource - just a connection to the remote http service static::getDdbResource(); return static::$resDdb; break; // RabbitMQ Broker Resources (not database) case RESOURCE_ADMIN : case RESOURCE_BROKER : case RESOURCE_SEGUNDO : case RESOURCE_TERCERO : try { return (static::getBrokerResource($_resourceType)); } catch (TypeError $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_TYPE_EXCEPTION; static::internalLog($msg); static::internalLog($t->getMessage()); } break; case RESOURCE_PDO_MASTER : // check, for lite initialization, if the config has been loaded if (empty(static::$cfgPDO) and !static::$resourcesChecked) { static::$cfgPDO = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_PDO]; if (empty(static::$cfgPDO)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_CONFIG_RESOURCE_404 . CONFIG_DATABASE_PDO; static::internalLog($msg); return null; } } // resource must be "turned on" in the XML config otherwise reject the request if (1 != static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][STRING_ENABLED]) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_RESOURCE_404 . RESOURCE_PDO_MASTER; static::internalLog($msg); return null; } if (is_object(static::$resPDO) and static::$PDOAvailable) return static::$resPDO; try { static::$resPDO = static::getPDOResourceMaster(ENV_APPSERVER); static::$PDOAvailable = (is_null(static::$resPDO)) ? false : true; return static::$resPDO; } catch (TypeError $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_TYPE_EXCEPTION; static::internalLog($msg); static::internalLog($t->getMessage()); } break; case RESOURCE_WH_COOL_PDO_MASTER : // resource must be "turned on" in the XML config otherwise reject the request if (1 != static::$cfgPDO[CONFIG_COOL_STORAGE][STRING_ENABLED]) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_RESOURCE_404 . RESOURCE_WH_COOL_PDO_MASTER; static::internalLog($msg); return null; } try { static::$resPDOWHMaster = static::getPDOResourceMaster(ENV_SEGUNDO); static::$PDOWHMasterAvailable = (is_null(static::$resPDOWHMaster)) ? false : true; return static::$resPDOWHMaster; } catch (TypeError $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_TYPE_EXCEPTION; static::internalLog($msg); static::internalLog($t->getMessage()); } break; case RESOURCE_PDO_SECONDARY : if (empty(static::$cfgPDO) and !static::$resourcesChecked) { static::$cfgPDO = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_PDO]; if (empty(static::$cfgPDO)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_CONFIG_RESOURCE_404 . CONFIG_DATABASE_PDO; static::internalLog($msg); return null; } } // resource must be "turned on" in the XML config otherwise reject the request if (1 != static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][STRING_ENABLED]) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_RESOURCE_404 . RESOURCE_PDO_SECONDARY; static::internalLog($msg); return null; } elseif (isset(static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][CONFIG_DATABASE_PDO_USE_SECONDARY]) and 1 != static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][CONFIG_DATABASE_PDO_USE_SECONDARY]) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_PDO_SLAVE; static::internalLog($msg); return null; } if (is_object(static::$resPDOSlave) and static::$PDOSlaveAvailable) return static::$resPDOSlave; try { static::$resPDOSlave = static::getPDOResourceSlave(ENV_APPSERVER); static::$PDOSlaveAvailable = (is_null(static::$resPDOSlave)) ? false : true; return static::$resPDOSlave; } catch (TypeError $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_TYPE_EXCEPTION; static::internalLog($msg); static::internalLog($t->getMessage()); } break; case RESOURCE_MONGO_MASTER : try { static::$location = $_location; switch (static::$location) { case ENV_APPSERVER : static::getMongoResource(MONGO_MASTER); return static::$resMongoMaster; break; case ENV_ADMIN : static::getMongoResource(MONGO_ADMIN_MASTER); return static::$resMongoAdminMaster; break; case ENV_SEGUNDO : static::getMongoResource(MONGO_SEGUNDO_MASTER); return static::$resMongoSegundoMaster; break; case ENV_TERCERO : static::getMongoResource(MONGO_TERCERO_MASTER); return static::$resMongoTerceroMaster; break; default : $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_ENV_INVALID . static::$location; static::internalLog($msg); return null; break; } } catch (Throwable | TypeError $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_TYPE_EXCEPTION; static::internalLog($msg); static::internalLog($t->getMessage()); } break; case RESOURCE_CACHE : static::$resCache = gasCache::singleton(); if (is_object(static::$resCache)) static::$cacheAvailable = true; return static::$resCache; break; default: $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_RESOURCE_TYPE_UNDEF, $_resourceType); static::internalLog($msg); break; } } catch (Throwable $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), $t->getLine()) . ERROR_THROWABLE_EXCEPTION; static::internalLog($msg); static::internalLog(sprintf(FAIL_RESOURCE_LOAD, $_resourceType, $_location)); static::internalLog($t->getMessage()); } return null; } /** * reconnect() -- public method * * This method requires one input parameter - the named resource that we'll attempt to reconnect to. * * When we lose a resource connection, we'll call this method so that we can log the dropped-resource event. * * todo: make logging a system event * * This is a pass-through method to fetchResource() (in this class); this method is the preferred method for * re-establishing a dropped-resource connection because of the information logging. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_resource -- a well-defined Namaste resource * @return null|object -- returns the PDO resource on success, null on error * * * HISTORY: * ======== * 01-16-18 mks CORE-697: original coding * */ public static function reconnect(string $_resource) { static::internalLog(sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_PDO_RECONNECT); try { return (static::fetchResource($_resource)); } catch (TypeError $t) { static::internalLog(sprintf(INFO_LOC,basename(__FILE__), __LINE__, ERROR_TYPE_EXCEPTION)); static::internalLog($t->getMessage()); } return null; } /** * getDdbResource() -- private method * * reads the current configuration for the no-sql (Dynamo DB) resource and, if not set, will open a (HTTP) * connection to the resource as defined in the xml configuration. * * Calculated values are implicitly returned to the calling client via class member population. * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 06-12-17 mks original coding * */ private static function getDdbResource() { $config = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_DDB][CONFIG_DATABASE_DDB_APPSERVER]; $endpoint = $config[CONFIG_DATABASE_DDB_DSN] . ':' . $config[CONFIG_DATABASE_DDB_PORT]; $region = $config[CONFIG_DATABASE_DDB_REGION]; $version = $config[CONFIG_DATABASE_DDB_VERSION]; $credentials = [ STRING_KEY => $config[CONFIG_DATABASE_DDB_KEY_ID], STRING_SECRET => $config[CONFIG_DATABASE_DDB_ACCESS_KEY] ]; if (is_null(static::$resDdb)) { try { // connect to the aws sdk $awsSDK = new Aws\Sdk([ STRING_ENDPOINT => $endpoint, STRING_REGION => $region, STRING_CREDS => $credentials, STRING_VERSION => $version ]); // todo -- what does this return and can we trap an error? static::$ddbAvailable = true; static::$resDdb = $awsSDK->createDynamoDb(); } catch (Aws\Exception\AwsException | Throwable $t) { static::internalLog(sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_DDB_INSTANTIATE); static::internalLog($t->getMessage()); } } } /** * getBrokerResource() -- private static function * * this method initializes a connection to the RabbitMQ service as defined in the configuration file(s) and * establishes the broker availability and resource. * * there is an input parameter to the method which is the name of which broker resource we're going to initialize. * These tags are limited, at the time this method was written, to the following: * * BROKER -- unimaginative name which indicates the local, primary, namaste AMQP resource * SEGUNDO -- the second (optional) namaste resource, a remote namaste instance * TERCERO -- the third (optional) namaste resource, also a remote instance * ADMIN -- the namaste administrative service which could also be a remote instance * * first step is to evaluate which configuration was passed so we can load the appropriate configuration (XML) * in the correct container. If an invalid config was passed, record the error and return a Boolean(false). * * next, load-up the basic access-data values (x5) from the current configuration... * * next, check to see if the current configuration requires an SSL connection to the resource. If it does, * make a quick check to ensure that the certificate files are accessible by namaste and then attempt to * make the SSL connection. * * If the config does not require SSL, make a non-SSL connection. (Note: we're using AMQPStreamConnection for * the first time instead of the generic AMQPConnection type.) Also, because of an inherent PHP bug, only * pass-in the heartbeat and the keep-alive options for the non-SSL connection. * * todo -- see if the SSL heartbeat and keepalive bugs were fixed in PHP7.0 * * the connect request is exception wrapped and will post a log message * * On successful connect, we mark the resource as available, otherwise error messages are sent and the resource * is explicitly marked as unavailable prior to returning. * * PROGRAMMER NOTES: * ----------------- * In previous incarnations of this module, there were explicit connector methods for each type of broker * resource. This incarnation deprecates the separate methods as all four broker resources are now initialized * by this single method. Progress. * * * @param string $_config * @return bool|AMQPStreamConnection|AMQPStreamConnection * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 06-12-17 mks original coding * 05-31-18 mks CORE-1011: update for new XML broker services configuration * */ private static function getBrokerResource(string $_config) { $rabbitResource = false; $options = null; $currentConfig = null; if (!empty(gasConfig::$settings[CONFIG_BROKER_SERVICES][$_config])) { switch ($_config) { case CONFIG_BROKER_APPSERVER: static::$cfgBroker = gasConfig::$settings[CONFIG_BROKER_SERVICES][$_config]; $currentConfig = static::$cfgBroker; break; case CONFIG_BROKER_WH: static::$cfgSegundo = gasConfig::$settings[CONFIG_BROKER_SERVICES][$_config]; $currentConfig = static::$cfgSegundo; break; case CONFIG_BROKER_TERCERO: static::$cfgTercero = gasConfig::$settings[CONFIG_BROKER_SERVICES][$_config]; $currentConfig = static::$cfgTercero; break; case CONFIG_ADMIN: static::$cfgAdmin = gasConfig::$settings[CONFIG_BROKER_SERVICES][$_config]; $currentConfig = static::$cfgAdmin; break; default: $msg = sprintf(basename(__FILE__), __LINE__) . ERROR_BROKER_TYPE_UNDEF . $_config; static::internalLog($msg); return false; break; } } else { $rabbitResource = false; $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_CONFIG_RESOURCE_404 . $_config; static::internalLog($msg); return $rabbitResource; } if (!empty($currentConfig)) { // ensure we have a broker configuration... $user = $currentConfig[STRING_USER]; // and load the basic MQ connection params $pass = $currentConfig[STRING_PASS]; $host = $currentConfig[STRING_HOST]; $port = $currentConfig[STRING_PORT]; $vhost = gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_BROKER_VHOST]; if (gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_BROKER_USE_SSL] and gasConfig::$settings[CONFIG_SECURITY][CONFIG_SECURITY_REQUIRES_TLS][CONFIG_SECURITY_REQUIRES_TLS_MQ] == 1) { try { // if the configuration requires SSL, make a secure connection to the resource. if (!static::checkCerts()) return false; // build the ssl-options array and request a broker resource from the resource manager $ssl_opts = [STRING_CA_FILE => static::$caFile, STRING_LOCAL_CERT => static::$certFile]; $rabbitResource = new AMQPSSLConnection($host, $port, $user, $pass, $vhost, $ssl_opts, $options); } catch (Throwable $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_RESOURCE_404 . RESOURCE_BROKER; static::internalLog($msg); static::internalLog($t->getMessage()); return false; } } else { // non-ssl broker connection try { $tv = gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_BROKER_TIMER_VIOLATION]; $ka = gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_BROKER_KEEP_ALIVE]; $ka = ($ka === 1) ? true : false; $hb = gasConfig::$settings[CONFIG_BROKER_SERVICES][CONFIG_BROKER_HEARTBEAT]; $rabbitResource = new AMQPStreamConnection($host, $port, $user, $pass, $vhost, false, STRING_AMQPLAIN, null, LOCALE, $tv, $tv, null, $ka, $hb); } catch (Throwable $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_RESOURCE_404 . RESOURCE_BROKER; static::internalLog($msg); static::internalLog($t->getMessage()); return false; } } } switch ($_config) { case CONFIG_BROKER_APPSERVER: static::$brokerAvailable = is_object($rabbitResource); static::$resBroker = $rabbitResource; break; case CONFIG_BROKER_WH: static::$segundoAvailable = is_object($rabbitResource); static::$resSegundo = $rabbitResource; break; case CONFIG_BROKER_TERCERO: static::$terceroAvailable = is_object($rabbitResource); static::$resTercero = $rabbitResource; break; case CONFIG_ADMIN: static::$adminAvailable = is_object($rabbitResource); static::$resAdmin = $rabbitResource; break; } return $rabbitResource; } /** * checkCerts() -- private static method * * this method was embedded in the resource-initialization method for starting a broker -- since we can now * use this code twice (once for the localhost broker and once for the localhost vault), it was broken out * into a private method and improved a bit. * * basically the method just checks to see if the key-cert and ca files are accessible. if they both are, * then the method will return a Boolean(true). If either, or both, are not available, then a Boolean(false) * is return and an error message is written to the console and, if available, to the global log file. * * There are no inputs to the method. * * @author mike@givingassistant.org * @version 1.0 * * @return bool * * * HISTORY: * ======== * 06-26-15 mks original coding * */ private static function checkCerts(): bool { $goodCerts = false; $pass1 = false; // verify that we can "see" the ssl client key-cert file if (!file_exists(static::$certFile)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . STRING_ERROR . ERROR_FILE_404 . static::$certFile; static::internalLog($msg); } else { $pass1 = true; } // verify that we can "see" the certificate-authority file if (!file_exists(static::$caFile)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . STRING_ERROR . ERROR_FILE_404 . static::$caFile; static::internalLog($msg); } elseif ($pass1) { $goodCerts = true; } return $goodCerts; } /** * getPDOResourceMaster() -- private static method * * this method checks to see if a mysql-pdo master resource exists and, if not, creates one. * * errors encountered are logged as fatals and the PDO-Master availability is set to false, and the * PDO resource is set to null. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_which -- mandatory and must be defined as ENV_PRIME, ENV_SEGUNDO or ENV_TERCERO * @return null|PDO * * * HISTORY: * -------- * 06-29-17 mks original coding * 08-17-17 mks CORE-561: in with PDO, out with mysqli * 05-07-18 mks _INF-188: WH support, PHP7 header, explicit return * */ private static function getPDOResourceMaster(string $_which): ?PDO { $PDOResource = null; $validEnvs = [ ENV_APPSERVER, ENV_SEGUNDO, ENV_TERCERO ]; if (!in_array($_which, $validEnvs)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_RESOURCE_TYPE_UNDEF, $_which); static::internalLog($msg); return null; } // exit if we've not loaded a the PDO configuration if (empty(static::$cfgPDO)) { $msg = sprintf(basename(__FILE__), __LINE__) . ERROR_CONFIG_RESOURCE_404 . RESOURCE_PDO_MASTER; static::internalLog($msg); return null; } // qualify and populate based on the environment requested // "default" settings are for ENV_PRIME (namaste) $namastePDO = static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][CONFIG_DATABASE_PDO_MASTER]; $db = 'dbname=' . gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH . $namastePDO[CONFIG_DATABASE_PDO_DB]; switch ($_which) { case ENV_APPSERVER : if (static::$resourcesChecked and static::$PDOAvailable and !is_null(static::$resPDO)) { // if services are already flagged as available, query the PDO resource to confirm static::$PDOAvailable = (is_null(static::$resPDO->getAttribute(PDO::ATTR_CONNECTION_STATUS))) ? false : true; if (static::$PDOAvailable) return static::$resPDO; } break; case ENV_SEGUNDO : if (static::$PDOWHMasterAvailable and !is_null(static::$resPDOWHMaster)) { static::$PDOWHMasterAvailable = (is_null(static::$resPDOWHMaster->getAttribute(PDO::ATTR_CONNECTION_STATUS))) ? false : true; if (static::$PDOWHMasterAvailable) return static::$resPDOWHMaster; } $namastePDO = static::$cfgPDO[CONFIG_COOL_STORAGE][CONFIG_DATABASE_PDO_MASTER]; $db = 'dbname=' . gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH . $namastePDO[CONFIG_DATABASE_PDO_DB]; break; } $pdoAttributes = [ PDO::ATTR_EMULATE_PREPARES => false, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => 1, // PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; // if we failed to verify the connection, we've marked the availability as false, fall-thru and attempt to reconnect // if ((!static::$PDOAvailable) and static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][CONFIG_DATABASE_PDO_ENABLED]) { $host = $namastePDO[CONFIG_DATABASE_PDO_HOSTNAME]; $user = $namastePDO[CONFIG_DATABASE_PDO_USERNAME]; $pass = $namastePDO[CONFIG_DATABASE_PDO_PASSWORD]; $port = $namastePDO[CONFIG_DATABASE_PDO_PORT]; // connect to mariaDB $PDOConnectString = 'mysql:host=' . $host . COLON_NS . $port . SEMI . $db . SEMI . $namastePDO[CONFIG_DATABASE_PDO_CHARSET]; if (static::$debug) static::$logger->debug('PDO-Connection: ' . $PDOConnectString); try { $PDOResource = new PDO($PDOConnectString, $user, $pass, $pdoAttributes); } catch (PDOException | Throwable $e) { if (isset(static::$logger) and static::$logger->available) { static::$logger->fatal(ERROR_PDO_CONNECT); static::$logger->fatal($e->getMessage()); } $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_PDO_CONNECT; static::internalLog($msg); static::internalLog($e->getMessage()); return null; } // } return $PDOResource; } /** * getPDOResourceSlave() -- private static method * * this method checks to see if a mysql-pdo slave resource exists and, if not, creates one. * * errors encountered are logged as fatals and the PDO-Slave availability is set to false, and the * PDO resource is set to null. * * https://secure.php.net/manual/en/mysqlnd-ms.quickstart.usage.php * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_env * @return null|PDO * * * HISTORY: * -------- * 09-15-17 mks CORE-562: original coding * 05-07-18 mks _INF-188: WH Support, PHP7 Header, explicit resource return * */ private static function getPDOResourceSlave(string $_env = ENV_APPSERVER): ? PDO { $PDOResource = null; $validEnvs = [ ENV_APPSERVER, ENV_SEGUNDO, ENV_TERCERO ]; if (!in_array($_env, $validEnvs)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_RESOURCE_TYPE_UNDEF, $_env); static::internalLog($msg); return null; } // exit if we've not loaded a the PDO configuration if (empty(static::$cfgPDO)) { $msg = sprintf(basename(__FILE__), __LINE__) . ERROR_CONFIG_RESOURCE_404 . RESOURCE_PDO_MASTER; static::internalLog($msg); static::$PDOSlaveAvailable = false; static::$resPDOSlave = null; return null; } // qualify and populate based on the environment requested // "default" settings are for ENV_PRIME (namaste) $slaveCFG = static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][CONFIG_DATABASE_PDO_SECONDARY]; $dbName = static::$cfgPDO[CONFIG_DATABASE_PDO_APPSERVER][CONFIG_DATABASE_PDO_MASTER][CONFIG_DATABASE_PDO_DB]; $db = 'dbname=' . gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH . $dbName; switch ($_env) { case ENV_APPSERVER : if (static::$PDOSlaveAvailable and !is_null(static::$resPDOSlave)) { // if services are already flagged as available, query the PDO resource to confirm static::$PDOSlaveAvailable = (is_null(static::$resPDOSlave->getAttribute(PDO::ATTR_CONNECTION_STATUS))) ? false : true; if (static::$PDOSlaveAvailable) return static::$resPDOSlave; } break; case ENV_SEGUNDO : if (static::$PDOWHSlaveAvailable and !is_null(static::$resPDOWHSlave)) { static::$PDOWHSlaveAvailable = (is_null(static::$resPDOWHSlave->getAttribute(PDO::ATTR_CONNECTION_STATUS))) ? false : true; if (static::$PDOWHSlaveAvailable) return static::$resPDOWHSlave; } $slaveCFG = static::$cfgPDO[CONFIG_COOL_STORAGE][CONFIG_DATABASE_PDO_SECONDARY]; $dbName = static::$cfgPDO[CONFIG_COOL_STORAGE][CONFIG_DATABASE_PDO_MASTER][CONFIG_DATABASE_PDO_DB]; $db = 'dbname=' . gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH . $dbName; break; } $pdoAttributes = [ PDO::ATTR_EMULATE_PREPARES => false, // PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::FETCH_ASSOC => PDO::FETCH_ASSOC ]; // check to see if the PDO read-slave service is enabled and, if so, connect. $host = $slaveCFG[CONFIG_DATABASE_PDO_HOSTNAME]; $user = $slaveCFG[CONFIG_DATABASE_PDO_USERNAME]; $pass = $slaveCFG[CONFIG_DATABASE_PDO_PASSWORD]; $port = $slaveCFG[CONFIG_DATABASE_PDO_PORT]; $PDOConnectString = 'mysql:host=' . $host . COLON_NS . $port . SEMI . $db . SEMI . $slaveCFG[CONFIG_DATABASE_PDO_CHARSET]; if (static::$debug) static::$logger->debug('PDO-Connection: ' . $PDOConnectString); try { $PDOResource = new PDO($PDOConnectString, $user, $pass, $pdoAttributes); } catch (PDOException | Throwable $e) { if (isset(static::$logger) and static::$logger->available) { static::$logger->fatal(ERROR_PDO_CONNECT); static::$logger->fatal($e->getMessage()); } else { static::internalLog(sprintf(basename(__FILE__), __LINE__) . ERROR_PDO_CONNECT); static::internalLog($e->getMessage()); } return null; } return $PDOResource; } /** * getMongoResource() -- private method * * the method has an optional parameter which establishes the target resource as being either the mongo master * (default) the mongo slave, the mongo WH master, or the mongo WH slave. * * A second, also optional, parameter is only used for warehousing resources -- specifies the level of * warehousing (COOL, COLD, WARM, etc.) and this is used to ensure we're pulling the correct XML section * from the configuration. * * this method checks to see if a mongo-configuration exists, and if so, has the mongo service been enabled. If * the former is missing, or the latter is not enabled, then post an error and return; * * continue by scraping the XML configuration to build the connectivity string that we'll pass to the MongoDB * Manager driver. Include the replSet configuration if enabled. * * We attempt to connect to the mongo service and store the connection resource as a class property object. * The connection attempt is exception wrapped so any failures will be recorded. * * A successful connection will set the class property $resMongoMaster to the mongo resource whereas any error * will result in this value being set to null. * * A successful connection will set the class property $mongoMasterAvailable to Boolean(true) whereas any error * will result in this value being set to Boolean(false). * * Programmer's Notes: * ------------------- * as of now, there's no support coded for SSL connectivity * user passwords are located in the database specified in the XML file which may not be the current db * * the only difference between a master and a slave (read-only) connection for mongo is the readPreference * setting in the $options array. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_which -- indicates resource allocation for either master or slave defaulting to master * @param string $_whLevel -- should be either COOL, COLD, or WARM or HOT * * * HISTORY: * ======== * 07-05-17 mks CORE-464: original coding * 07-13-17 mks support for multiple locations (namaste, admin, etc.) * updated exception handling for PHP 7 * 10-02-17 mks CORE-572: updated mongo read-preferences for 3.2.17, exception logging * 11-27-17 mks CORE-635: sharded-cluster, repl-set, stand-alone instance connection options * 05-05-18 mks _INF-188: support for mongo WH resources, converted error messaging to console log method * 07-03-18 mks CORE-1053: testing a successful mongoDB connection * 08-24-18 mks CORE-1097: refactoring mongo resources (RBAC, RP) * 08-24-18 mks CORE-1099: deprecation mongodb slave resource handling * 08-28-18 mks DB-50: lite configuration (no bootstrap) support * 07-29-20 mks DB-156: tercero support * 12-10-20 mks DB-180: support for segundo (non warehousing broker) * */ private static function getMongoResource(string $_which = MONGO_MASTER, string $_whLevel = ''): void { // todo - system table for these variables $validLocations = [ ENV_APPSERVER, ENV_ADMIN, ENV_SEGUNDO, ENV_TERCERO ]; $validWHLevels = [ WH_TYPE_COOL, WH_TYPE_HOT, WH_TYPE_WARM, WH_TYPE_COLD ]; $validWhich = [ MONGO_MASTER, MONGO_WH_MASTER, MONGO_SEGUNDO_MASTER, MONGO_ADMIN_MASTER, MONGO_TERCERO_MASTER ]; $errors = []; // validate the which param if (!in_array($_which, $validWhich)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_MONGO_RESOURCE_INVALID, $_which); static::internalLog($msg); return; } // is set, validate the wh-level if (!empty($_whLevel) and !in_array($_whLevel, $validWHLevels)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_MONGO_WH_LEVEL_INVALID, $_whLevel); static::internalLog($msg); return; } // validate which mongo service location if (!in_array(static::$location, $validLocations)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_MONGO_LOCATION_INVALID, static::$location); static::internalLog($msg); return; } // cannot continue without a mongo configuration -- if not set (lite initialization) then attempt to load if (empty(static::$cfgMongo)) { $msg = ''; // attempt to reload the config if (!empty(gasConfig::$settings[CONFIG_DATABASE])) { static::$cfgMongo = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_MONGODB]; if (empty(static::$cfgMongo)) $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_CONFIG_RESOURCE_404 . RESOURCE_MONGO_MASTER; } else { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_CONFIG_RESOURCE_404 . RESOURCE_MONGO_MASTER; } if (strlen($msg)) { static::internalLog($msg); return; } } // validate that the service config exists if (empty(static::$cfgMongo[static::$location])) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_MONGO_LOCATION_DNE, static::$location); static::internalLog($msg); return; } // validate that the requested env been enabled in the configuration if (static::$location == ENV_SEGUNDO and $_which == MONGO_WH_MASTER) { if (empty($_whLevel)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_MONGO_WH_LEVEL_404; static::internalLog($msg); return; } switch ($_whLevel) { case WH_TYPE_COOL : static::$location = CONFIG_COOL_STORAGE; break; case WH_TYPE_COLD : static::$location = CONFIG_COLD_STORAGE; break; case WH_TYPE_WARM : static::$location = CONFIG_WARM_STORAGE; break; case WH_TYPE_HOT : static::$location = CONFIG_HOT_STORAGE; break; } } if (intval(static::$cfgMongo[static::$location][CONFIG_DATABASE_MONGODB_ENABLED]) !== 1) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_MONGO_NOT_ENABLED, static::$location); static::internalLog($msg); return; } // validate which mongo node to connect to and reset those resources switch ($_which) { case MONGO_MASTER : static::$resMongoMaster = null; static::$mongoMasterAvailable = false; $service = ENV_APPSERVER; break; case MONGO_SEGUNDO_MASTER : static::$resMongoSegundoMaster = null; static::$mongoSegundoMasterAvailable = false; $service = ENV_SEGUNDO; break; case MONGO_WH_MASTER : static::$resMongoWHMaster = null; static::$mongoWHMasterAvailable = false; $service = ENV_SEGUNDO; break; case MONGO_ADMIN_MASTER : if (intval(gasConfig::$settings[ENV_ADMIN][CONFIG_ACTIVE]) !== 1) { if (isset(static::$logger) and static::$logger->available) { $msg = sprintf(INFO_LOC, basename(__METHOD__), __LINE__) . ERROR_ADMIN_NOT_ENABLED; static::internalLog($msg); } return; } if (static::$location != ENV_ADMIN) static::$location = ENV_ADMIN; static::$resMongoAdminMaster = null; static::$mongoAdminMasterAvailable = false; $service = ENV_ADMIN; break; case MONGO_TERCERO_MASTER : if (intval(gasConfig::$settings[CONFIG_BROKER_TERCERO][CONFIG_ACTIVE]) !== 1) { if (isset(static::$logger) and static::$logger->available) { $msg = sprintf(INFO_LOC, basename(__METHOD__), __LINE__) . ERROR_TERCERO_NOT_ENABLED; static::internalLog($msg); } return; } if (static::$location != ENV_TERCERO) static::$location = ENV_TERCERO; static::$resMongoTerceroMaster = null; static::$mongoTerceroMasterAvailable = false; $service = ENV_TERCERO; break; default : $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_SERVICE_404 . COLON . $_which; static::internalLog($msg); return; break; } // todo: SSL connectivity option // build the connect string to the mongo resource based on the requesting environment try { $mongoData = static::buildMongoDSN($service); if (is_null($mongoData) or !is_array($mongoData)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_DATA_METHOD_404, 'buildMongoDSN'); static::internalLog($msg); return; } $mongoDSN = $mongoData[STRING_DSN]; $authSource = (empty($mongoData[STRING_AUTH_SRC])) ? CONFIG_ADMIN : $mongoData[STRING_AUTH_SRC]; $mongoOptions = $mongoData[STRING_OPTIONS]; } catch (TypeError $t) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_TYPE_EXCEPTION; static::internalLog($msg); static::internalLog($t->getMessage()); return; } try { /* * connect to the named mongo resource - note that mongo manager will always return a resource - even if * there is no database to connect to - so, once we connect, issue a simple ping query to the named db to * validate the connection. */ $res = new MongoDB\Driver\Manager($mongoDSN, $mongoOptions); if (!is_object($res)) { consoleLog($res, CON_SYSTEM, ERROR_MONGO_CONNECT . COLON . $_which); return; } $command = new MongoDB\Driver\Command(['ping' => 1]); @$res->executeCommand($authSource, $command); // was "admin" for $authsource; ok to ping the target db // @$res->executeCommand('admin', $command); switch ($_which) { case MONGO_MASTER : static::$resMongoMaster = $res; static::$mongoMasterAvailable = true; break; case MONGO_ADMIN_MASTER : static::$resMongoAdminMaster = $res; static::$mongoAdminMasterAvailable = true; break; case MONGO_SEGUNDO_MASTER : static::$resMongoSegundoMaster = $res; static::$mongoSegundoMasterAvailable = true; break; case MONGO_WH_MASTER : static::$resMongoWHMaster = $res; static::$mongoWHMasterAvailable = true; break; case MONGO_TERCERO_MASTER : static::$resMongoTerceroMaster = $res; static::$mongoTerceroMasterAvailable = true; break; } } catch (MongoDB\Driver\Exception\ConnectionException | MongoDB\Driver\Exception\InvalidArgumentException | MongoDB\Driver\Exception\RuntimeException | MongoDB\Driver\Exception\Exception | Throwable $e) { // superclass for sslConnection and TimeoutException $hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__); @handleExceptionMessaging($hdr, $e->getMessage(), $errors, true); } } /** * buildMongoDSN() -- private static method * * This method requires one input parameter: the name of the current environment requesting a resource. This * is a well-defined (e.g.: constant) value and pre-validated by the invoking client. * * The method builds an associative array which is returned to the calling client and is divided into three * elements: * * 1. The mongo DSN (connect string) under the key: STRING_DSN * 2. The mongo Authentication Source DB under the key: STRING_AUTH_SRC * 3. The mongo options array (which is, itself, another associative array) * * Depending on which environment we're attempting to connect to, (defined in $_which), we'll pull that XML config * block out from the mongo-config super-block and use that configuration data to build the mongo data necessary * to connect to the named mongo resource. * * Any errors raised in processing will cause an error message to be generated and a null value returned to the * calling client. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_which * @return array|null * * * HISTORY: * ======== * 07-23-18 mks CORE-1097: original coding * */ private static function buildMongoDSN(string $_which): ?array { $validReadPrefs = [ 'primary', 'primaryPreferred', 'secondary', 'secondaryPreferred', 'nearest' ]; $skipRPConfig = false; $mongoDSN = MONGO_DSN; // return array $ra = [ STRING_DSN => null, STRING_AUTH_SRC => null, STRING_OPTIONS => null ]; // default read-preference $mongoOptions[CONFIG_DATABASE_MONGODB_RP] = MONGODB\Driver\ReadPreference::RP_PRIMARY; // note: user passwords, by default, are located in the target namaste db and not in the admin db // build the DSN based on the correct env (appServer, segundo, tercero or admin) configuration sub-block $thisConfig = static::$cfgMongo[$_which]; // step 1 -- ensure that the service ($_which) is local and active... // if (intval(gasConfig::$settings[$_which][CONFIG_IS_LOCAL]) !== 1) { // static::internalLog(sprintf(ERROR_SERVICE_NOT_LOCAL, $_which)); // return null; // } elseif (intval(gasConfig::$settings[$_which][CONFIG_ACTIVE]) !== 1) { // $msg = sprintf(ERROR_SERVICE_NOT_ACTIVE, $_which); // static::internalLog($msg); // return null; // } if (intval(gasConfig::$settings[$_which][CONFIG_ACTIVE]) !== 1) { $msg = sprintf(ERROR_SERVICE_NOT_ACTIVE, $_which); static::internalLog($msg); return null; } // the above commented-out block replaces the above code // step 1a -- ensure that mongo is enabled on the current service if (intval($thisConfig[CONFIG_DATABASE_MONGODB_ENABLED]) !== 1) { // and that mongo is enabled for the service $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_LOCAL_SERVICE_404; static::internalLog($msg); return null; } // Step 2 -- if RBAC enabled, grab the user credentials and inject into mongo-DSN if (intval($thisConfig[CONFIG_DATABASE_MONGODB_USE_AUTH]) === 1) { $mongoDSN .= $thisConfig[CONFIG_DATABASE_MONGODB_USER]. COLON_NS; $mongoDSN .= $thisConfig[CONFIG_DATABASE_MONGODB_PASSWORD] . AT; $authSource = gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH . $thisConfig[CONFIG_DATABASE_MONGODB_AUTH_SOURCE]; $mongoOptions[CONFIG_DATABASE_MONGODB_AUTH_SOURCE] = $authSource; $ra[STRING_AUTH_SRC] = $authSource; } // Step 3 -- evaluate the level of service in order of: sharded-cluster, replication-set, stand-alone-instance if (intval($thisConfig[CONFIG_DATABASE_MONGODB_SHARDING][CONFIG_DATABASE_MONGODB_ENABLED]) === 1) { // sharded cluster configuration foreach($thisConfig[CONFIG_DATABASE_MONGODB_SHARDING][CONFIG_DATABASE_MONGODB_SHARDING_MONGOS_NODES] as $node) { $mongoDSN .= $node . COMMA_NS; } $mongoDSN = rtrim($mongoDSN, COMMA_NS); } elseif (intval($thisConfig[CONFIG_DATABASE_MONGODB_REPLSET][CONFIG_DATABASE_MONGODB_REPLSET_ENABLED]) === 1) { // replication set configuration $mongoOptions[MONGO_REPL_SET] = $thisConfig[CONFIG_DATABASE_MONGODB_REPLSET][CONFIG_DATABASE_MONGODB_REPLSET_NAME]; foreach ($thisConfig[CONFIG_DATABASE_MONGODB_REPLSET][CONFIG_DATABASE_MONGODB_REPLSET_DSN][CONFIG_DATABASE_MONGODB_ADMIN_REPLSET_SET] as $node) { $mongoDSN .= $node . COMMA_NS; } $mongoDSN = rtrim($mongoDSN, COMMA_NS); } else { // stand-alone instance $mongoDSN .= $thisConfig[CONFIG_DATABASE_MONGODB_HOST] . COLON_NS . $thisConfig[CONFIG_DATABASE_MONGODB_PORT]; $skipRPConfig = true; } if (isset(static::$logger) and static::$logger->available and gasConfig::$settings[CONFIG_DEBUG]) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . 'MongoDSN (' . static::$location . '): ' . $mongoDSN; static::internalLog($msg); } $ra[STRING_DSN] = $mongoDSN; // Step 4: add options: heartbeat and read-preferences to the options array // heartbeat: $mongoOptions[CONFIG_DATABASE_MONGODB_HB] = intval($thisConfig[CONFIG_DATABASE_MONGODB_HB]); // readPreference: if the current env supports the read-secondary, then set the read-preference to the value // stored as secondaryReadPreference, otherwise use readPreference (primary) $mongoOptions[CONFIG_DATABASE_MONGODB_RP] = (isset($thisConfig[CONFIG_DATABASE_PDO_USE_SECONDARY]) === 1) ? $thisConfig[CONFIG_DATABASE_MONGODB_USE_READ_SECONDARY] : $thisConfig[CONFIG_DATABASE_MONGODB_RP]; // if not a single-node-instance of mongo, get the readPreference from XML config and override the default if (!$skipRPConfig) { // $slaveReadPreference = $thisConfig[CONFIG_DATABASE_MONGODB_SECONDARY_RP]; if (!in_array($thisConfig[CONFIG_DATABASE_MONGODB_RP], $validReadPrefs)) { $msg = sprintf(INFO_LOC, basename(__FILE__), __LINE__) . sprintf(ERROR_MDB_INVALID_RP, $thisConfig[CONFIG_DATABASE_MONGODB_RP]); static::internalLog($msg); return null; } } $ra[STRING_OPTIONS] = $mongoOptions; return $ra; } /** * internalLog() -- static private method * * This method requires a single input parameter - a string value which is the message to be logged. * * The method evaluates if the framework's logger resource is currently available and, if it is, publishes the * message to the log file -- otherwise, the message is published to the namaste console. * * The method returns void. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_msg * * * HISTORY: * ======== * 07-27-18 mks CORE-1108: initial coding * 08-22-18 mks DB-49: consoleLog is default log vector -- add to mongodb log iff available * 08-28-18 mks DB-49: squelching consoleLog output if client is a webApp, expanded exception trap * 10-11-20 mks DB-168: ensuring logging is not engaged until after broker clients have started * */ private static function internalLog(string $_msg): void { try { if (gasResourceManager::$adminAvailable and isset(static::$logger) and isset(static::$logger->available) and static::$logger->available === true) { static::$logger->info($_msg); } if (!(isset($_SERVER['HTTP_USER_AGENT']))) consoleLog(static::$myID, CON_SYSTEM, $_msg); } catch (TypeError | Throwable $t) { consoleLog(static::$myID, CON_ERROR, basename(__METHOD__) . AT . __LINE__ . COLON . $t->getMessage()); } } /** * singleton() -- public static class * * method to instantiate the resourceManager singleton class * * note: * ----- * it's the calling client's responsibility to check for the null return value in the member variable $instance. * * @author mike@givingassistant.org * @version 1.0 * * @return null * * HISTORY: * ======== * 06-12-17 mks original coding * */ public static function singleton() { if (static::$instance === null) { $c = __CLASS__; static::$instance = new $c(); } return(static::$instance); } /** * __destruct() -- public method * * explicitly close any open resources - note that this is the only place in the framework where a resource * connection is explicitly closed. * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 06-12-17 mks original coding * */ public function __destruct() { // As of PHP 5.3.10 destructors are not run on shutdown caused by fatal errors. // // destructor is registered shut-down function in constructor -- so any recovery // efforts should go in this method. } /** * __clone() -- public function * * Silently disallows cloning of the object * * @author mike@givingassistant.org * @version 1.0 * * @return null * * HISTORY: * ======== * 06-12-17 mks original coding * */ private function __clone() { return null; } }