$_req->get(BROKER_CORRELATION_ID))); try { /** @noinspection PhpUndefinedMethodInspection */ $_req->delivery_info[BROKER_CHANNEL]->basic_publish($msg, '', $_req->get(BROKER_REPLY_TO)); /** @noinspection PhpUndefinedMethodInspection */ $_req->delivery_info[BROKER_CHANNEL]->basic_ack($_req->delivery_info[BROKER_DELIVERY_TAG]); } catch (AMQPTimeoutException | AMQPRuntimeException | Throwable $e) { $logMsg = ERROR_BROKER_EXCEPTION . $e->getMessage(); $_log->fatal($logMsg); consoleLog($_res, CON_ERROR, $logMsg); } } /** * isAssoc() -- public function * * this simple function determines if an array is associative or indexed by returning a boolean -- the boolean * return indicates if ALL of the elements are numeric (false) or non-numeric (true). * * var_dump(isAssoc(array('a', 'b', 'c'))); // false * var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))); // false * var_dump(isAssoc(array("1" => 'a', "0" => 'b', "2" => 'c'))); // true * var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))); // true * * @author mike@givingassistant.org * @version 1.0,0 * * @param $arr * @return bool * * HISTORY: * ======== * 06-07-17 mks original coding * */ function isAssoc($arr) { return array_keys($arr) !== range(0, count($arr) - 1); } /** * validateGUID() -- global function * * this function takes a GUID string argument and returns a boolean value indicating whether or not the * supplied string is qualified as a GUID. * * --------------------------------------------------------------------- * regex translation * --------------------------------------------------------------------- * / beginning of expression * ^ beginning of string * (\{)? optional opening bracket { * [A-F\d]{8} 8 hex characters hhhhhhhh * (-[A-F\d]{4}) 4 hex characters proceeded by dash -hhhh * {4} previous pattern repeated 4 times * [A-F\d]{8} 8 hex characters hhhhhhhh * (?(1)\}) if first pattern was present {, then match closing tag } * $ end of string * / close expression * i ignore case sensitivity * * @author mike@givingassistant.org * @version 1.0 * * @param $_guid * @return bool * * HISTORY: * ======== * 06-07-17 mks original coding * 07-30-18 mks CORE-774: conversion to PHP 7.2 Exception (TypeError) format * 01-31-19 mks DB-107: added support for broker label text in validating guids (thanks Scott!) * 03-15-19 mks DB-116: added support for cacheMapped GUIDs where the Namaste environment is prepended * */ function validateGUID(string $_guid): bool { // if the guid has a cache tag, e.g.: {$ENV}_{GUID}, strip out the "{$ENV}_" part $envTag = '%' . gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV] . UDASH . '%'; $guid = preg_replace($envTag, '', $_guid); /** @noinspection RegExpRedundantEscape */ return(boolval(preg_match('/^([A-Z0-9]{4}_)?\{?[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{12}\}?$/', strtoupper($guid)))); } /** * isBinStr() -- public function * * simple function to look at an input string and detect non-standard ASCII characters. If non-standard ASCII * characters are found, assume the string to binary and return Boolean(true). Otherwise, if all the chars ($c) * are standard ASCII, return Boolean(false). * * Requires one input parameter - the string to test. * * This is the de-facto test for ensuring that a string is "binary" as opposed to plain text. * * * @author mike@givingassistant.org * @version 1.0 * * @param $_s -- input string * @return bool -- Boolean indicating if the input string contains non-ASCII (binary) characters * * * HISTORY: * ======== * 06-07-17 mks original coding * 04-17-19 mks DB-116: added $allowedChars array and added \n (chr(10)) to the array * */ function isBinStr(string $_s):bool { $isBinary = false; $allowedChars = [ chr(9), chr(10), chr(12), chr(13)]; if (is_string($_s)) { for ($x = 0; $x < strlen($_s); $x++) { $c = substr($_s, $x, 1); if ($c < chr(32) or $c > chr(127)) { if (!in_array($c, $allowedChars)) { $isBinary = true; } } } } return ($isBinary); } /** * convertBinaryArrayElements() -- general function * * this is a recursive function that will do a deep-examination of an array and will convert any binary * string stored as a value within the array to a base64-encoded string. * * the need for this function is to ensure that all elements of an array can be converted to a JSON string. If one * array element is a binary string, then the conversion will fail. * * the function requires a single, key-value-paired array as an input parameter and this is a call-by-reference * parameter. * * The method will return either Boolean value of true or false or a null value under the following conditions: * * false: error encountered during processing * true: processing completed and at least one array element was converted * null: processing completed without conversion * * * @author mike@givingassistant.org * @version 1.0 * * @param $_aryData * @param $_tval * @return bool * * * HISTORY: * ======== * 06-07-17 mks original coding * 01-30-20 mks DB-142: PHP7.4 compatibility, removed gacLogger instantiation because recursive * 11-05-20 mks DB-171: fixed processing loop by qualifying $value prior to entering processing conditionals * */ function convertBinaryArrayElements(&$_aryData, &$_tval = true):bool { if (!is_array($_aryData)) { consoleLog('fCBA: ', CON_ERROR, sprintf(INFO_LOC, basename(__FILE__), __LINE__) . ERROR_DATA_ARRAY_NOT_ARRAY . STRING_DATA); return(false); } foreach ($_aryData as $key => &$value) { if (false === $_tval) break; // if the current value is an array, recursively call self if (!empty($value)) { if (is_array($value)) { convertBinaryArrayElements($value, $_tval); } elseif (is_object($value) and !empty($value->bin)) { $_aryData[$key] = base64_encode($value->bin); $_tval = true; } elseif (isBinStr($value)) { $_tval = base64_encode($value); if (false === $_tval) return ($_tval); $_aryData[$key] = $_tval; $_tval = true; } } } return($_tval); } /** * cleanMysqlString() -- common function * * this function accepts a string as input and removes the %,-,'," characters from the string * making the string safe for a mysql insert. * * * @author mike@givingassistant.org * @version 1.0 * * @param $_string * @return string * * HISTORY: * -------- * 06-20-17 mks original coding * */ function cleanQueryString(string $_string):string { $cleansed = preg_replace("/[%\-'\"]/", '', $_string); return($cleansed); } /** * jsonHandler() -- generic function * * this function is a detailed json handler where it will always parse-out the last-error returned from the json request. * * the function takes three input parameters: * * $_jsonData -- the json data object (either a string or an array) * $_direction - specified to encode or decode * $_es -- a call-by-reference container for error messages * $_skipBC -- boolean value, default to false that, if true, skips the binary conversion call * * the purpose of this function is to evaluate the success or failure of a json operation and return a verbose error * statement as to why the operation (may have) failed. * * it operates off the json_last_error() method, returning an integer (error code) value which is parsed and translated * into clear-text string and pushed onto the error stack. If no errors were encountered, nothing gets pushed. * * it will also look at the return data and evaluate if the payload is null or false. if so, and it didn't report a * last_error(), then another error message will be generated as this is a bad thing. * * if anything fails during processing, a null value is returned to the calling client. Otherwise, the * json-ized payload is returned for encoding or the decoded array for decoding * * * @author mike@givingassistant.org * @version 1.0 * * @param $_jsonData * @param string $_direction * @param array $_es * @param bool $_skipBC * @return null|string|array * * * HISTORY: * ======== * 06-07-17 mks original coding * 04-15-19 mks DB-116: updated headers for strong type-casting a-la PHP7 as much as possible, added feature * allowing client to skip the binary element conversion * */ function jsonHandler($_jsonData, string $_direction, array &$_es, bool $_skipBC = false) { $err = ERROR_JSON_NONE; /** @noinspection PhpUnusedLocalVariableInspection */ $lastJsonError = ERROR_JSON_NONE; /** @var gacErrorLogger $logger */ $logger = new gacErrorLogger(); if ($_direction != JSON_DIR_DEC and $_direction != JSON_DIR_ENC) { $_es[] = ERROR_JSON_DIR; return null; } // decode if ($_direction == JSON_DIR_DEC and is_string($_jsonData)) { //remove padding // $_jsonData = preg_replace('/(.*)\}.*?$/', "$1}", $_jsonData); /** @noinspection RegExpRedundantEscape */ $_jsonData = preg_replace('/((.*)[\]\}]).*$/','$1', $_jsonData); $data = json_decode($_jsonData, true); $lastJsonError = json_last_error(); } elseif ($_direction == JSON_DIR_ENC and is_array($_jsonData) and $_skipBC) { if (!convertBinaryArrayElements($_jsonData)) { $logger->warn(ERROR_DATA_BIN_CONV_FAIL); return null; } $data = json_encode($_jsonData); $lastJsonError = json_last_error(); } elseif ($_direction == JSON_DIR_ENC) { $data = json_encode($_jsonData); $lastJsonError = json_last_error(); } else { $_es[] = ERROR_JSON_OP; $logger->info(ERROR_JSON_OP); return null; } switch ($lastJsonError) { case JSON_ERROR_NONE : // $_es[] = ERROR_JSON_NONE; // do nothing break; case JSON_ERROR_DEPTH : $err = ERROR_JSON_DEPTH; break; case JSON_ERROR_STATE_MISMATCH : $err = ERROR_JSON_STATE_MISMATCH; break; case JSON_ERROR_CTRL_CHAR : $err = ERROR_JSON_CTRL_CHAR; break; case JSON_ERROR_SYNTAX : $err = ERROR_JSON_SYNTAX; break; case JSON_ERROR_UTF8 : $err = ERROR_JSON_UTF8; break; case JSON_ERROR_RECURSION : $err = ERROR_JSON_RECURSE; break; case JSON_ERROR_INF_OR_NAN : $err = ERROR_JSON_INF_NAN; break; case JSON_ERROR_UNSUPPORTED_TYPE : $err = ERROR_JSON_TYPE; break; default : $err = ERROR_JSON_UNK; break; } if ($lastJsonError != JSON_ERROR_NONE) { $logger->warn($err); $_es[] = STUB_JSON_ERROR . $err; } if (is_null($data) or false === $data) { if (is_null($data)) $logger->info(ERROR_JSON_RET_NULL); if (false === $data) $logger->info(ERROR_JSON_RET_FALSE); if ($err == ERROR_JSON_NONE) { $logger->warn(ERROR_JSON_API); $_es[] = ERROR_JSON_API; } return null; } return($data); } /** * lorumIpsum() -- public function * * this function takes two input parameters, a sentence count, or a paragraph count -- if both parameters are zero * then we're going to return all of the text, stored in a support file called lorumIpsum.inc. * * if the paragraph or sentence count is not null, then we're going to get a count of the the number of elements * requested and return just that sub-set of the data. * * Since the text is just a huge string, we're going to split it based on either the period (.) or the newline break * (PHP_EOL) with the explode function, passed as an argument to implode predicated with the same constraint so * that we're just returning a string to the calling client. * * @author mike@givingassistant.org * @version 1.0 * * @param int $_sentence * @param int $_paragraph * @return string|null * * HISTORY: * ======== * 06-07-17 mks original coding * */ function lorumIpsum($_sentence = 0, $_paragraph = 0) { /** @var gacErrorLogger $logger */ $logger = new gacErrorLogger(); if ($_paragraph > 4) $_paragraph = 4; // there are 4 paragraphs in the lorum variable if ($_paragraph < 1) $_paragraph = 0; if ($_sentence > 65) $_sentence = 0; // there are 65 sentences in the lorum variable if ($_sentence < 1) $_sentence = 0; $text = null; $return = null; $file = __DIR__ . '/lorumIpsum.inc'; /** @noinspection PhpIncludeInspection */ include($file); if (is_null($text)) { $logger->warn(ERROR_FILE_404 . 'lorumIpsum.inc'); return null; } if ($_sentence == 0 and $_paragraph == 0) { return($text); } elseif ($_paragraph == 0) { $return = explode('.', $text, ($_sentence + 1)); unset($return[$_sentence]); $return = implode ('. ', $return); $return .= '.'; } else { $temp = preg_split("/\\r\\n|\\r|\\n/", $text); $count = 0; foreach ($temp as $key) { if (strlen($key) and $count < $_paragraph) { $return[] = $key; $count++; } } $return = implode(PHP_EOL, $return); } return($return); } /** * validateDate() -- general purpose method * * this method validates a date string that's in the format of 'Y-m-d H:i:s' by generating a DateTime object from * the input $_date using the provided $_format. * * The calling client can override the format, perhaps to check abbreviated date strings, by simply passing in a * substitute value for the default $_format. * * We compare the results of the generated date against the input data and return a boolean(true) if we're able * to re-generate the original date, otherwise boolean(false) is returned. * * Notes: * ------ * https://www.php.net/manual/en/function.checkdate.php#113205 * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_date -- the date string (ex: 2017-11-13 08:21:46) * @param string $_format -- the date format to validate against * @return bool * * * HISTORY: * ======== * 06-07-17 mks original coding * 11-13-17 mks CORE-588: revised for mysql dateTime * */ function validateDate(string $_date, string $_format = 'Y-m-d H:i:s'): bool { $d = DateTime::createFromFormat($_format, $_date); return $d && $d->format($_format) == $_date; } /** * validateCacheChecksum() -- general purpose method * * I needed this method for unit-testing - to be able to validate the checksum stored in cache objects against the * data stored in the cache container. * * This method has two input parameters: * * $_cacheRecord -- this is the cache record as retrieved from cache, and de-compressed/de-json'ized. * $_errors -- empty string container for storing any diagnostics raised during processing. This parameter * is a call-by-reference parameter. * * We validate the incoming data payload by ensuring the payload array has 2 elements, one labeled as "checksum" and * the other one a '0'. If these elements are not present, or if there are not 2 elements in the array, then we'll * populate the error container with an appropriate error message and return a boolean false to the calling client. * * Once base validation is completed, we extract, compress/json'ize, and calculate the checksum of the array and * compare it against the stored checksum in the incoming payload. If it matches, then we return a true value to * the calling client, otherwise we generate another error message and return a boolean false. * * * @author mike@givingassistant.org * @version 1.0 * * @param array $_cacheRecord * @param string $_errors * @return bool * * * HISTORY: * ======== * 04-25-19 mks DB-116: original coding * */ function validateCacheChecksum(array $_cacheRecord, string &$_errors = ''): bool { $mxrc = intval(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_QUERY_RECORD_LIMIT]); if (count($_cacheRecord) < 2 or count($_cacheRecord) > $mxrc) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = sprintf(ERROR_DATA_ARRAY_COUNT_RANGE, 2, $mxrc); $_errors = $hdr . $msg; return false; } elseif (!array_key_exists(STRING_CHECKSUM, $_cacheRecord)) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = ERROR_ARRAY_KEY_404 . STRING_CHECKSUM; $_errors = $hdr . $msg; return false; } elseif (!array_key_exists(0, $_cacheRecord)) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = ERROR_ARRAY_KEY_404 . STRING_DATA; $_errors = $hdr . $msg; return false; } $aryTmp = $_cacheRecord; unset($aryTmp[STRING_CHECKSUM]); $cs = md5(gzcompress(json_encode($aryTmp))); if ($cs != $_cacheRecord[STRING_CHECKSUM]) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = ERROR_CACHE_DATA_CHECKSUM . STRING_DATA; $_errors = $hdr . $msg; return false; } return true; } /** * prettyTime() -- general purpose method * * this function converts an epoch time string value to a series of date-relative times in readable form. * * for example, if I pass in a epoch time value, the output should look something like: * * 6 days, 15 hours, 48 minutes, 19 seconds * * if a value is zero (years, weeks, etc., then the output will be squelched for that value. * * * @author mike@givingassistant.org * @version 1.0 * @noinspection PhpUnused * * @param integer $_t - epoch time in seconds * @return string - returns time in readable format * * HISTORY: * ======== * 06-07-17 mks original coding * */ function prettyTime($_t) { $retData = null; $bit = array( ' years' => $_t / 31556926 % 12, ' weeks' => $_t / 604800 % 52, ' days' => $_t / 86400 % 7, ' hours' => $_t / 3600 % 24, ' minutes' => $_t / 60 % 60, ' seconds' => $_t % 60 ); foreach($bit as $k => $v) if ($v) $retData[] = $v . $k; return rtrim(join(', ', $retData), ', '); } /** * unPrettyTime() -- general function * * This function takes/requires a single input parameter which should be a date/time string in the format of * "YYYY-DD-MM H:i:s" and converts the string value to unix epoch time. * * The return value, given that a good date is submitted to the method, will cause the corresponding and valid epoch * time value to be returned to the calling client. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_t * @return int * * * HISTORY: * ======== * 04-25-18 mks _INF-188: original coding * */ function unPrettyTime(string $_t): int { return DateTime::createFromFormat('Y-m-d H:i:s', $_t)->getTimestamp(); } /** * buildReturnPayload() -- general purpose method * * this method assembles the return payload from a broker event that's to be passed back to the calling client. * * the method requires a single input array: an associative array containing the following: * * $state -- the state constant -- required! * $status -- the status constant -- required! * $diagnostics -- the error stack * $results -- the return data payload -- optional (the request results data) * * The method will check the optional parameters and, if empty, not return these subscripts. The exception is the * results parameter - if not supplied, it should be null and a null will be returned to the client. * * @author mike@givingassistant.org * @version 1.0,0 * * @param array $_data -- associative array containing all of the return payload data * @return array * * * HISTORY: * ======== * 06-07-17 mks original coding * 02-27-19 mks DB-116: updated for cacheMapping transmogrification on outbound data payloads * */ function buildReturnPayload(array $_data): array { // extract the data payload list($status, $state, $diagnostics, $results) = $_data; $status = ($status == STATE_SUCCESS) ? true : false; $rd = [ PAYLOAD_STATUS => $status, PAYLOAD_STATE => $state, PAYLOAD_DIAGNOSTICS => $diagnostics ]; // call cacheMapper for outbound payload $rd[ PAYLOAD_RESULTS ] = (empty($results) ? null : $results); return($rd); } /** * firstPassPayloadValidation() -- general purpose method * * this method is called by all brokers and it's purpose is to provide general validation (e.g. required sub-elements * of the payload exist) of the broker event incoming data payload. * * method takes several input parameters, defined as follows: * * $_req the raw request data as received by the broker * $_msg CALL-BY-REF string that contains and diagnostic messages * $_aryReq CALL-BY-REF array that will contain the decoded and uncompressed payload data * $_eg REQUIRED: the event GUID as generated by the invoking broker client * $_skipMeta Boolean value indicating we should bypass the check for meta data * * Initially, the incoming Call-By-Reference parameters ($_msg and $_aryReq) are sent as blank or null values and * are populated as a result of processing the payload. * * The method returns a BOOL value indicating if the payload data was successfully validated or not. * * @author mike@givingassistant.org * @version 1.0 * * @param AMQPMessage $_req * @param string $_svc * @param string $_msg * @param array $_aryReq * @param string $_eg * @param bool $_skipMeta * @return bool * * HISTORY: * ======== * 06-07-17 mks original coding * 02-08-18 mks _INF-139: fixed error where not including BROKER_REQUEST in a request was crashing the broker * 10-30-18 mks DB-68: bug fix (warning/notice) for array-check on array_key_exists function * 01-02-19 mks DB-78: tracking pre-populated eventGUIDs * 01-28-19 mks DB-107: re-wrote how the eventGUID is propagated/maintained from the event payload * 07-28-20 mks DB-156: injecting broker service, PHP7 type decls for params * 09-11-20 mks DB-168: checks for "data" and "meta" in the data payload so the brokers no longer have to, * and added the broker service decl to the meta payload * */ function firstPassPayloadValidation(AMQPMessage $_req, string $_svc, ?string &$_msg, ?array &$_aryReq, string $_eg, bool $_skipMeta = false):bool { $res = 'FPPV:'; $goodRequest = false; $_msg = ERROR_DATA_VALIDATION_FIRST_PASS; $file = basename(__FILE__); $tmp = new gacMeta(); if (empty($_req)) { $_msg = ERROR_DATA_INPUT_EMPTY; } else { $request = $_req->body; if (empty($request)) { $_msg = ERROR_DATA_INPUT_EMPTY . ' - request body'; } else { try { $badData = false; $_aryReq = json_decode(gzuncompress($request), true); if (is_array($_aryReq) and !array_key_exists(BROKER_REQUEST, $_aryReq)) { $hdr = sprintf(INFO_LOC, $file, __LINE__); $_msg = $hdr . ERROR_BROKER_REQUEST_BAD . BROKER_REQUEST; $badData = true; } elseif (!is_array($_aryReq)) { $hdr = sprintf(INFO_LOC, $file, __LINE__); $_msg = $hdr . ERROR_DATA_ARRAY_NOT_ARRAY . STRING_BROKER_PAYLOAD; $badData = true; } elseif (!array_key_exists(BROKER_DATA, $_aryReq)) { $hdr = sprintf(INFO_LOC, $file, __LINE__); $_msg = $hdr . ERROR_DATA_KEY_404 . BROKER_DATA; $badData = true; } elseif (!array_key_exists(BROKER_META_DATA, $_aryReq)) { $hdr = sprintf(INFO_LOC, $file, __LINE__); $_msg = $hdr . ERROR_DATA_KEY_404 . BROKER_META_DATA; $badData = true; } } catch (Throwable | TypeError $t) { $hdr = sprintf(INFO_LOC, $file, __LINE__); @handleExceptionMessaging($hdr, $t->getMessage(), $foo, true); if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return false; } // if an event guid was passed in the meta payload, do not replace it if (array_key_exists(META_EVENT_GUID, $_aryReq[BROKER_META_DATA])) { $incGUID = $_aryReq[BROKER_META_DATA][META_EVENT_GUID]; if (gasConfig::$settings[CONFIG_DEBUG]) consoleLog($res, CON_SYSTEM, 'Incoming GUID was accepted'); } else { $incGUID = $_eg; if (gasConfig::$settings[CONFIG_DEBUG]) consoleLog($res, CON_SYSTEM, sprintf(INFO_EVENT_GUID_REPLACED, $_eg, basename(__FILE__), __LINE__)); } if (!validateGUID($incGUID)) { $_msg = ERROR_EVENT_GUID_404 . STRING_META_PAYLOAD; consoleLog($res, CON_SYSTEM, $_msg); consoleLog($res, CON_SYSTEM, ERROR_INVALID_GUID . $incGUID); $badData = true; } if ($badData) { if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return false; } $_aryReq[BROKER_META_DATA][META_EVENT_GUID] = $incGUID; $_aryReq[BROKER_META_DATA][META_BROKER_SERVICE] = $_svc; $_aryReq[STRING_SERVICE] = $_svc; if (!in_array($_aryReq[BROKER_REQUEST], $tmp->skipChecksForMeta)) { if (!is_array($_aryReq)) { $_msg = ERROR_DATA_UNPACK; } elseif (!$_skipMeta and empty($_aryReq[BROKER_META_DATA])) { $_msg = ERROR_DATA_META_404; } else { $_msg = null; $goodRequest = true; } } else { $goodRequest = true; $msg = null; } } } if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return($goodRequest); } /** * postSystemEvent() -- generic function * * This function requires the following input parameters: * * $_data - this is an associative array of event data that will be published to the Admin-In broker * $_groot - this is the root GUID for the broker * $_logger - this is the currently-active logger resource * * This function declares a new SystemEvents object and, on successful instantiation, checks to see if we're local to * the admin service and, if we are, then we write the data directly to the database otherwise, we publish a request * to save the data to the admin service. * * (NOTE THAT WE'RE ENCAPSULATING THE DATA-BALL AS AN ARRAY OF ARRAYS IN THE SEND REQUEST!) * * The function returns a boolean value indicating success or failure (in sending the request to the admin-in * broker) but does not indicate if the request was successfully processed on the remote service since the admin-in * broker is a fire-n-forget type broker. * * * @author mike@givingassistant.org * @version 1.0 * * @param array $_data * @param null | string $_groot * @param gacErrorLogger $_logger * @return bool * * HISTORY: * ======== * 08-22-17 mks CORE-500: original coding * 08-13-20 mks DB-168: added exception handling and refactored, groot can now be null * */ function postSystemEvent(array $_data, ?string $_groot, gacErrorLogger $_logger): bool { $rc = false; if (isset($_data[BROKER_META_DATA][META_EVENT_GUID])) { $meta = $_data[BROKER_META_DATA]; } else { $stack = debug_backtrace(); $stackPointer = (count($stack) > 1) ? 1 : 0; $meta = [ META_TEMPLATE => TEMPLATE_CLASS_SYS_EVENTS, META_EVENT_GUID => $_groot ?? STRING_NULL, META_SYSTEM_NOTES => $stack[$stackPointer][STRING_FILE] . AT . $stack[$stackPointer][STRING_LINE], META_SESSION_IP => STRING_SESSION_HOME, META_CLIENT => CLIENT_SYSTEM, META_TLTI => STRING_CLASS_GAT ]; } // When the admin service is local, write the system event record directly to the database using the data class. // You may be tempted to route all system event postings through the broker but, if you do this, then you'll see // an infinite loop as the admin broker posts a system-event record for processing your system event request // which is a new system event request...SO DON'T DO IT! try { if (gasConfig::$settings[CONFIG_ADMIN][CONFIG_IS_LOCAL] === 1) { $tmpObj = new gacSystemEvents($meta); if ($tmpObj->status) { $tmpObj->_createRecord([$_data]); if (!$tmpObj->status) { $hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__); @handleExceptionMessaging($hdr, ERROR_MDB_SYS_EVENT_SAVE, $foo, true); } else { $rc = true; } } else { $_logger->error(ERROR_FAILED_TO_INSTANTIATE . TEMPLATE_CLASS_SYS_EVENTS); $_logger->error(var_export($tmpObj->eventMessages, true)); } if (is_object($tmpObj)) $tmpObj->__destruct(); unset($tmpObj); } else { if (!gasStatic::publishSystemEvent([$_data], $meta, $_groot)) $_logger->error(ERROR_MDB_SYS_EVENT_SAVE); else $rc = true; } } catch (Throwable | TypeError $t) { $hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__); @handleExceptionMessaging($hdr, $t->getMessage(), $foo, true); } return($rc); } /** * grabWidget() -- general function * * I got tired of writing code that grabs a new factory, tests the factory, then grabs and tests the widget. This * function automates that and eliminates the related code redundancy. * * The function has the following input parameters: * * $_meta -- the meta data payload standard for creating a factory class object * $_guid -- the guid if we're going to fetch on instantiation * $_errors -- call-by-reference parameter to contain the error stack * * The function attempts to instantiate a factory class object using the passed $_meta data array -- we test for * instantiation success after and if we failed, generate messaging and return a null to the calling client. * * If the instantiation of the factory class was successful, then grab the widget and test for errors in the widget * again returning a null if the creation of the widget within the factory failed. * * At every step, update the $_errors call-by-reference parameter. * * Finally, if success was found in both objects, return the widget object back to the calling client. * * Clean-up all the things. * * * @author mike@givingassistant.org * @version 1.0 * * @param array $_meta * @param string $_guid * @param array $_errors * @return object|null * * * HISTORY: * ======== * 09-30-20 mks DB-168: Original coding * */ function grabWidget(array $_meta, string $_guid = '', array &$_errors = []):?object { $obj = null; $widget = null; try { $obj = new gacFactory($_meta, FACTORY_EVENT_NEW_CLASS, $_guid, $_errors); if (!checkFactory($obj, __LINE__, basename(__METHOD__), $msg)) { // failed to instantiate factory $widget = null; } else { /** @var gacMongoDB | gacPDO $widget */ $widget = $obj->widget; } if (is_object($obj)) $obj->__destruct(); unset($obj); return $widget; } catch (Throwable | TypeError $t) { $hdr = sprintf(INFO_LOC, basename(__FILE__), __LINE__); @handleExceptionMessaging($hdr, $t->getMessage(), $_errors, true); if (is_object($widget)) $widget->__destruct(); if (is_object($obj)) $obj->__destruct(); unset($widget, $obj); return null; } } /** * checkFactory() -- public function * * This function requires the following input parameters: * * $_factory - a copy of the gacFactory class for which we're checking for successful instantiation * $_line - the line number that called this method * $_file - the file name that called this method * $_msg - call-by-reference parameter for the return error message * * This method validates basic instantiation of a new factory class object by ensuring that the status of the * factory object, and the status of the factory's widget (data) class are both true indicating that the factory * was successfully instantiated and, as such, we'll return a boolean(true) to the calling client. * * Otherwise, if either of the status values are not true, then we'll post error messages and return an error * message to the calling client via the $_msg parameter, and explicitly return a boolean(false). * * * @author mike@givingassistant.org * @version 1.0 * * @param gacFactory $_factory * @param int $_line * @param string $_file * @param string $_msg * @return bool * * HISTORY: * ======== * 00-29-20 mks Original coding * */ function checkFactory(gacFactory $_factory, int $_line, string $_file, ?string &$_msg):bool { $hdr = sprintf(INFO_LOC, $_file, $_line); if (!$_factory->status) { // could not instantiate the failed-session data class: error and exit $_msg = ERROR_FAILED_TO_INSTANTIATE . TEMPLATE_CLASS_FAILED_SESSIONS; @handleExceptionMessaging($hdr, $_msg, $_factory->eventMessages, true); foreach ($_factory->eventMessages as $eventMessage) { consoleLog(FUNC_RES, CON_ERROR, $eventMessage); } return false; } if (!$_factory->widget->status) { // we created the factory but creating the widget failed: error and exit $_msg = ERROR_WH_MISSING_WIDGET . TEMPLATE_CLASS_FAILED_SESSIONS; @handleExceptionMessaging($hdr, $_msg, $foo, true); return false; } return true; } /** * handleExceptionMessaging() -- public function * * This function has the following input parameters: * * $_hdr - string that contains the error message location * $_msg - string containing the error message to be stored * $_errStack - call-by-reference parameter that should be the class eventMessages() error container * $_cl - boolean, if true, then print a console log message * * The method returns a void - and if there are processing errors, it will silently fail without posting a message. * * Otherwise, we store the message in the database as a WARN event, we push the latest hdr+err onto the error stack * and, if the $_cl is true, then we'll also output the header and message to the console log. * * Programmer's Notes: * ------------------- * When calling this method, sometimes I pass a dummy variable for param 3 (the error stack) named $foo. What this * means is that I don't want the error message to be returned to the client via the eventMessages class member, usually * because the message contains framework or schema-revealing details. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_hdr -- location string where the error was generated * @param string $_msg -- the message to be logged * @param array|null $_errStack -- implicitly pass-back the error via the error stack reference * @param bool $_cl -- copy mesg to console log * * HISTORY: * ======== * 08-18-20 mks DB-168: original coding * */ function handleExceptionMessaging(string $_hdr, string $_msg, ?array &$_errStack = null, bool $_cl = false):void { $logger = new gacErrorLogger(); if (empty($_msg)) return; if (empty($_hdr)) return; $logger->warn($_hdr . $_msg); $_errStack[] = $_msg; if ($_cl) consoleLog(FUNC_RES, CON_SYSTEM, $_hdr . $_msg); if (is_object($logger)) $logger->__destruct(); unset($logger); } /** * validateMetaData() -- general function * * this function is called by all brokers for advanced validation of the meta-data payload. * * for the most part, we examine required meta data components then, based on the client type, add additional checks. * * the method requires three input parameters: * * $_meta - the meta data payload as a call by reference parameter because cacheMapping changes BROKER_DATA * $_request - the name of the broker event * $_msg - a call-by-reference parameter that contains error messages * * the method ultimately returns (explicitly) a Boolean value that indicates if the meta-data payload was successfully * validated or not. * * Notes: * ------ * Do not pass in a pre-populated error message stack ($_msg) as this variable is reset on entry. * * * @author mike@givingassistant.org * @version 1.0 * * @param array $_payload * @param array $_msg * @return bool * * * HISTORY: * ======== * 06-07-17 mks original coding completed * 02-08-18 mks _INF-139: de-janked return statements, PHP7 param types * 08-30-18 mks DB-50: sneakerstrap coding - ensuring meta has populated the config member * 12-06-18 mks DB-55: explicitly de-allocating $tmp class, replaced validateMetaPayload() call that was * handled by gasStatic to invoke the meta class method instead * 02-14-19 mks DB-116: refactored for cacheMapping being stored in memcache and handled at the broker level, * simplified the incoming parameters, added cacheMap processing calls. * Also improved the meta-payload requirements by client-type based on config settings. * 10-10-19 mks DB-136: Added exemptEvents check for requests that don't have cacheMapped data in the payload * 06-10-20 mks ECI-108: added check for evaluating return from mapIncomingData() - if this completely fails, * e.g.: user submitted wrong data payload for the declared class, then detect this and * return the appropriate messaging. * 06-15-20 mks ECI-164: call to gasStatic::getTLTI() b/c this method is called before gacFactory::__construct() * 11-06-20 mks DB-171: the PDO code in appServer for an update event calls this method directly and embeds the * broker service in the meta data payload - added code to check-for & extract before * returning an error message * 01-05-21 mks DB-180: better exception handling * */ function validateMetaData(array &$_payload, array &$_msg): bool { $meta = $_payload[BROKER_META_DATA]; $data = $_payload[BROKER_DATA]; $request = (isset($_payload[BROKER_REQUEST])) ? $_payload[BROKER_REQUEST] : ''; $exemptEvents = [ BROKER_REQUEST_CALL_SP ]; $tlti = STRING_CLASS_GAT; $method = basename(__METHOD__); $debug = false; if ($debug) { echo '============================== D E B U G ========================================' . PHP_EOL; echo 'Request: ' . $request . PHP_EOL; var_export($_payload); echo PHP_EOL . PHP_EOL; echo '================================================================================+' . PHP_EOL; } if (!isset($_payload[STRING_SERVICE])) { if (isset($_payload[BROKER_META_DATA][META_BROKER_SERVICE])) { $_payload[STRING_SERVICE] = $_payload[BROKER_META_DATA][META_BROKER_SERVICE]; } else { if ($debug) { echo debug_backtrace()[1]['function'] . AT . debug_backtrace()[1]['line'] . PHP_EOL; var_export($_payload); echo PHP_EOL; } $hdr = sprintf(INFO_LOC, $method, __LINE__); $_msg[] = ERROR_SERVICE_UNK; consoleLog(FUNC_RES, CON_ERROR, $hdr . ERROR_SERVICE_UNK); return false; } } $service = $_payload[STRING_SERVICE]; $tmp = new gacMeta(true); if (empty($tmp->config) and !empty(gasConfig::$settings)) { $tmp->config = gasConfig::$settings[CONFIG_META]; } if (empty($tmp->config)) { $_msg[] = ERROR_DATA_META_KEY_404 . CONFIG_META; if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return false; } // don't bother validating if the event is one of the skipCheck events... if (in_array($request, $tmp->skipChecksForMeta)) { if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return true; } if (!isset($meta[META_EVENT_GUID])) $meta[META_EVENT_GUID] = guid(); // if the broker request is an exempt event, return true if (isset($_payload[BROKER_REQUEST]) and in_array($_payload[BROKER_REQUEST], $exemptEvents)) return true; /* * cacheMapping Process: * --------------------- * First thing we check, to expedite processing, is to ensure that we're currently executing on AppServer (Namaste). * Next we check is if there's a META_TEMPLATE defined in the meta data: if there is, then we're going to fetch * the template's cacheMap (which must also exist) from cache. * */ if ($service == CONFIG_BROKER_APPSERVER) { if (array_key_exists(META_TEMPLATE, $meta)) { try { // check for TLTI already present in meta payload: if (isset($meta[META_TLTI]) and !empty($meta[META_TLTI])) { $tlti = $meta[META_TLTI]; } elseif (isset($meta[CLIENT_AUTH_TOKEN]) and $meta[META_CLIENT] == CLIENT_API_USER) { if (is_null($tlti = gasStatic::getTLTI($meta[CLIENT_AUTH_TOKEN], $meta[META_EVENT_GUID], $response))) { $tlti = STRING_CLASS_GAT; } } if (!is_null($vector = gasCache::getCacheMap($meta[META_TEMPLATE], $tlti)) and $vector[CACHE_MAP] != STRING_NOT_DEFINED and isset($vector[STRING_SERVICE])) { $_payload[BROKER_META_DATA][META_TLTI] = $tlti; // we have a cacheMap -- we are re-routing iff this is a tercero (only) instantiation if ($vector[STRING_SERVICE] == EXCHANGE_SERVICE_TERCERO) { // this is a tercero request ==> // ... first, see if the Partner can instantiate the GA class or if the class is closed if ($vector[CACHE_CLOSED]) { $hdr = sprintf(INFO_LOC, $method, __LINE__); $msg = sprintf(ERROR_TEMPLATE_FILE_NOT_AUTH, $meta[META_TEMPLATE]); $_msg[] = $msg; $tmp->logger->error($hdr . $msg); return false; } } // process the incoming data payloads and pass through the return bool $res = (gasCache::mapIncomingData($data, $vector, $_msg)); if ($res) { if (is_array($data) and array_key_exists(0, $data) and is_null($data[0])) { // cachemap failed to map any data $hdr = sprintf(INFO_LOC, $method, __LINE__); $msg = ERROR_CACHE_MAP_FAIL . STRING_DATA; $_msg[] = $msg; $msg = ERROR_CACHE_MAP_FAIL . $tlti . $meta[META_TEMPLATE]; $tmp->logger->data($hdr . $msg); return false; } $_payload[BROKER_DATA] = $data; } else { $hdr = sprintf(INFO_LOC, $method, __LINE__); $msg = ERROR_CACHE_MAP_FAIL . STRING_PAYLOAD_DATA; $tmp->logger->error($hdr . $msg); } } elseif (is_null($vector)) { $hdr = sprintf(INFO_LOC, $method, __LINE__); $msg = ERROR_CACHE_MAP_LOAD . $tlti . $meta[META_TEMPLATE]; $_msg[] = $msg; $tmp->logger->error($hdr . $msg); consoleLog(FUNC_RES, CON_ERROR, $msg); } } catch (TypeError | Throwable $t) { $hdr = sprintf(INFO_LOC, $method, __LINE__); @handleExceptionMessaging($hdr, $t->getMessage(), $foo, true); return false; } } } // validate the meta data format if (empty($meta)) { $_msg[] = ERROR_DATA_META_REQUIRED; if ($tmp->debug) $tmp->logger->debug(ERROR_DATA_META_REQUIRED); if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return false; } if (!is_array($meta)) { $_msg[] = ERROR_META_INVALID_FORMAT_ARRAY; if ($tmp->debug) $tmp->logger->debug(ERROR_META_INVALID_FORMAT_ARRAY); if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return false; } // return true if this is a system daemon request while ensuring that META_CLIENT exists in the meta payload if (isset($meta[META_CLIENT]) and $meta[META_CLIENT] == CLIENT_SYSTEM) { if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return true; } elseif (!isset($meta[META_CLIENT])) { $hdr = basename(__FILE__) . AT . __LINE__ . COLON; $msg = ERROR_META_CLIENT_404; $_msg[] = $msg; $tmp->logger->error($hdr . $msg); return false; } $clientConfig = gasConfig::$settings[CONFIG_META]; // check the required meta-data fields for each client type $clients = $tmp->validClients; // clients should be in the array if (!in_array($meta[META_CLIENT], $clients)) { $hdr = basename(__FILE__) . AT . __LINE__ . COLON; $msg = sprintf(ERROR_META_CLIENT_UNK, $meta[META_CLIENT]); $_msg[] = $msg; $tmp->logger->error($hdr . $msg); return false; } // client is valid but namaste doesn't know about it in the namaste.xml configuration // (really - this should never execute...if it does, check gacMeta->validClients list // or ensure that the client is defined in the xml file if (!array_key_exists($meta[META_CLIENT], $clientConfig)) { $hdr = basename(__FILE__) . AT . __LINE__ . COLON; $msg = sprintf(ERROR_META_CLIENT_UNK, $meta[META_CLIENT]); $_msg[] = $msg; $tmp->logger->fatal($hdr . $msg); return false; } // for the client type - enforce the meta-data requirement as defined in the namaste xml config $errorsInMeta = false; foreach ($clientConfig[$meta[META_CLIENT]] as $metaTag => $setting) { if ($setting and !array_key_exists($metaTag, $meta)) { $hdr = basename(__FILE__) . AT . __LINE__ . COLON; $msg = sprintf(ERROR_REQ_META_KEY_404, $request, $meta[META_CLIENT], $metaTag); $tmp->logger->data($hdr . $msg); // if we're an API request, white-box the response if ($meta[META_CLIENT] == CLIENT_API_USER) $msg = ERROR_REQ_META_KEY_404_WB; $_msg[] = $msg; $errorsInMeta = true; } } if ($errorsInMeta) { if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return false; } // validate meta-data field value types if (!$tmp->validateMetaPayload($meta)) { $_msg = $tmp->eventMessages; if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return false ; } if (is_object($tmp)) $tmp->__destruct(); unset($tmp); return true; } /** * validateService() -- general function * * This function allows the broker to self-declare which service they are. This is important for processing event data * on non-appServer services. For example, when Tercero gets a request, we don't need to perform the SMAX API check * to see if this is a tercero request (in validateMetaData()) because this has been resolved at the appServer level. * * This method gets called by each broker, before any meta payload validation is conducted. The meta validation * functions, of which there are two, then have responsibility for check the current broker's registered service * prior to performing any checks specific to that service. * * The function has one input parameter: * * $_service -- this is the name of the service submitted by the broker * * The function returns a boolean value which indicates if the service name, submitted by the broker, is valid or not. * * * @author mike@givingassistant.org * @version 1.0 * * @param string $_service * @param null|array $_errors * @return bool * * * HISTORY: * ======== * 07-28-20 mks ECI-156: original coding * 09-17-20 mks DB-168: added $_errors, refactored for new XML configuration, changed method name * */ function validateService(string $_service, ?array &$_errors):bool { $res = 'fnVS: '; $function = basename(__FUNCTION__); try { if (!is_array(gasConfig::$settings[$_service]) or empty(gasConfig::$settings[$_service])) { $hdr = sprintf(INFO_LOC, $function, __LINE__); $msg = ERROR_SERVICE_UNK . COLON . ENV_ALL; consoleLog($res, CON_ERROR, $hdr . $msg); $_errors[] = $msg; return false; } if (empty($_service)) { $hdr = sprintf(INFO_LOC, $function, __LINE__); $msg = ERROR_SERVICE_UNK; $_errors[] = $msg; consoleLog($res, CON_ERROR, $hdr . $msg); return false; } } catch (Throwable | TypeError $t) { $hdr = sprintf(INFO_LOC, $function, __LINE__); @handleExceptionMessaging($hdr, $t->getMessage(), $foo, true); $_errors[] = ERROR_EXCEPTION; return false; } return true; } /** * scrub() -- public function * * this function is a near-mirror of the private class method used in the data core. it's provided as a generic * function for use, mainly, for the admin-server when it fetches data from the vault. the primary purpose, then, * is to strip collection extensions from the associative keys. * * the function has the following parameters: * $_data -- the payload (record) data * $_ext -- the extension to be stripped * $_history - a boolean indicating if the history collection should be returned in the scrubbed set * * NOTES: * ------ * this is a recursive function that will drill down and clean all array containers. * this param does not support more than one record in the data array like the root method does * * * @author mike@givingassistant.org * @version 1.0 * @noinspection PhpUnused * * @param $_data * @param $_ext * @param bool $_history * * * HISTORY: * ======== * 06-07-17 mks original coding * * */ function scrub(&$_data, $_ext, $_history = false) { $hiddenColumns = [DB_PKEY]; foreach ($_data as $key => $value) { $newKey = str_replace($_ext, '', $key); if (is_array($value)) { scrub($value, $_ext, $_history); } if ($newKey != $key and !in_array($newKey, $hiddenColumns)) { $_data[$newKey] = $value; unset($_data[$key]); } elseif (in_array($newKey, $hiddenColumns)) { unset($_data[$key]); } } if (!$_history and array_key_exists(DB_HISTORY, $_data)) { unset($_data[DB_HISTORY]); } } /** * checkEmailAndDomain() -- generic function * * this function takes either a fully-qualified email name, or a domain name, and validates the following: * * -- that if a fully-qualified email was submitted that the email is valid * -- if a domain is submitted (@something.com or something.com) that the domain returns a valid MX record * * Also, run the domain through an I18N parser to handle non-printable characters that may be in an email address. * * @author mike@shallop.com * @version 1.0 * @noinspection PhpUnused * * @param $_email * @return bool * * HISTORY: * ======== * 06-07-17 mks original coding * */ function checkEmailAndDomain($_email) { $exp = "/^(.*)@(.*)$/"; preg_match($exp, $_email, $matches); // $_email = 'mike@shallop.com' // $matches[0] = 'mike@shallop.com' // $matches[1] = 'mike' // $matches[2] = 'shallop.com' if (!empty($matches[1]) and (!filter_var($matches[0], FILTER_VALIDATE_EMAIL))) return (false); if (empty($matches[2])) // no domain submitted return (false); return (@checkdnsrr(idn_to_ascii($matches[2]), 'MX')); // validates the domain's MX record } /** * getEmailDomain() -- generic function * * this function takes the code from the above "checkEmailAndDomain()" method and executes the same expression * parse-and-match command to extract the domain from an email address. * * the input parameter is a fully-formed email address and the return value is a string if we were able to successfully * extract the fomain from the email, otherwise return a Boolean(false). * * * @author mike@givingassistant.org * @version 1.1.0 * @noinspection PhpUnused * * @param $_email * @return bool * * HISTORY: * ======== * 06-07-17 mks ome-650: original coding * */ function getEmailDomain($_email) { $exp = "/^(.*)@(.*)$/"; preg_match($exp, $_email, $matches); return(empty($matches[2])) ? false : $matches[2]; } /** * shutDownBroker() -- generic function * * this function performs a shutdown (and not much else) of a broker indicated by the passed parameters $ch and $con * to the function. * * $ch is the broker channel resource and $con is the broker queue connection. * $br is the 4-char text name of the broker being shutdown. * * the function merely calls the resource close() methods and then outputs a console message. * * there is no error-checking of the inputs and no error checking of the close() return state. * * @author mike@givingassistant.org * @version 1.0 * @noinspection PhpUnused * * @param $ch - broker channel resource pointer * @param $con - broker connection resource pointer * @param $br - 4-char console text string indicating which broker is being stopped * * HISTORY: * ======== * 06-07-17 mks original coding * 07-10-18 mks CORE-773: replaced echo statement with consoleLog() * */ function shutDownBroker(AMQPChannel $ch, AMQPStreamConnection $con, $br) { try { $ch->close(); $con->close(); consoleLog($br, CON_SUCCESS, SUCCESS_SHUTDOWN); } catch (Throwable $t) { $hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__); consoleLog(FUNC_RES, CON_SYSTEM, $hdr . $t->getMessage()); } } /** * getDateTime() * * returns the current date and time in a string in a format used for console messages. * * there is one, optional, input parameter to the method - that's a time stamp in time() format. As of this * writing, this is only used from the log dumper utility to generate a nicely-formatted timestamp for the logdump * output. If the parameter is not supplied, use the current time() value. * * * @author mike@givingassistant.org * @version 1.0,,0 * * @param int $_time * @return string * * HISTORY: * ======== * 06-07-17 mks original coding * 07-24-17 mks CORE-470: added parameter for log dump timestamp generation * */ function getDateTime(int $_time = 0): string { return (ltrim(rtrim('[' . date("d/m/y@H:i:s", ($_time ? $_time : time())) . ']'))); } /** * consoleLog() -- general function * * consoleLog takes three input parameters: * * $_res: the name of the (code) resource issuing the log request. (DEFAULT: "UNKN: ") * $_state: the console log state (Success, error, debug, etc.) (DEFAULT: "[ !]") * $_msg: the message to be logged * * If $_msg is not supplied, nothing will be output and control will return to the calling client. * * Otherwise a console message is generated and output to STDOUT. * * The function acknowledges the global constant (set in bootstrap) for the end-of-line character so that output * is simpatico with both the command line and the web-browser. * * * @author mike@givingassistant.org * @version 1.0 * * @param $_res * @param $_state * @param $_msg * * HISTORY: * ======== * 01-16-18 mks CORE-697: Original coding * 07-03-18 mks CORE-797: syslog redirect * 10-24-18 mks DB-47: fixed incorrect param list and error in syslog processing check for empty * 11-08-18 mks DB-55: fixed a PHP notice: explicitly casting $_msg to string to avoid notice * 09-24-19 mks DB-136: removed syslog support since that's now a broker thing * */ function consoleLog(string $_res, string $_state = CON_SYSTEM, $_msg = ''): void { global $eos; if (empty($_res)) $_res = 'UNKN: '; if (!empty($_msg)) echo getDateTime() . $_state . $_res . (string) $_msg . $eos; } /** * getRandomString() -- general function * * this function generates a random string -- defined by the characters A-Z inclusive, the characters a-z inclusive, * and the numbers (as a string) 0-9 inclusive. * * the function takes an optional parameter which is the length of the string to generate. if no parameter is * given, the default of 10 is used. (For absolutely no particular reason.) * * * @author mike@givingassistant.org * @version 1.0 * @noinspection PhpUnused * * @param $_length * @return string * * HISTORY: * ======== * 06-07-17 mks original coding * */ function getRandomString($_length = 10) { $str = ''; $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $size = strlen( $chars ); for($i = 0; $i < $_length; $i++) { $str .= $chars[ rand( 0, $size - 1 ) ]; } return($str); } /** * objectToArray() - general function * * recursive function to flatten objects to a single array. * If the object contains embedded objects, then self-invoke. * * @author mike@givingassistant.org * @version 1.0 * @noinspection PhpUnused * * @param $_obj - a collection of either objects or arrays * @return array - the flattened object * * HISTORY: * ======== * 06-07-17 mks original coding * */ function objectToArray($_obj) { $ph = null; // placeholder $ph = (is_object($_obj)) ? get_object_vars($_obj) : $_obj; foreach ($ph as $key => $val) { $ph[$key] = ((is_array($val)) or (is_object($val))) ? objectToArray($val) : $val; } return ($ph); }