952 days continuous production uptime, 40k+ tp/s single node. Original corpo Bitbucket history not included — clean archive commit.
1736 lines
61 KiB
PHP
1736 lines
61 KiB
PHP
<?php
|
|
/**
|
|
* functions.php
|
|
*
|
|
* collection of multi-use functions
|
|
*
|
|
* each function has it's own documentation section.
|
|
*
|
|
* functions that are stored here are usually used by the brokers although this file is read into memory as part of
|
|
* the bootstrap process.
|
|
*
|
|
*/
|
|
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
|
use PhpAmqpLib\Channel\AMQPChannel;
|
|
use PhpAmqpLib\Exception\AMQPRuntimeException;
|
|
use PhpAmqpLib\Message\AMQPMessage;
|
|
use PhpAmqpLib\Exception\AMQPTimeoutException;
|
|
|
|
const FUNC_RES = 'FUNC: ';
|
|
|
|
/**
|
|
* guid() -- global function
|
|
*
|
|
* this works in all php versions and generates a version 4 UUID.
|
|
*
|
|
* the function takes a single input parameter: $_useBraces -- which defaults to Boolean(false).
|
|
* if $_useBraces is true, then we'll return the GUID surrounded by braces (Microsoft format), otherwise, just
|
|
* return the GUID.
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param bool $_useBraces
|
|
* @return string
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 06-07-17 mks original coding
|
|
*
|
|
*/
|
|
function guid($_useBraces = false)
|
|
{
|
|
if (function_exists('com_create_guid')) {
|
|
return com_create_guid();
|
|
} else {
|
|
mt_srand((double)microtime() * 10000); //optional for php 4.2.0 and up.
|
|
$charId = strtoupper(md5(uniqid(rand(), true)));
|
|
$hyphen = chr(45); // "-"
|
|
if ($_useBraces) {
|
|
/** @noinspection PhpUnusedLocalVariableInspection */
|
|
$uuid = chr(123); // "{"
|
|
}
|
|
$uuid = substr($charId, 0, 8) . $hyphen
|
|
.substr($charId, 8, 4) . $hyphen
|
|
.substr($charId,12, 4) . $hyphen
|
|
.substr($charId,16, 4) . $hyphen
|
|
.substr($charId,20,12);
|
|
if ($_useBraces) {
|
|
$uuid .= chr(125); // "}"
|
|
}
|
|
return $uuid;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* postResponse() -- internal method
|
|
*
|
|
* Because this broker returns a payload response in a warehouse request, before the warehousing has actually happened,
|
|
* b/c we don't want to block the client on what could be a lengthy operation, we can actually execute this code from
|
|
* two locations in this file -- so, to reduce code duplication, this function provides the entry point to publish a
|
|
* response to the calling client, outside of the normal broker-event-processing flow.
|
|
*
|
|
* There are four input parameters to the method and one global parameter passed:
|
|
*
|
|
* $_retData -- an array containing the return-data payload to the calling client
|
|
* $_req -- this is the rabbitMQ original request payload
|
|
* $_log -- this is a copy of the current child log resource so we can post error messages if needed
|
|
* $_res -- the resource identifier
|
|
*
|
|
* the function returns void.
|
|
*
|
|
* todo -- consider using this stub in all brokers
|
|
*
|
|
*
|
|
* @author mike@givingassistant.org
|
|
* @version 1.0
|
|
*
|
|
* @param array $_retData
|
|
* @param object $_req
|
|
* @param gacErrorLogger $_log
|
|
* @param string $_res
|
|
*
|
|
*
|
|
* HISTORY:
|
|
* ========
|
|
* 04-26-18 mks _INF-188: original coding
|
|
*
|
|
*/
|
|
function postResponse(array $_retData, object $_req, gacErrorLogger $_log, string $_res = '????: '): void
|
|
{
|
|
$msg = new AMQPMessage(gzcompress(json_encode($_retData)), array(BROKER_CORRELATION_ID => $_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);
|
|
}
|