Files
namaste/classes/gacFactory.class.inc
gramps 373ebc8c93 Archive: Namaste PHP AMQP framework v1.0 (2017-2020)
952 days continuous production uptime, 40k+ tp/s single node.
Original corpo Bitbucket history not included — clean archive commit.
2026-04-05 09:49:30 -07:00

1586 lines
73 KiB
PHP

<?php declare(strict_types=1);
/**
* gacFactory()
*
* This is the factory class which is used to instantiate all database class objects. Since Namaste supports and
* abstracts different data schemas, instantiation of those classes starts with this class.
*
* The basic algorithm is that, when this class is invoked, it analyzed the template passed to it's constructor.
* It instantiates a new data class based on the template settings and returns that object class back to the calling
* client.
*
* Some requested events, such as a schema-fetch, may not require full instantiation - instead a data-ball may be
* passed back to the calling client instead. So, it's up the calling client to understand and anticipate which data
* object will be returned back to them based on the event that's being invoked.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
*
* HISTORY:
* ========
* 06-15-17 mks original coding
* 03-02-18 mks CORE-680: deprecated trace/squelch logging
* 07-31-18 mks CORE-774: PHP7.2 exception handling
* 01-07-20 mks DB-150: PHP7.4 type casting for member variables
* 06-01-20 mks ECI-108: Protecting Instantiations from SMAX API Requests
*
*/
class gacFactory
{
public ?object $template = null; // container for the template class
private string $class; // defines the current class
private gacErrorLogger $logger; // instantiation of the logger class client
private string $eventID; // the broker event ID, extracted from meta
private array $validTemplates; // array/list of the valid templates stored in the templates directory
public array $meta; // hold a copy of the meta data passed in via the constructor
public array $schema = []; // the schema data object returned from a fetchSchema event
public bool $status; // boolean indicating the current status of the class
public string $state; // string indicating the current state of the class
private gacDdb $ddbWidget;
private gacMongoDB $mongoWidget;
private gacPDO $pdoWidget;
public object $widget; // the new object we'll construct and return to the calling client
public array $eventMessages; // container for processing event messages we may want to know about
private string $res; // label for console log output
private bool $debug; // for squelching output in prod envs
/**
* gacFactory constructor.
*
* this is the constructor for the factory class - the following input parameters are supported:
*
* $_meta -- this is the array of meta data as received by the broker. Within the meta data, we must extract:
*
* $_template -- this string defines the name of the class that we're wanting to work with. it is a required
* parameter and should match the name of the template without the ".class.inc" extensions.
* $_guid -- this is an optional parameter: if supplied, it means to instantiate the class and populate it
* with the record referenced by the guid.
*
* $_event -- this is the event that the constructor will process -- it defaults to instantiating a new class.
* $_es -- this is an array which is a call-by-reference parameter and will contain an error stack of
* on or more errors that were created during processing to help the calling client understand
* why the request failed.
*
* First, we're going to set some class variables before attempting to extract the template name from the
* meta-data payload. We'll load up a dynamic list of all templates currently in the templates directory and
* see if our template is in the list, if not, then return an error.
*
* Next, we process the event - and call the appropriate method. In the called method, the member variable state
* is updated and that's what's evaluated and subsequently returned to the calling client to indicate if the
* requested event successfully processed or not.
*
* Instantiation Security:
* -----------------------
* Namaste no longer checks for the environment before instantiation a data class. In most cases, any class can
* now be instantiated on any service. This follows the common Unix pattern "ALLOW_ALL_EXCEPT...".
*
* Where the "except" part happens is within the data template. There are two member variables related to class
* instantiation security:
*
* $isGA
* -----
* boolean, indicates if the data class is a Namaste (internal) class. If set to false, then the class is a "user"
* data class. This member is provided only as a sanity check for the next member.
* $authToken
* ----------
* This member can contain the Partner's API token key which is set in the constructor of the template class. By
* default, this member is assigned a string constant: NULL_STRING which is a string of zeros in 36-character
* GUID format. The presence of an actual, non-zero, string GUID indicates that this data class is proprietary to
* the user account, indicated by the token value and found within the SMAX token collection.
*
* When we're working with a user class, and the $authToken value is set, and we're instantiating on appServer
* ONLY, then we lookup the SMAX Token value to ensure that the account (a) exists and that the record status
* is (b) valid. If so, then we compare the GUID to the string value passed in the API call by SMAX.
*
* Correspondingly, these checks will only be performed when the request is coming from SMAX, meaning that the
* class can be instantiated internally by Namaste for events such as audit, journaling, user-validation, etc.
*
* $isGA should always be set to false for proprietary user classes providing an additional check to ensure
* the instantiation is legit.
*
* In summary, we'll instantiate a proprietary data class only if the following conditions are satisfied, in the
* following order:
* -- the instantiation is happening on the appServer service in Namaste
* -- the instantiation request originated from the SMAX API
* -- the meta payload has the Partner's API Token (SMAX inserted it into the meta payload for us, how nice!)
* -- the data template has a valid GUID assigned to the $authToken member
* -- the GUID token submitted by the requester matches the value in $authToken
*
* Note: This upgraded security deprecates the constant META_SKIP_ENV_CHECK
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_event
* @param array|null $_meta
* @param string|null $_guid
* @param array|null $_es
*
*
* HISTORY:
* ========
* 06-15-17 mks original coding
* 08-03-17 mks CORE-503: qualify schema and env of template against current xml configuration
* 02-21-18 mks _INF-139: registered destructor function
* 05-04-18 mks _INF-188: embedding the meta class into the widget
* 05-09-18 mks _INF-188: adding segundo service
* 06-06-18 mks CORE-1012: revamp security for data classes following an "ALLOW_ALL" pattern.
* 06-15-20 mks ECI-164: support for partner tlti designations, instantiation security for partner
* classes with override for system requests
* 12-21-20 mks DB-180: updated exception handling
*
*/
public function __construct(array $_meta, string $_event = FACTORY_EVENT_NEW_CLASS, string $_guid = '', array &$_es = array())
{
register_shutdown_function(array($this, '__destruct'));
$this->class = get_class($this);
$this->res = 'FACT: ';
$this->widget = new stdClass();
$this->eventMessages = [];
$this->status = false;
$this->debug = boolval(gasConfig::$settings[CONFIG_DEBUG]);
$file = basename(__FILE__); // using __FILE__ instead of __METHOD__ b/c constructor
$this->state = STATE_PENDING;
$this->eventID = (array_key_exists(META_EVENT_GUID, $_meta)) ? $_meta[META_EVENT_GUID] : '';
$tlti = STRING_CLASS_GAT;
$templateKey = null;
try {
$this->logger = new gacErrorLogger();
} catch (Throwable | TypeError $t) {
$hdr = sprintf(INFO_LOC, basename(__FILE__), __LINE__);
@handleExceptionMessaging($hdr, $t->getMessage(), $_es, true);
return;
}
// PROCESS THE TEMPLATE
if (!array_key_exists(META_TEMPLATE, $_meta)) {
$msg = ERROR_CONFIG_RESOURCE_404 . STRING_TEMPLATE;
$_es[] = $msg;
$this->logger->error($msg);
return;
}
// get a list of the current templates
$this->validTemplates = gasStatic::loadValidTemplateNames();
if (!is_array($this->validTemplates)) {
$_es[] = ERROR_TEMPLATE_DIR_404;
$this->logger->error(ERROR_TEMPLATE_DIR_404);
return;
}
// if this is a SMAX request, fetch and replace the TLTI based on the auth code
if (isset($_meta[CLIENT_AUTH_TOKEN]) and $_meta[CLIENT_AUTH_TOKEN] != NULL_TOKEN) {
// ensure a valid token...
if (!validateGUID($_meta[CLIENT_AUTH_TOKEN])) {
$_es[] = ERROR_CLIENT_AUTH_TOKEN_BAD;
$this->state = STATE_DATA_ERROR;
return;
}
if (!isset($_meta[META_TLTI]) or empty($_meta[META_TLTI])) {
$tlti = gasStatic::getTLTI($_meta[CLIENT_AUTH_TOKEN], $_meta[META_EVENT_GUID], $response);
if ($response[PAYLOAD_STATUS] and $response[PAYLOAD_STATE] == STATE_NOT_FOUND) {
$_es[] = ERROR_CLIENT_AUTH_TOKEN_BAD;
$this->logger->warn(sprintf(INFO_LOC, $file, __LINE__) . ERROR_CLIENT_AUTH_TOKEN_SEARCH . $_meta[CLIENT_AUTH_TOKEN]);
$this->state = STATE_REQUEST_REJECTED;
return;
} elseif (!$response[PAYLOAD_STATUS] or is_null($tlti)) {
$_es[] = ERROR_SOUR_PICKLE;
$this->logger->fatal(sprintf(INFO_LOC, $file, __LINE__) . ERROR_ARRAY_KEY_404 . SMAX_TLTI);
$this->state = STATE_FRAMEWORK_FAIL;
return;
}
// inject the tlti into the meta-payload for use later by the instantiation classes
$_meta[META_TLTI] = $tlti;
$templateKey = $response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0][STRING_KEY];
} else {
$templateKey = $_meta[CLIENT_AUTH_TOKEN];
}
} elseif (!isset($_meta[META_TLTI]) or empty($_meta[META_TLTI])) {
$_meta[META_TLTI] = $tlti;
}
// is the requested template in the list of valid templates?
if (!in_array(($_meta[META_TLTI] . $_meta[META_TEMPLATE]), $this->validTemplates)) {
$msg = ERROR_TEMPLATE_FILE_404 . $_meta[META_TLTI] . $_meta[META_TEMPLATE];
$_es[] = $msg;
$this->logger->error($msg);
return;
}
// instantiate a copy of the template
$tc = $_meta[META_TLTI] . $_meta[META_TEMPLATE];
try {
$this->template = new $tc();
} catch (Throwable | TypeError $t) {
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__);
@handleExceptionMessaging($hdr, $t->getMessage(), $_es, true);
$this->state = STATE_NOT_FOUND;
return;
}
$env = $this->template->service;
$dbConfig = gasConfig::$settings[CONFIG_DATABASE];
// In this block, we're validating the current environment against the schema because not all of the schema's
// exist for every environment... this is a weak check just to ensure we don't try to instantiate a class,
// internal, or external, for a schema that's non-existent in the current env.
// -------------------------------------------------------------------------------------------------------------
$msg = '';
switch ($this->template->schema) {
case TEMPLATE_DB_MONGO :
if ($dbConfig[CONFIG_DATABASE_MONGODB][CONFIG_DATABASE_MONGODB_ENABLED] != 1) {
$msg = sprintf(CONFIG_DB_NOT_ENABLED, TEMPLATE_DB_MONGO);
$_es[] = $msg;
$this->logger->warn($msg);
}
// we can have mongo running on all services
if (($env == CONFIG_DATABASE_SERVICE_APPSERVER and $dbConfig[CONFIG_DATABASE_MONGODB][CONFIG_DATABASE_MONGODB_APPSERVER][CONFIG_DATABASE_MONGODB_ENABLED] != 1)
or ($env == CONFIG_DATABASE_SERVICE_ADMIN and $dbConfig[CONFIG_DATABASE_MONGODB][CONFIG_DATABASE_MONGODB_ADMIN][CONFIG_DATABASE_MONGODB_ENABLED] != 1)
or ($env == CONFIG_DATABASE_SERVICE_SEGUNDO and $dbConfig[CONFIG_DATABASE_MONGODB][CONFIG_DATABASE_SERVICE_SEGUNDO][CONFIG_DATABASE_MONGODB_ENABLED] != 1)
or ($env == CONFIG_DATABASE_SERVICE_TERCERO and $dbConfig[CONFIG_DATABASE_MONGODB][CONFIG_DATABASE_SERVICE_TERCERO][CONFIG_DATABASE_MONGODB_ENABLED] != 1)) {
$msg = sprintf(CONFIG_DB_ENV_NOT_ENABLED, TEMPLATE_DB_MONGO, $env, $_meta[META_TEMPLATE]);
$_es[] = $msg;
$this->logger->warn($msg);
}
break;
case TEMPLATE_DB_DDB :
if ($dbConfig[CONFIG_DATABASE_DDB][CONFIG_DATABASE_DDB_ENABLED] != 1) {
$msg = sprintf(CONFIG_DB_NOT_ENABLED, TEMPLATE_DB_MONGO);
$_es[] = $msg;
$this->logger->warn($msg);
}
// DynamoDB can be running on appServer or Admin
if (($env == CONFIG_DATABASE_SERVICE_APPSERVER and $dbConfig[CONFIG_DATABASE_DDB][CONFIG_DATABASE_DDB_APPSERVER][CONFIG_DATABASE_DDB_ENABLED] != 1)
or ($env == CONFIG_DATABASE_SERVICE_ADMIN and $dbConfig[CONFIG_DATABASE_DDB][CONFIG_DATABASE_DDB_ADMIN][CONFIG_DATABASE_DDB_ENABLED] != 1)) {
$msg = sprintf(CONFIG_DB_ENV_NOT_ENABLED, TEMPLATE_DB_MONGO, $env, $_meta[META_TEMPLATE]);
$_es[] = $msg;
$this->logger->warn($msg);
}
break;
case TEMPLATE_DB_PDO :
if ($dbConfig[CONFIG_DATABASE_PDO][CONFIG_DATABASE_PDO_ENABLED] != 1) {
$msg = sprintf(CONFIG_DB_NOT_ENABLED, TEMPLATE_DB_MONGO);
$_es[] = $msg;
$this->logger->warn($msg);
}
// PDO (mariaDB) can be running on appServer and Segundo
if (($env == CONFIG_DATABASE_SERVICE_APPSERVER and $dbConfig[CONFIG_DATABASE_PDO][CONFIG_DATABASE_PDO_APPSERVER][CONFIG_DATABASE_PDO_ENABLED] != 1)
or ($env == CONFIG_DATABASE_SERVICE_SEGUNDO and $dbConfig[CONFIG_DATABASE_PDO][CONFIG_DATABASE_SERVICE_SEGUNDO][CONFIG_DATABASE_PDO_ENABLED] != 1)) {
$msg = sprintf(CONFIG_DB_ENV_NOT_ENABLED, TEMPLATE_DB_PDO, $env, $_meta[META_TEMPLATE]);
$_es[] = $msg;
$this->logger->warn($msg);
}
break;
}
if (!empty($msg)) return;
// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------
// instantiation security begins...
if (gasConfig::$settings[CONFIG_REGISTERED_SERVICES][CONFIG_BROKER_APPSERVER] == 1 and $env == ENV_APPSERVER) {
// ^^^ if we're on appServer...
if (isset($_meta[META_CLIENT]) and $_meta[META_CLIENT] == CLIENT_API_USER) {
// ^^^ if the request originated from SMAX-User...
if (isset($_meta[CLIENT_AUTH_TOKEN])) {
// ^^^ if the request has the Partner API token...
if (isset($this->template->authToken) and $this->template->authToken != NULL_TOKEN) {
// ^^^ if we have a non-zero auth token set in the template class
// we need to validate the client-auth-token in the template against the SMAX record
// which we already fetched in real-time (above) when deriving the tlti...
if ($this->template->authToken != $templateKey) {
$_es[] = ERROR_CLIENT_AUTH_TOKEN_MISMATCH;
$this->state = STATE_AUTH_ERROR;
return;
}
} // END: check for presence of auth token
} else {
// error: we got a SMAX Client request but the meta payload is missing the auth token
$_es[] = ERROR_CLIENT_AUTH_TOKEN_404;
$this->state = STATE_META_ERROR;
return;
} // END: check for client Auth Token in meta payload
} // END: check for SMAX user
} // END: check for appServer env
// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------
// instantiate a meta-data class
try {
$tmpMeta = new gacMeta(true);
// this is NOT the functions.php::validateMetaPayload() function!
if (!$tmpMeta->validateMetaPayload($_meta)) {
if (empty($_es) and !empty($tmpMeta->eventMessages)) {
$_es = $tmpMeta->eventMessages;
} elseif (!empty($_es) and !empty($tmpMeta->eventMessages)) {
$_es = array_merge($_es, $tmpMeta->eventMessages);
}
return;
}
} catch (Throwable | TypeError $t) {
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__);
@handleExceptionMessaging($hdr, $t->getMessage(), $_es, true);
return;
}
// -------------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------------
// normally, for an instantiation class (widget) this would be a copy of the instantiated meta class and the
// meta data would go into metaPayload. However, remember this is the ephemeral factory class...
$this->meta = $_meta;
// PROCESS THE EVENT
switch ($_event) {
case FACTORY_EVENT_SCHEMA_REQUEST:
case FACTORY_EVENT_NEW_CLASS:
try {
$this->instantiateClass($_guid);
$this->fetchSchema($this->eventMessages);
$this->widget->addMetaTemplate($tmpMeta);
$this->widget->templateReport = $this->schema;
$this->status = true;
$this->state = STATE_SUCCESS;
} catch (Throwable | TypeError $t) {
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__);
@handleExceptionMessaging($hdr, $t->getMessage(), $_es, true);
}
break;
default:
$_es[] = (empty($_event)) ? ERROR_NO_EVENT : ERROR_UNKNOWN_EVENT;
break;
}
$tmpMeta->__destruct();
unset($tmpMeta);
// -------------------------------------------------------------------------------------------------------------
}
/**
* instantiateClass() -- private method
*
* this method is called from the constructor whenever someone wants to instantiate a new class. The parameter
* passed to the method is a guid -- which is optional -- and, if provided, indicates that the instantiation of
* the class include loading the record (singular) referenced by the guid.
*
* There are no explicit returns -- the new object is created under the class property "widget" (because all
* factories make widgets, yah?) and that widget object (which also has the standard state/status properties)
* contains the success/fail indicators.
*
* As more schemas are added to the framework, this method will expand to accommodate each schema type.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_guid
*
*
* HISTORY:
* ========
* 06-22-17 mks initial coding
* 07-06-17 mks CORE-463: logging/metrics in mongodb
* 09-18-17 mks CORE-562: PDO class coding
* 01-07-20 mks DB-150: refactored making $widget a reference-assignment to another db-class resource
*
*/
private function instantiateClass(string $_guid)
{
try {
switch ($this->template->schema) {
case TEMPLATE_DB_DDB :
$this->ddbWidget = new gacDdb($this->meta, $_guid);
$this->widget =& $this->ddbWidget;
break;
case TEMPLATE_DB_MONGO :
$this->mongoWidget = new gacMongoDB($this->meta, $_guid);
$this->widget =& $this->mongoWidget;
break;
case TEMPLATE_DB_PDO :
$this->pdoWidget = new gacPDO($this->meta, $_guid);
$this->widget =& $this->pdoWidget;
break;
default :
$msg = ERROR_CLASS_SCHEMA_404 . COLON . $this->template->schema;
$this->logger->error($msg);
$this->eventMessages[] = $msg;
$this->state = STATE_SCHEMA_ERROR;
$this->status = false;
$this->widget = new stdClass();
break;
}
} catch (Throwable $t) {
$msg = ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
$this->eventMessages[] = $msg;
if ($this->logger->available) {
$this->logger->error($msg);
} else {
consoleLog($this->res, CON_ERROR, $msg);
}
}
}
/**
* fetchSchema() -- private class method
*
* the fetchSchema() event instantiates the template passed implicitly as a class member and creates an
* array of data describing the data template - the array has been homogenized to work with all schemas.
*
* On success, we'll return the template report (array) otherwise, we'll just return a null value. Also,
* on success, we'll update the class $status member to Boolean(true) so that the constructor realizes that
* the operation was successful.
*
* Programmer Notes:
* -----------------
* While there exists only one explicitly coded point of failure, this processing will work unless there's
* something missing, or something wrong, in the template file. So, if you're not getting the expected
* results in the template report, check the template file first.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_es
*
*
* HISTORY:
* ========
* 06-15-17 mks original coding
* 07-07-17 mks updated: each schema type has their own indexing strategy - the template report now
* evaluates the schema and generates the appropriate index report.
* 09-20-17 mks CORE-562: added PDO processing, refactored code segments to reduce footprint
* 10-18-17 mks CORE-584: added PDO dataObjects processing, system-variables section
* 06-08-18 mks CORE-1035: adding migration and warehouse to schema report
* 11-01-19 mks DB-136: added the indexList to processing, fixed some switch-break syntax
*
*/
private function fetchSchema(array &$_es): void
{
$templateReport = null;
$templateReport[TEMPLATE_NAMASTE_INFO] = null; // ensure this is the first element in the array
if (empty($this->template)) {
$_es[] = ERROR_TEMPLATE_FILE_404;
$this->logger->warn(ERROR_TEMPLATE_FILE_404);
return;
}
$templateReport[TEMPLATE_FEATURE_TITLE] = $this->template->collection;
$templateReport[TEMPLATE_FEATURE_WH_TITLE] = (isset($this->template->whTemplate)) ? $this->template->whTemplate : STRING_NOT_DEFINED;
$templateReport[TEMPLATE_FEATURE_USES_CACHE_MAPS] = ($this->template->setCache and !empty($this->template->cacheMap)) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_USES_LOCKS] = ($this->template->setLocking) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_USES_TIMER] = ($this->template->setTimers) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_USES_TOKENS] = ($this->template->setTokens) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_USES_HISTORY] = ($this->template->setHistory) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_ALLOWS_AUDITS] = ($this->template->setAuditing) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_ALLOWS_JOURNALING] = ($this->template->setJournaling) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_ALLOWS_DELETES] = ($this->template->setDeletes) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_ALLOWS_UPDATES] = ($this->template->setUpdates) ? YES : NO;
$templateReport[TEMPLATE_FEATURE_CACHE_EXPIRES] = $this->template->cacheTimer;
try {
switch ($this->template->schema) {
case TEMPLATE_DB_DDB :
// get the field lists and data types
$this->getFieldList($templateReport);
// if Binary fields are declared, grab them but filter through the cache map and exposed field lists
$templateReport = $this->getBinaryFields($templateReport);
// process the primary, global and local indexes
foreach ($this->template->indexes as $key => $value)
$templateReport[DDB_INDEX_TYPE_PRIMARY][$value] = $key;
foreach ($this->template->globalIndexes as $key => $value) {
if ($key == STRING_INDEXES)
foreach ($key as $k => $v)
$templateReport[DDB_INDEX_TYPE_GLOBAL][$v] = $k;
else
$templateReport[$key] = $value;
}
foreach ($this->template->localIndexes as $key => $value) {
if ($key == STRING_INDEXES)
foreach ($key as $k => $v)
$templateReport[DDB_INDEX_TYPE_LOCAL][$v] = $k;
else
$templateReport[$key] = $value;
}
break;
case TEMPLATE_DB_MONGO :
$this->processTemplateIndexes($templateReport);
break;
case TEMPLATE_DB_PDO :
// generate the mySQL/mariaDB template report
// populate the field lists and data types
$this->getFieldList($templateReport);
// if Binary fields are declared, grab them but filter through the cache map and exposed field lists
$templateReport = $this->getBinaryFields($templateReport);
// process the indexes
$this->processTemplateIndexes($templateReport);
// get the database objects
$templateReport = $this->processDBObjects($templateReport);
break;
}
if (isset($this->widget->migrationConfig) and !empty($this->widget->migrationConfig)) {
$templateReport[TEMPLATE_FEATURE_MIG_CONFIG] = var_export($this->widget->migrationConfig, true);
} else {
$templateReport[TEMPLATE_FEATURE_MIG_CONFIG] = STRING_NOT_DEFINED;
}
if (isset($this->widget->warehouseConfig) and !empty($this->widget->warehouseConfig)) {
$templateReport[TEMPLATE_FEATURE_WH_CONFIG] = var_export($this->widget->warehouseConfig, true);
} else {
$templateReport[TEMPLATE_FEATURE_WH_CONFIG] = STRING_NOT_DEFINED;
}
$templateReport = $this->getTemplateReportSystemHeader($templateReport);
} catch (Throwable $t) {
$msg = ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
$_es[] = $msg;
if ($this->logger->available) {
$this->logger->error($msg);
} else {
consoleLog($this->res, CON_ERROR, $msg);
}
return;
}
$this->status = true;
$this->schema = $templateReport;
}
/**
* getTemplateReportSystemHeader() -- private method
*
* This method requires one input parameter - an array containing the current template report.
*
* This method returns the update template report back to the calling client once the namaste server information
* has been copied from the configuration (static) class.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
*
* HISTORY:
* ========
* 10-18-17 mks CORE-584: original coding
*
*/
private function getTemplateReportSystemHeader(array $_templateReport): array
{
$_templateReport[TEMPLATE_NAMASTE_INFO] = [
TEMPLATE_NAMASTE_ENV => gasConfig::$settings[CONFIG_ID][CONFIG_ID_ENV],
TEMPLATE_NAMASTE_VER => gasConfig::$settings[CONFIG_ID][CONFIG_ID_VER],
TEMPLATE_NAMASTE_DB => (gasConfig::$settings[CONFIG_DEBUG]) ? STRING_ENABLED : STRING_DISABLED,
TEMPLATE_NAMASTE_AUDIT => (gasConfig::$settings[CONFIG_AUDIT_ON]) ? STRING_ENABLED : STRING_DISABLED,
TEMPLATE_NAMASTE_JOURNAL => (gasConfig::$settings[CONFIG_JOURNAL_ON]) ? STRING_ENABLED : STRING_DISABLED
];
return($_templateReport);
}
/**
* processDBObjects() -- private method
*
* This method is part of the template report-generation process.
*
* The input parameter is the array containing the templateReport in it's current state.
* The return is the templateReport with the DB-Objects inserted into the report.
*
* The only validation check we make is to ensure we're working with a current PDO instantiation class.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
* HISTORY:
* ========
* 10-18-17 mks CORE-584: original coding
*
*/
private function processDBObjects(array $_templateReport): array
{
$schema = $this->widget->schema;
if ($schema != TEMPLATE_DB_PDO) {
$msg = sprintf(ERROR_SCHEMA_NOT_SUPPORTED, $schema);
$this->eventMessages[] = $msg;
if (gasConfig::$settings[CONFIG_DEBUG]) $this->logger->debug($msg);
return ($_templateReport);
}
if (!isset($this->template->dbObjects)) {
$_templateReport[TEMPLATE_DB_OBJECTS] = STRING_NOT_DEFINED;
} else {
$_templateReport[TEMPLATE_DB_OBJECTS] = $this->template->dbObjects;
}
return($_templateReport);
}
/**
* processTemplateIndexes() -- private method
*
* This method is invoked by the fetchSchema (above) method and requires a single input parameter which is a
* call-by-reference parameter:
*
* $_template: container that will eventually be assigned to the the schema property for the class
*
* This method was pulled from fetchSchema() and updated to check that a field has been set before we evaluate
* if the field is empty - because the index fields change between the mongoDB and PDO schemas.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return bool
*
*
* HISTORY:
* ========
* 09-20-18 mks CORE-562: original coding
*
*/
private function processTemplateIndexes(array &$_templateReport): bool
{
$schema = $this->widget->schema;
if ($schema != TEMPLATE_DB_PDO and $schema != TEMPLATE_DB_MONGO) {
$msg = sprintf(ERROR_SCHEMA_NOT_SUPPORTED, $schema);
$this->eventMessages[] = $msg;
if (gasConfig::$settings[CONFIG_DEBUG]) $this->logger->debug($msg);
return false;
}
try {
switch ($schema) {
case TEMPLATE_DB_MONGO :
if ($this->template->setCache and !empty($this->template->cacheMap)) {
$_templateReport = $this->mongoCacheMappedIndexes($_templateReport);
} elseif (empty($this->template->cacheMap) and !empty($this->template->exposedFields)) {
$_templateReport = $this->mongoExposedFieldsIndexes($_templateReport);
} else {
$_templateReport = $this->mongoFieldIndexes($_templateReport);
}
break;
case TEMPLATE_DB_PDO :
if ($this->template->setCache and !empty($this->template->cacheMap)) {
$_templateReport = $this->pdoCacheMappedIndexes($_templateReport);
} elseif (empty($this->template->cacheMap and !empty($this->template->exposedFields))) {
$_templateReport = $this->pdoExposedFieldIndexes($_templateReport);
} else {
$_templateReport = $this->pdoFieldIndexes($_templateReport);
}
break;
default:
$msg = sprintf(ERROR_SCHEMA_NOT_SUPPORTED, $schema);
$this->eventMessages[] = $msg;
if (gasConfig::$settings[CONFIG_DEBUG]) $this->logger->debug($msg);
return false;
break;
}
} catch (Throwable $t) {
$msg = ERROR_THROWABLE_EXCEPTION . COLON . $t->getMessage();
$this->eventMessages[] = $msg;
if ($this->logger->available) {
$this->logger->error($msg);
} else {
consoleLog($this->res, CON_ERROR, $msg);
}
return false;
}
return true;
}
/**
* pdoFieldIndexes() -- private method
*
* This method is called from the processTemplateIndexes() method which passes a single input parameter to the
* method -- the template report which is in-progress of being build. This method will add the fieldList
* and process and add the template indexes to the report.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
*
* HISTORY:
* ========
* 09-22-17 mks CORE-562: original coding
* 06-15-20 mks ECI-164: fixed warning in index processing b/c not all indexes in $indexList are k->v arrays
*
*/
private function pdoFieldIndexes(array $_templateReport): array
{
// build the template field list first
foreach ($this->template->fields as $key => $value) {
if ($key != PDO_ID) {
$_templateReport[TEMPLATE_FIELD_LIST][$key] = $value;
}
}
// build the index loop-control variants...
$indexList = [
'indexFields', 'singleFields', 'uniqueIndexes', 'binFields'
];
$indexLabels = [
TEMPLATE_FIELD_INDEX_LIST, TEMPLATE_SINGLE_INDEXES, TEMPLATE_UNIQUE_INDEX_PROPERTY, TEMPLATE_BINARY_FIELD_LIST
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (!is_null($this->template->$currentIndex)) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex) and is_array($currentIndex)) {
foreach ($this->template->$currentIndex as $key => $value) {
if ($key != PDO_ID) {
$_templateReport[$indexLabels[$counter]][] = $key;
}
}
} elseif (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key) {
if ($key != PDO_ID) {
$_templateReport[$indexLabels[$counter]][] = $key;
}
}
}
$counter++;
}
}
if (isset($this->template->compoundIndexes) and !empty($this->template->compoundIndexes)) {
foreach ($this->template->compoundIndexes as $key => $subArray) {
foreach ($subArray as $indexKey => $value) {
if ($indexKey != PDO_ID) {
if (!isset($_templateReport[TEMPLATE_COMPOUND_INDEXES]) or !in_array($indexKey, $_templateReport[TEMPLATE_COMPOUND_INDEXES])) {
$_templateReport[TEMPLATE_COMPOUND_INDEXES][] = $indexKey;
}
}
}
}
}
return($_templateReport);
}
/**
* mongoFieldIndexes() -- private method
*
* This method is called from processTemplateIndexes() when it's been determined that the current instantiation
* is a mongoDB class and that both cacheMapping and ExposedFields limitations do not exist. (System collections
* would be a good example...)
*
* Based on the index architecture, we're able to group all the indexes into three processing loops -- each loop
* processes the index fields in the template and adds them to the template report as long as the current field
* isn't the MONGO_ID which is never exposed to the client.
*
* The input parameter to the method is the partially completed template report, which will eventually be
* assigned as the class schema once this method processes and assigns the field and index field lists.
*
* The return value, an array, is the updated template report.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
*
* HISTORY:
* ========
* 09-22-17 mks CORE-562: original coding
* 10-19-17 mks CORE-610: added not-defined placeholders for not-defined indexes
* 01-31-18 mks _INF-139: support for non-caching classes when building index list
*
*/
private function mongoFieldIndexes(array $_templateReport): array
{
foreach ($this->template->fields as $key => $value) {
if ($key != MONGO_ID) {
$_templateReport[TEMPLATE_FIELD_LIST][$key] = $value;
}
}
// set-up the index lists and process
$indexList = [
'singleFields', 'uniqueIndexes', 'ttlIndexes', 'subC'
];
$indexLabels = [
TEMPLATE_SINGLE_INDEXES, TEMPLATE_UNIQUE_INDEX_PROPERTY,
TEMPLATE_TTL_INDEX_PROPERTY, TEMPLATE_SUB_COLLECTION_FIELDS
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key => $value) {
if ($value != MONGO_ID) {
$_templateReport[$indexLabels[$counter]][] = $key;
}
}
} else {
$_templateReport[$indexLabels[$counter]] = STRING_NOT_DEFINED;
}
$counter++;
}
// set-up the index lists and process
$indexList = [
'compoundIndexes', 'multiKey', 'partialIndexes'
];
$indexLabels = [
TEMPLATE_COMPOUND_INDEXES, TEMPLATE_MULTIKEY_INDEXES, TEMPLATE_PARTIAL_INDEX_PROPERTY
];
// nested-array (sub-key) indexes
// example: 'cIdx1Test' => [TEST_FIELD_TEST_INT => 1, TEST_FIELD_TEST_DOUBLE => -1 ]
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key => $subArray) {
foreach ($subArray as $indexKey => $value) {
if ($indexKey != MONGO_ID and !isset($_templateReport[$indexLabels[$counter]])) {
$_templateReport[$indexLabels[$counter]][] = $indexKey;
}
}
}
} else {
$_templateReport[$indexLabels[$counter]] = STRING_NOT_DEFINED;
}
$counter++;
}
// set-up the index lists and process (these are all indexed arrays)
$indexList = [
'indexFields', 'regexFields', 'binFields'
];
$indexLabels = [
TEMPLATE_FIELD_INDEX_LIST, TEMPLATE_REGEX_INDEXES, TEMPLATE_BINARY_FIELD_LIST
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key) {
if ($this->template->setCache) {
if (array_key_exists($key, $this->template->cacheMap)) {
$_templateReport[$indexLabels[$counter]][] = $key;
}
} else {
$_templateReport[$indexLabels[$counter]][] = $key;
}
}
} else {
$_templateReport[$indexLabels[$counter]] = STRING_NOT_DEFINED;
}
$counter++;
}
return($_templateReport);
}
/**
* pdoCacheMappedIndexes() -- private method
*
* This method is called from processTemplateIndexes() and is responsible for processing PDO class indexes
* in a template file when said class has caching enabled and a cacheMap built in the template.
*
* The input parameter to the method is the in-progress template array from the calling client. During processing
* of the template indices, we'll continue to build the template report which will be the array passed back
* to the calling client.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
*
* HISTORY:
* ========
* 09-22-17 mks CORE-562: original coding
* 10-18-17 mks CORE-584: bug fixing, added protectedFields to report
* 10-19-17 mks CORE-610: added event messages for when a key is not in cacheMap
* 03-01-18 mks CORE-689: filtering identical error messages out of the eventMessage container,
* filtering 'id' out of comparisons b/c we never expose this key
*
*/
private function pdoCacheMappedIndexes(array $_templateReport): array
{
// build the template field list first
foreach ($this->template->fields as $key => $value) {
if ($key != PDO_ID) {
if (array_key_exists($key, $this->template->cacheMap)) {
$_templateReport[TEMPLATE_FIELD_LIST][$this->template->cacheMap[$key]] = $value;
} else {
$msg = sprintf(ERROR_MDB_FIELD_NOT_CACHED, $key, $this->template->collection);
if (!in_array($msg, $this->eventMessages)) {
$this->eventMessages[] = $msg;
if (gasConfig::$settings[CONFIG_DEBUG]) $this->logger->debug($msg);
}
}
}
}
$indexList = [
'indexFields', 'singleFields', 'uniqueIndexes', 'binFields', 'protectedFields'
];
$indexLabels = [
TEMPLATE_FIELD_INDEX_LIST, TEMPLATE_SINGLE_INDEXES, TEMPLATE_UNIQUE_INDEX_PROPERTY,
TEMPLATE_BINARY_FIELD_LIST, TEMPLATE_PROTECTED_FIELDS
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key) {
if ($key != PDO_ID) {
if (array_key_exists($key, $this->template->cacheMap)) {
$_templateReport[$indexLabels[$counter]][] = $this->template->cacheMap[$key];
} else {
$msg = sprintf(ERROR_MDB_FIELD_NOT_CACHED, $key, $this->template->collection);
if (!in_array($msg, $this->eventMessages)) {
$this->eventMessages[] = $msg;
if (gasConfig::$settings[CONFIG_DEBUG]) $this->logger->debug($msg);
}
}
}
}
} else {
$_templateReport[$indexLabels[$counter]] = STRING_NOT_DEFINED;
}
$counter++;
}
if (isset($this->template->compoundIndexes) and !empty($this->template->compoundIndexes)) {
foreach ($this->template->compoundIndexes as $key => $subArray) {
if ($key != PDO_ID) {
foreach ($subArray as $indexKey => $value) {
if (array_key_exists($value, $this->template->cacheMap)) {
$_templateReport[TEMPLATE_COMPOUND_INDEXES][$key][] = $this->template->cacheMap[$value];
} else {
$msg = sprintf(ERROR_MDB_FIELD_NOT_CACHED, $key, $this->template->collection);
if (!in_array($msg, $this->eventMessages)) {
$this->eventMessages[] = $msg;
if (gasConfig::$settings[CONFIG_DEBUG]) $this->logger->debug($msg);
}
}
}
}
}
} else {
$_templateReport[TEMPLATE_COMPOUND_INDEXES] = STRING_NOT_DEFINED;
}
return($_templateReport);
}
/**
* mongoCacheMappedIndexes() -- private method
*
* This method is called from the processTemplateIndexes() method - we pass the templateReport from that method
* as the only input parameter to which we'll add the indexes... said report is returned to the calling program
* and will eventually become the schema's template report.
*
* The method processes the different types of indexes (in terms of their internal format), maps the field names
* against the current cache-map, and stores those into the appropriate index containers.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
*
* HISTORY:
* ========
* 09-21-17 mks CORE-562: original coding
* 10-19-17 mks CORE-610: refactored the processing for the new index formats, including sort-direction,
* explicitly enumerating not-defined indexes as part of the report, and adding
* error messages when a column is not found in the cache-map
* 10-25-19 mks DB-136: error message improvements which also necessitated type-error handling
*
*/
private function mongoCacheMappedIndexes(array $_templateReport): array
{
$consoleMessages = [];
// build the template field list first
foreach ($this->template->fields as $key => $value) {
if (array_key_exists($key, $this->template->cacheMap)) {
$_templateReport[TEMPLATE_FIELD_LIST][$this->template->cacheMap[$key]] = $value;
} else {
try {
$this->processCacheMapSearchErrors((string) $key, $consoleMessages);
} catch (TypeError $t) {
$msg = sprintf(INFO_LOC, __METHOD__, __LINE__) . ERROR_TYPE_EXCEPTION;
$this->logger->error($msg);
consoleLog($this->res, CON_ERROR, $msg);
$this->state = STATE_DATA_ERROR;
$this->status = false;
return [];
}
}
}
// set-up the like-formatted index lists and process (col -> sortDir)
$indexList = [
'singleFields', 'uniqueIndexes'
];
$indexLabels = [
TEMPLATE_SINGLE_INDEXES, TEMPLATE_UNIQUE_INDEX_PROPERTY
];
// associative array (key) indexes
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key => $value) {
if (array_key_exists($key, $this->template->cacheMap)) {
$_templateReport[$indexLabels[$counter]][$this->template->cacheMap[$key]] = ($value == 1) ? STRING_SORT_ASC : STRING_SORT_DESC;
} else {
try {
$this->processCacheMapSearchErrors((string)$key, $consoleMessages);
} catch (TypeError $t) {
$msg = sprintf(INFO_LOC, __METHOD__, __LINE__) . ERROR_TYPE_EXCEPTION;
$this->logger->error($msg);
consoleLog($this->res, CON_ERROR, $msg);
$this->state = STATE_DATA_ERROR;
$this->status = false;
return [];
}
}
}
} else {
$_templateReport[$indexLabels[$counter]] = STRING_NOT_DEFINED;
}
$counter++;
}
// set-up the index lists and process
$indexList = [
'compoundIndexes', 'multiKey'
];
$indexLabels = [
TEMPLATE_COMPOUND_INDEXES, TEMPLATE_MULTIKEY_INDEXES
];
// nested-array (sub-key) indexes
// example: 'cIdx1Test' => [TEST_FIELD_TEST_INT => 1, TEST_FIELD_TEST_DOUBLE => -1 ]
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key => $subArray) {
foreach ($subArray as $indexKey => $value) {
if (array_key_exists($indexKey, $this->template->cacheMap)) {
if (!isset($_templateReport[$indexLabels[$counter]]) or !in_array($this->template->cacheMap[$indexKey], $_templateReport[$indexLabels[$counter]])) {
$_templateReport[$indexLabels[$counter]][$key][] = [$this->template->cacheMap[$indexKey] => ($value == 1) ? STRING_SORT_ASC : STRING_SORT_DESC];
} else {
try {
$this->processCacheMapSearchErrors((string)$indexKey, $consoleMessages);
} catch (TypeError $t) {
$msg = sprintf(INFO_LOC, __METHOD__, __LINE__) . ERROR_TYPE_EXCEPTION;
$this->logger->error($msg);
consoleLog($this->res, CON_ERROR, $msg);
$this->state = STATE_DATA_ERROR;
$this->status = false;
return [];
}
}
}
}
}
} else {
$_templateReport[$indexLabels[$counter]] = STRING_NOT_DEFINED;
}
$counter++;
}
// set-up the index lists and process (these are all indexed arrays)
$indexList = [
'indexFields', 'regexFields', 'binFields', 'protectedFields', 'exposedFields'
];
$indexLabels = [
TEMPLATE_FIELD_INDEX_LIST, TEMPLATE_REGEX_INDEXES, TEMPLATE_BINARY_FIELD_LIST,
TEMPLATE_PROTECTED_FIELDS, TEMPLATE_EXPOSED_FIELDS
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key) {
if (array_key_exists($key, $this->template->cacheMap)) {
$_templateReport[$indexLabels[$counter]][] = $this->template->cacheMap[$key];
} else {
try {
$this->processCacheMapSearchErrors((string)$key, $consoleMessages);
} catch (TypeError $t) {
$msg = sprintf(INFO_LOC, __METHOD__, __LINE__) . ERROR_TYPE_EXCEPTION;
$this->logger->error($msg);
consoleLog($this->res, CON_ERROR, $msg);
$this->state = STATE_DATA_ERROR;
$this->status = false;
return [];
}
}
}
} else {
$_templateReport[$indexLabels[$counter]] = STRING_NOT_DEFINED;
}
$counter++;
}
// the remaining indexes have to be processed individually owing to their uniqueness...
// partial indexes ( indexName => column => operand => bool)
if (isset($this->template->partialIndexes) and !is_null($this->template->partialIndexes)) {
foreach ($this->template->partialIndexes as $indexName => $index) {
foreach ($index as $column => $subArray) {
if (array_key_exists($column, $this->template->cacheMap)) {
$_templateReport[TEMPLATE_PARTIAL_INDEX_PROPERTY][$indexName] = [$this->template->cacheMap[$column] => $subArray];
} else {
try {
$this->processCacheMapSearchErrors((string)$column, $consoleMessages);
} catch (TypeError $t) {
$msg = sprintf(INFO_LOC, __METHOD__, __LINE__) . ERROR_TYPE_EXCEPTION;
$this->logger->error($msg);
consoleLog($this->res, CON_ERROR, $msg);
$this->state = STATE_DATA_ERROR;
$this->status = false;
return [];
}
}
}
}
} else {
$_templateReport[TEMPLATE_PARTIAL_INDEX_PROPERTY] = STRING_NOT_DEFINED;
}
// ttl indexes: (col => value)
if (isset($this->template->ttlIndexes) and !is_null($this->template->ttlIndexes)) {
foreach ($this->template->ttlIndexes as $column => $value) {
if (array_key_exists($column, $this->template->cacheMap)) {
$_templateReport[TEMPLATE_TTL_INDEX_PROPERTY][$column] = $value;
} else {
try {
$this->processCacheMapSearchErrors((string)$column, $consoleMessages);
} catch (TypeError $t) {
$msg = sprintf(INFO_LOC, __METHOD__, __LINE__) . ERROR_TYPE_EXCEPTION;
$this->logger->error($msg);
consoleLog($this->res, CON_ERROR, $msg);
$this->state = STATE_DATA_ERROR;
$this->status = false;
return [];
}
}
}
} else {
$_templateReport[TEMPLATE_TTL_INDEX_PROPERTY] = STRING_NOT_DEFINED;
}
// sub-collections: (subCName => [ cols ])
if (isset($this->template->subC) and !is_null($this->template->subC)) {
foreach ($this->template->subC as $indexName => $fieldList) {
foreach ($fieldList as $column) {
if (array_key_exists($column, $this->template->cacheMap)) {
$_templateReport[TEMPLATE_SUB_COLLECTION_FIELDS][$indexName][] = $this->template->cacheMap[$column];
} else {
try {
$this->processCacheMapSearchErrors((string)$column, $consoleMessages);
} catch (TypeError $t) {
$msg = sprintf(INFO_LOC, __METHOD__, __LINE__) . ERROR_TYPE_EXCEPTION;
$this->logger->error($msg);
consoleLog($this->res, CON_ERROR, $msg);
$this->state = STATE_DATA_ERROR;
$this->status = false;
return [];
}
}
}
}
} else {
$_templateReport[TEMPLATE_SUB_COLLECTION_FIELDS] = STRING_NOT_DEFINED;
}
return($_templateReport);
}
/**
* pdoExposedFieldIndexes() -- private method
*
* This method is called from the processTemplateIndexes() method once it's been determined that the current class
* is PDO and that the cacheMapping is disabled but exposed-fields are enabled.
*
* The input parameter to the method is the in-progress template report which we'll add to during processing and
* that template report will eventually be assigned to the $widget->schema member.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
*
* HISTORY:
* ========
* 09-22-17 mks CORE-562: original coding
*
*/
private function pdoExposedFieldIndexes(array $_templateReport): array
{
// build the template field list first
foreach ($this->template->fields as $key => $value) {
if (array_key_exists($key, $this->template->exposedFields)) {
$_templateReport[TEMPLATE_FIELD_LIST][$this->template->exposedFields[$key]] = $value;
}
}
$indexList = [
'indexFields', 'singleFields', 'uniqueIndexes', 'binFields'
];
$indexLabels = [
TEMPLATE_FIELD_INDEX_LIST, TEMPLATE_SINGLE_INDEXES, TEMPLATE_UNIQUE_INDEX_PROPERTY, TEMPLATE_BINARY_FIELD_LIST
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key => $value) {
if (array_key_exists($key, $this->template->exposedFields) and $key != PDO_ID) {
$_templateReport[$indexLabels[$counter]][] = $key;
}
}
}
$counter++;
}
if (isset($this->template->compoundIndexes) and !empty($this->template->compoundIndexes)) {
foreach ($this->template->compoundIndexes as $key => $subArray) {
foreach ($subArray as $indexKey => $value) {
if (array_key_exists($indexKey, $this->template->exposedFields)) {
if (!isset($_templateReport[TEMPLATE_COMPOUND_INDEXES]) or !in_array($this->template->exposedFields[$indexKey], $_templateReport[TEMPLATE_COMPOUND_INDEXES])) {
$_templateReport[TEMPLATE_COMPOUND_INDEXES][] = $this->template->exposedFields[$indexKey];
}
}
}
}
}
return($_templateReport);
}
/**
* mongoExposedFieldsIndexes() -- private method
*
* This method is called from the processTemplateIndexes() method once it's been determined that we're going to be
* building the template report for a mongo class where we have cacheMapping disabled but exposed fields enabled.
*
* In which case, the general rule is that for a class member (field) to be exposed to the client, it must be
* explicitly listed in the exposed fields array. Exposed field arrays are indexed-arrays who's keys, like
* the cacheMap array, are the "real" column names minus the three-letter table extension.
*
* The method requires one input parameter and that's the template report that will eventually become the widget
* schema member. As such, this array is returned to the calling client.
*
* We're going to build the bulk of the index containers using the array member as the loop controller - this
* has been set in the constructor for this class.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array
*
*
* HISTORY:
* ========
* 09-21-17 mks CORE-562: original coding
*
*/
private function mongoExposedFieldsIndexes(array $_templateReport): array
{
// build the template field list first
foreach ($this->template->exposedFields as $key) {
$_templateReport[TEMPLATE_FIELD_LIST][$key] = $this->template->fields[$key];
}
// generate the index lists...
// set-up the index lists and process
$indexList = [
'singleFields', 'uniqueIndexes', 'ttlIndexes', 'subC'
];
$indexLabels = [
TEMPLATE_SINGLE_INDEXES, TEMPLATE_UNIQUE_INDEX_PROPERTY,
TEMPLATE_TTL_INDEX_PROPERTY, TEMPLATE_SUB_COLLECTION_FIELDS
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key => $value) {
if (array_key_exists($key, $this->template->exposedFields) and $key != MONGO_ID) {
$_templateReport[$indexLabels[$counter]][] = $key;
}
}
}
$counter++;
}
// set-up the index lists and process (these are all indexed arrays)
$indexList = [
'indexFields', 'regexFields', 'binFields'
];
$indexLabels = [
TEMPLATE_FIELD_INDEX_LIST, TEMPLATE_REGEX_INDEXES, TEMPLATE_BINARY_FIELD_LIST
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key => $subArray) {
foreach ($subArray as $indexKey => $value) {
if (in_array($indexKey, $this->template->exposedFields) and $key != MONGO_ID) {
if (!isset($_templateReport[$indexLabels[$counter]]) or !in_array($this->template->exposedFields[$indexKey], $_templateReport[$indexLabels[$counter]])) {
$_templateReport[$indexLabels[$counter]][] = $indexKey;
}
}
}
}
}
$counter++;
}
// set-up the index lists and process (these are all indexed arrays)
$indexList = [
'indexFields', 'regexFields', 'binFields'
];
$indexLabels = [
TEMPLATE_FIELD_INDEX_LIST, TEMPLATE_REGEX_INDEXES, TEMPLATE_BINARY_FIELD_LIST
];
$counter = 0;
foreach ($indexList as $currentIndex) {
if (isset($this->template->$currentIndex) and !empty($this->template->$currentIndex)) {
foreach ($this->template->$currentIndex as $key) {
if (in_array($key, $this->template->exposedFields) and $key != MONGO_ID) {
$_templateReport[$indexLabels[$counter]][] = $key;
}
}
}
$counter++;
}
return ($_templateReport);
}
/**
* getPrivateFields() -- private method
*
* this method was scraped from the fetchSchema() method in order to reduce the code footprint.
*
* the method checks for the declaration/existence of binary fields declared in the template file and, if set,
* will then processes those fields through the cacheMap or the exposed fields member.
*
* The parameter to the method is the in-progress template report which is explicitly returned back to the
* calling client upon completion.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport
* @return array $_templateReport
*
* HISTORY:
* ========
* 09-20-17 mks CORE-562: original coding
*
*/
private function getBinaryFields(array $_templateReport): array
{
if (!empty($this->template->binFields)) {
if (empty($this->template->cacheMap) and empty($this->template->exposedFields)) {
// case 1: no cacheMap and no exposedFields list
$_templateReport[TEMPLATE_BINARY_FIELD_LIST] = $this->template->binFields;
} elseif (!empty($this->template->cacheMap)) {
// case 2: cachemap and maybe an exposedFields list
foreach ($this->template->binFields as $key) {
if (!empty($this->template->exposedFields)) {
if (in_array($key, $this->template->exposedFields)) {
$_templateReport[TEMPLATE_BINARY_FIELD_LIST][] = $key;
}
} elseif (in_array($key, $this->template->cacheMap)) {
$_templateReport[TEMPLATE_BINARY_FIELD_LIST][] = $this->template->cacheMap[$key];
}
}
} elseif (!empty($this->template->exposedFields)) {
foreach ($this->template->binFields as $key) {
if (in_array($key, $this->template->exposedFields)) {
$_templateReport[TEMPLATE_BINARY_FIELD_LIST][] = $key;
}
}
}
} else {
$_templateReport[TEMPLATE_BINARY_FIELD_LIST] = STRING_NOT_DEFINED;
}
return($_templateReport);
}
/**
* getFieldList() -- private method
*
* this method is called from the template-report builder method and is responsible for parsing the
* template cachemap and exposedFields containers to build the class field list.
*
* at this stage of the instantiation process, all relevant error-checking has already previously happened so
* we can rip through this processing.
*
* Note: method created to avoid code redundancy.
* Note: this builds the field list for the TEMPLATE REPORT -- not for the class member of the same name
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_templateReport -- call by reference parameter to continue assembling the template report
*
*
* HISTORY:
* ========
* 09-20-17 mks CORE-562: original coding (import from fetchSchema())
* 10-02-17 mks CORE-572: refactored and removed all the weird
* 03-26-18 mks CORE-852: fixed bug in exposed-field list processing
*
*/
private function getFieldList(array &$_templateReport): void
{
// get the field lists and data types
if (!isset($_templateReport[TEMPLATE_FIELD_LIST])) $_templateReport[TEMPLATE_FIELD_LIST] = array();
// if this class uses cache and we have a cacheMap, then populate the field list using the cachemap
if ($this->widget->useCache and !empty($this->widget->cacheMap)) {
// case 1: no cachemap and no exposed field list => all fields are listed
foreach ($this->widget->fieldTypes as $key => $value) {
if (array_key_exists($key, $this->widget->cacheMap)) {
$_templateReport[TEMPLATE_FIELD_LIST][$this->widget->cacheMap[$key]] = $value;
}
}
} elseif (!empty($this->widget->exposedFields)) {
// case 2: we have an exposed fields list
foreach ($this->widget->fieldTypes as $key => $value) {
if (in_array($key, $this->widget->exposedFields)) {
$_templateReport[TEMPLATE_FIELD_LIST][$key] = $value;
// $_templateReport[TEMPLATE_FIELD_LIST][$this->widget->exposedFields[$key]] = $value;
}
}
} else {
// case 3: no cache map and no exposed fields
foreach ($this->widget->fieldTypes as $key => $value) {
if ($key != (MONGO_ID . $this->widget->ext) and $key != (PDO_ID . $this->widget->ext)) {
$_templateReport[TEMPLATE_FIELD_LIST][$key] = $value;
}
}
}
}
/**
* processCacheMapSearchErrors() -- private method
*
* This method requires two input parameters:
*
* $_key -- this is the string value containing the key that was being searched-for in the cache map
* $_savedMessages -- call-by-reference parameter that maintains the current array of errors to eliminate duplicates
*
* This method takes the incoming $key value and checks to see if the value contains the mongo primary key or an
* integer value. An integer value would indicate that the key is referencing a sub-array container and is not
* necessarily an error. If either of these values are stored in key, we'll output a console message but, before
* doing so, we'll check the $_savedMessages variable to see if the value has already been stored in this current
* run of data. If so, skip adding and outputting the error message.
*
* If the error is legit, then we'll add the error message to the event messages stack. We'll only output the
* error to the logger log if the global debug switch is enabled.
*
* The method does not return a value so is declared as type void.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_key
* @param array $_savedMessages
*
*
* HISTORY:
* ========
* 10-25-19 mks DB-136: original coding
*
*/
private function processCacheMapSearchErrors(string $_key, array &$_savedMessages): void
{
if ($_key == MONGO_ID) {
// having a pkey in payload data isn't necessarily an error
$hdr = sprintf(INFO_LOC, __METHOD__, __LINE__);
$msg = $hdr . ERROR_MDB_CACHE_NO_PKEYS;
if (!in_array($msg, $_savedMessages)) {
if ($this->debug) consoleLog($this->res, CON_SYSTEM, $msg);
$_savedMessages[] = $msg;
}
} elseif (is_numeric($_key)) {
// having an integer value means we're processing a sub-array so is not error
$hdr = sprintf(INFO_LOC, __METHOD__, __LINE__);
$msg = $hdr . ERROR_MDB_CACHE_INTVAL;
if (!in_array($msg, $_savedMessages)) {
if ($this->debug) consoleLog($this->res, CON_SYSTEM, $msg);
$_savedMessages[] = $msg;
}
} else {
// this is definitely an error and should be returned in the error stack
$hdr = sprintf(INFO_LOC, __METHOD__, __LINE__);
$msg = $hdr . sprintf(ERROR_MDB_FIELD_NOT_CACHED, $_key, $this->template->collection);
if (!in_array($msg, $this->eventMessages))
$this->eventMessages[] = $msg;
// this line reduces a LOT of log spam:
if (gasConfig::$settings[CONFIG_DEBUG]) $this->logger->debug($msg);
}
}
/**
* __destruct() -- public function
*
* class destructor
*
*
* @author mike@givingassistant.org
* @version 1.0
*
*
* HISTORY:
* ========
* 06-15-17 mks original coding
*
*/
public function __destruct()
{
// As of PHP 5.3.10 destructors are not run on shutdown caused by fatal errors.
//
// destructor is registered shut-down function in constructor -- so any recovery
// efforts should go in this method.
}
}