/** * convertCacheMap() -- private static method * * This private method is the gateway/entry-point for cacheMapping on data payloads. The function has the following * required input parameters: * * $_data -- this is the payload to be cacheMapped. This should be an indexed array of one, or more, assoc arrays * $_dir -- string value indicating if the data is incoming (IN) or outbound (OUT) * $_map -- this is the class-specific vector of cacheMapped settings pulled from the global cacheMap * $_type -- this defines the data payload as either record-data or query-data * $_errs -- this is a call-by-reference array that allows us to propagate error messages back up the stack * * The function looks at the contents of most of the input parameters and validates the content returning a null * if any of the params fail validation while also adding messages to the error stack and by publishing a message * to the error logger. * * Once validation is complete, we pass all of the input params to a second private function, allowing for * recursion in that function, and hopefully get back an array (which is passed though back up to the calling * client) that is successfully cacheMapped. * * * @author mike@givingassistant.org * @version 1.0 * * @param array $_data * @param string $_dir * @param array $_map * @param string $_type * @param array $_errs * @return array|null * * * HISTORY: * ======== * 02-25-19 mks DB-116: original coding * */ private static function convertCacheMap(array $_data, string $_dir, array $_map, string $_type, array &$_errs): ?array { // validate input param content -- input param type is implicitly validated by strong type decls if ($_dir != IN and $_dir != OUT) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = sprintf(ERROR_CACHE_DIRECTION, (string) $_dir); $_errs[] = $msg; static::$logger->data($hdr . $msg); return null; } if ($_type != STRING_QUERY and $_type != STRING_DATA) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = ERROR_CACHE_MAP_TYPE . $_type; $_errs[] = $msg; static::$logger->error($hdr . $msg); return null; } if (!is_array($_data)) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = $hdr . ERROR_DATA_ARRAY_NOT_ARRAY . STRING_DATA; $_errs[] = $msg; static::$logger->data($msg); return null; } if (!is_array($_map)) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = $hdr . ERROR_DATA_ARRAY_NOT_ARRAY . CACHE_MAP; $_errs[] = $msg; static::$logger->data($msg); return null; } try { return static::processCacheMap($_data, $_map, $_dir, $_type, $_errs); } catch (TypeError $t) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = $hdr . ERROR_TYPE_EXCEPTION; $_errs[] = $msg; static::$logger->warn($msg); $msg = $hdr . $t->getMessage(); $_errs[] = $msg; static::$logger->warn($msg); return null; } } /** * processCacheMap() -- private static method with recursion * * This function is responsible for the cacheMapping for both incoming and outbound data payloads. It is a * stand-alone function because of it's recursive nature -- when we encounter a sub-array within the payload, * we must recursively call this method in order to process the sub-array (etc.). * * There are the following input parameters to this method, all of which are required: * * $_data -- this is the incoming/outgoing data payload. The invoking method has parsed, for example, the incoming * data payload and, as an example, let's say there are three sub-arrays stored in the payload: * STRING_QUERY_DATA, STRING_SORT_DATA and STRING_RETURN_DATA -- the invoking method will make a total of three * calls to this method, one for each of the sub-arrays under BROKER_DATA. Note that $_data should be passed as * an indexed array s.t. each tuple in the array is processed as a separate record. * * $_map -- this is the cacheMap for the targeted class. In other words, it is not the entire cacheMap but the * named tuple for the current data class. * * $_dir -- this is a string value that may only be either IN or OUT (both are Namaste system constants). IN * designates the payload as incoming while OUT designates the payload as outbound. * * $_type -- this is a string value, defined as either STRING_QUERY or STRING_DATA, and is verified in the * the calling client. This value designates the type of payload to be mapped. * * $_es -- this is an array for the error-stack -- it's a call-by-reference parameter s.t. we can propagate any * error messages back to the invoking client. * * The method returns an array which, under optimal conditions, returns a mirror of the incoming data payload * save that the keys have been successfully cacheMapped. * * Any filed which fails cacheMapping (e.g.: not found) will be stored in the class static $badCacheFields and * will be implicitly returned to the calling client for processing. * * * @author mike@givingassistant.org * @version 1.0 * * * @param array $_data -- indexed array of data to be cacheMapped; may contain more than one record * @param array $_map -- cacheMap for the current data class * @param string $_dir -- indicates the direction (flow) of the data: either INcoming or OUTbound * @param string $_type - indicates payload type: either DATA or QUERY (validated in calling client) * @param array $_es -- call-by-reference array for returning error messages to the calling client * @return array|null -- returns the cacheMapped array or a null on error * * * HISTORY: * ======== * 02-25-19 mks DB-116: original coding * */ private static function processCacheMap(array $_data, array $_map, string $_dir, string $_type, array &$_es): ?array { $data = null; // container to hold the cacheMapped record $records = null; // container to hold all the cacheMapped records // todo -- test for subCollection array existing in the subC setting... which means you have to add it to the cacheMap data // this is where the cache-mapping magic happens... foreach ($_data as $record => $recordData) { foreach ($record as $column => &$value) { if ($_dir == IN) { // todo -- map off the type... // we're cache-mapping an incoming payload if (in_array($column, $_map[CACHE_MAP])) { if (is_array($value) and $_map[CACHE_SUBC] != STRING_NOT_DEFINED and array_key_exists(array_search($column, $_map[CACHE_MAP]), $_map[CACHE_SUBC])) { // note: not checking for the case where $value is an array but $newKey is not defined in the // cached SUBC definition for the class. This loose definition for sub-collections // permits the user to store sub-arrays without inspection/validation/mapping. try { // we have to process $value recursively as a sub-array $data[array_search($column, $_map[CACHE_MAP])] = static::processCacheMap($value, $_map, $_dir, $_type, $_es); } catch (TypeError $t) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = $hdr . ERROR_TYPE_EXCEPTION; $_errs[] = $msg; static::$logger->warn($msg); $msg = $hdr . $t->getMessage(); $_errs[] = $msg; static::$logger->warn($msg); return null; } } else { $data[array_search($column, $_map[CACHE_MAP])] = $value; } } else { static::$badCacheFields[$column] = $value; } } else { // we're cache-mapping an outbound payload so remove the class extension from the column name $newKey = str_replace($_map[CACHE_EXT], '', $column); if (array_key_exists($newKey, $_map[CACHE_MAP])) { // note: not checking for the case where $value is an array but $newKey is not defined in the // cached SUBC definition for the class. This loose definition for sub-collections // permits the user to store sub-arrays without inspection/validation/mapping. if (is_array($value) and $_map[CACHE_SUBC] != STRING_NOT_DEFINED and array_key_exists($newKey, $_map[CACHE_SUBC])) { try { // we have to process $value recursively as a sub-array $data[$_map[CACHE_MAP][$newKey]] = static::processCacheMap($value, $_map, $_dir, $_type,$_es); } catch (TypeError $t) { $hdr = basename(__METHOD__) . AT . __LINE__ . COLON; $msg = $hdr . ERROR_TYPE_EXCEPTION; $_errs[] = $msg; static::$logger->warn($msg); $msg = $hdr . $t->getMessage(); $_errs[] = $msg; static::$logger->warn($msg); return null; } } else { $data[$_map[CACHE_MAP][$newKey]] = $value; } } else { static::$badCacheFields[$newKey] = $value; } } } } if (!empty($data)) { $records[] = $data; unset($data); } // todo: else? return $records; }