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.
This commit is contained in:
2026-04-05 09:49:30 -07:00
commit 373ebc8c93
1284 changed files with 409372 additions and 0 deletions

View File

@@ -0,0 +1,634 @@
<?php /** @noinspection PhpUnused */
/**
* Class gatSessions -- mongo class
*
* This class is used to store user sessions (assuming that the Users table is also a mongo collection). Sessions
* are required for all communication with Namaste and must be linked to an active user, either external or internal,
* account.
*
* Questions requiring resolution:
* --------------------------------
* -- what is the hard-expiration for a user session?
* -- can a user have more than a single session open at a time?
*
* HISTORY:
* ========
* 02-03-20 mks DB-147: initial coding
* 06-01-20 mks ECI-108: support for auth token
*
*/
class gatSessions
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CLASS PROPERTIES...
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public int $version = 1; // template version; not the same as the release version
public string $service = CONFIG_DATABASE_SERVICE_TERCERO; // defines the mongo server destination
public string $schema = TEMPLATE_DB_MONGO; // defines the storage schema for the class
public string $templateClass = TEMPLATE_CLASS_SESSIONS; // defines the clear-text template class name
public string $collection = COLLECTION_MONGO_SESSIONS; // sets the collection (table) name
public ?string $whTemplate = null; // sets the WH(cool) collection name, null if not wh'd
public string $extension = COLLECTION_MONGO_SESS_EXT; // sets the extension for the collection
public bool $closedClass = false; // set to false to allow partner instantiations
public bool $setCache = true; // set to true to cache class data
public bool $setDeletes = false; // set to true to allow HARD deletes (otherwise: SOFT)
public int $setAuditing = AUDIT_NOT_ENABLED; // set to AUDIT_value constant (nondestructive = reads(yes))
public bool $setJournaling = false; // set to true to allow journaling
public bool $setUpdates = true; // set to true to allow record updates
public bool $setHistory = false; // set to true to enable detailed record history tracking
public string $setDefaultStatus = STATUS_ACTIVE; // set the default status for each record
public string $setSearchStatus = STATUS_ACTIVE; // set the default search status
public bool $setLocking = false; // set to true to enable record locking for collection
public bool $setTimers = true; // set to true to enable collection query timers
public string $setPKey = DB_TOKEN; // sets the primary key for the collection
public bool $setTokens = true; // set to true: adds the idToken field functionality
public bool $selfDestruct = false; // set to false if the class contains methods
public int $cacheTimer = 300; // number of seconds a tuple will remain in-cache
public bool $isGA = true; // set to true is this class is a Namaste internal class
public ?string $authToken = null; // if this data class is registered to a partner, you will
// need to initialize this member in the constructor (hard-coded)
// non-standard template member variables
public ?string $guid = null; // internal container for a guid value on instantiation
public string $res = 'tSES: '; // resource identifier for logging
// fields: a key-value paired array, defines the field name and the data type for each field. Prior to insertion,
// all data is validated for type and membership. Data that does not satisfy these requirements is
// silently dropped prior to insertion.
public array $fields = [
MONGO_ID => DATA_TYPE_INTEGER,
SESSION_EXPIRES => DATA_TYPE_DATETIME, // user-friendly time-stamp
SESSION_CLOSED => DATA_TYPE_STRING, // timestamp for when the session was actually closed
SESSION_DURATION => DATA_TYPE_INTEGER, // length of session in seconds
SESSION_FK_USER => DATA_TYPE_STRING, // fk-link to users.token_usr
SESSION_LEVEL => DATA_TYPE_INTEGER, // defines the session level (user, csr, etc.)
SESSION_CUSTOM_FIELD => DATA_TYPE_STRING, // user-defined KEY
SESSION_CUSTOM_VALUE => DATA_TYPE_STRING, // user-defined VALUE
SESSION_CREATED_WITH => DATA_TYPE_OBJECT, // legacy-data container for json-looking stuff
SESSION_ACTION => DATA_TYPE_STRING,
SESSION_AUTH_PROVIDER => DATA_TYPE_STRING,
DB_TOKEN => DATA_TYPE_STRING, // unique key exposed externally and is REQUIRED,
DB_EVENT_GUID => DATA_TYPE_STRING, // track-back identifier for broker/events
DB_CREATED => DATA_TYPE_INTEGER, // epoch time
DB_STATUS => DATA_TYPE_STRING, // record status
DB_ACCESSED => DATA_TYPE_INTEGER // epoch time
];
// protected fields are fields that a client is unable to modify or delete. If a client submits a query that
// updates these fields, the query will be rejected (worst case) or the directive to update/delete the field
// will be silently dropped (best case). In either way, updating or removing this fields cannot be accomplished.
//
// Minimally, this array should contain the following fields:
// -- DB_TOKEN, DB_EVENT_GUID, DB_CREATED, DB_ACCESSED
// -- the ID field (either PDO_ID or MONGO_ID)
// -- DB_WH_CREATED, DB_WH_EVENT_GUID, DB_WH_TOKEN
//
public ?array $protectedFields = [
DB_TOKEN, DB_CREATED, DB_ACCESSED, SESSION_FK_USER, SESSION_LEVEL, SESSION_EXPIRES,
SESSION_DURATION, MONGO_ID, DB_STATUS
];
// all fields that appear in any of the index declarations must appear in this list as this is the property
// that's used in the framework as an authoritative check to qualify discriminant fields as indexes.
//
// indexes are always declared with the template column name and not the cache-map column name
//
// warehouse indexes are limited to the original record's created date and the three WH fields only
//
public array $indexFields = [
MONGO_ID, DB_TOKEN, SESSION_FK_USER, DB_STATUS, DB_EVENT_GUID
];
// all index names that are explicitly declared in the indexes below must also appear in this array. If there are
// no pre-defined index names, then this field should be set to null.
//
// Note that if you're allowing mysql to generate the index names for you, and if you use a partial index (below)
// that references that randomly-generated index name, and that name does not appear in this list, then you will
// fail to load that template at run time, every time.
//
// You have been warned.
//
public ?array $indexNameList = [
'cIdxSession1'
];
// single field index declarations -- since you can have a field in more than one index
// (MONGO_ID should NEVER be listed as it's the default single-field index.)
// the format for the single-field index declaration is the same format used for all the
// index declarations:
// [ FIELD_NAME => <SORT-DIRECTION> ] where <SORT_DIR> = [ 1 | -1 ]
//
// NOTE: if you're going to declare a single column as a property, then do NOT also declare it as a single index!
//
public ?array $singleFields = [
DB_TOKEN => 1,
DB_STATUS => 1,
DB_EVENT_GUID => 1
];
// compound indexes have format of:
// [ INDEX-NAME => [ FIELD_NAME => <SORT-DIR>, ... ]]
// where INDEX-NAME is a unique string and SORT-DIR = [1|-1]
// unless it's for mongoDB -- mongoDB does not use index labels
public ?array $compoundIndexes = [
'cIdxSession1' => [ SESSION_FK_USER, DB_STATUS ]
];
// multiKey indexes are indexes on fields that are arrays (not the same as sub-collections) which indexes the
// content stored in the array based on the column names.
//
// mongo, as of 3.4, automatically creates a multi-key index on any field declared as a (sic) index that's
// an array. Meaning: we don't need to explicitly create a multi-key index on an array field if that field
// is declared as a single-key, compound, or unique index.
//
// -----------------------------------------------------------------------------------------------------------------
// NOTES: if you implicitly declare a multi-key index by using the column as a compound-index field, then you
// may, at MOST, have one array within the compound index.
//
// You may NOT declare a multi-key index as a shard key.
//
// Hashed keys may NOT be multi-key.
// -----------------------------------------------------------------------------------------------------------------
//
// In other words, if you want to apply an index to ALL of the array element then declare the column as singleField,
// or compound, or unique. This will have the multi-key index automagically applied by mongoDB.
//
// If you want to index a subset of the array, then declare the fields to be indexed by using dot notation:
//
// [ 'someIndex' => [ arrayColumnName.subField1 => 1, arrayColumnName.subField3 => -1 ... ] ]
//
// And this will apply the multi-key index property to subField1 and subField3 only.
//
// multiKey indexes are referenced by an index name in order to remove ambiguity when parsing index-properties
// against this and other indexes that may have the same field name. In other words, index-properties that will
// be applied to a multiKey index must reference the multiKey index by the index (and not the column) name.
//
// example:
// [ 'mIdx1Test' => [ ARRAY_FIELD_NAME => <1|-1>, ... ]]
//
public ?array $multiKey = null;
/*
* Valid index-type constants are:
* MONGO_INDEX_TYPE_SINGLE
* MONGO_INDEX_TYPE_COMPOUND
* MONGO_INDEX_TYPE_MULTIKEY
*
* INDEXES NOT SUPPORTED BY NAMASTE AT THIS TIME:
* ----------------------------------------------
* geoSpatial
* text
* hashed
*
*/
// =================================================================================================================
// INDEX PROPERTIES
// ----------------
// Index properties are applied to indexes. The supported properties are:
// unique, partial and ttl
// sparse is not supported because partial
//
// If a property is not in-use, then you must still declare the property as a class object but the
// value of the property will be set to null.
//
// Sparse property types are not supported in favor of partials.
//
// =================================================================================================================
// Partial Indexes are supported as of MongoDB 3.2 and replace sparse indexes. Format for declaration is the
// column name as an array key, with the value being a sub-array of a mongo operand and a value, all of which is
// associated with either an existing column name or index label.
//
// If an existing column name is used, then that field must be defined (exists) in one of the above index
// declarations for single, compound, or multikey indexes.
//
// Sparse indexes only add the row to the index if the column referenced satisfies the conditions specified
// in the query condition (expr2).
//
// Format:
// { expr1 }, { expr2 }
// Where:
// expr1 is an indexed column and the index direction. e.g.: { created_tst : 1 }
// AND
// expr2 is the keyword "partialFilterExpression : { [ query ] }
// e.g.: { partialFilterExpression : { integer_tst : { $gte : 10 }}
//
// db.myTable.createIndex({ lastName: -1, firstName : 1 }, { partialFilterExpression : { age : { $gte : 62 }})
// The above index would return a list of names (sorted DESC by last name) for people aged 62 or older.
//
//
public ?array $partialIndexes = null;
// unique indexes cause MongoDB to reject duplicate values for the indexed field. Unique indexes
// are functionally interchangeable with other mongo indexes.
// Format:
// [ < FIELD_NAME | INDEX-NAME > => <SORT_DIR>, ... ]
//
public ?array $uniqueIndexes = [
DB_TOKEN => 1, // MONGO_TOKEN should always appear
DB_EVENT_GUID => 1
];
// ttl indexes contain the column name and the time-to-live in seconds (e.g.: MONGO_TOKEN => 3600)
// ttl indexes can only be applied to fields that are MongoDB Date() (object) types, or an array that
// contains date values.
//
// If the field is an array, and there are multiple date values in the index, MongoDB uses lowest
// (i.e. earliest) date value in the array to calculate the expiration threshold. If the indexed
// field in a document is not a date or an array that holds a date value(s), the document will not expire.
//
// Format:
// [ SOME_FIELD_NAME => ExpireVal ]
//
// Example:
// [ SOME_FIELD_NAME => 86400 ] --- record will be sorted ASC and deleted after 1 day
//
public ?array $ttlIndexes = null;
// cache maps are required for namaste service classes. Even if caching is disabled for a class, a cache map is
// still required for the class. For PDO classes, the PDO_ID is never included in the mapping, nor is MONGO_ID.
public ?array $cacheMap = [
DB_TOKEN => CM_TOKEN,
DB_CREATED => CM_DATE_CREATED,
SESSION_CLOSED => CM_DATA_CLOSED,
DB_ACCESSED => CM_DATE_ACCESSED,
DB_STATUS => CM_STATUS,
DB_EVENT_GUID => CM_EVENT_GUID,
SESSION_EXPIRES => CM_SESSION_EXPIRES,
SESSION_DURATION => CM_SESSION_DURATION,
SESSION_LEVEL => CM_SESSION_LEVEL,
SESSION_FK_USER => CM_SESSION_UID,
SESSION_CUSTOM_FIELD => CM_SESSION_CUSTOM_KEY,
SESSION_CUSTOM_VALUE => CM_SESSION_CUSTOM_VAL,
SESSION_CREATED_WITH => CM_SESSION_CW,
SESSION_ACTION => CM_SESSION_ACTION,
SESSION_AUTH_PROVIDER => CM_SESSION_AP
];
/*
* if there is no cache-mapping supported for the class, and you want to limit the fields returned,
* then those fields are listed here as the associative array: $exposedFields. Only those fields,
* enumerated within this container, will be exposed to the client.
*
* NOTE: You can have caching disabled for the class and still have a cache-map -- this controls the labels
* assigned to the returned data column names exposed to the client. Schema should never be exposed.
*
* NOTE: if you do not support caching for the class and this class is one that is returned to a client,
* (some classes are limited to internal use only, like logging), then you should (at a minimum)
* exclude the primary key field (integer).
*
*
* This array is an associative array -- the key is the native column name and the value doesn't matter. The
* important thing is that the keys are the column names that you want to return back to the client.
*
* If $exposedFields is to be undefined for the class, then assign it to null.
*
*/
public ?array $exposedFields = null;
public ?array $binFields = null; // binary fields require special handling; define binary fields here
// regex fields -- within the indexFields array, which fields enable regex searches?
// this does not define an index, but rather to control when to use a regex operand in a query...
public ?array $regexFields = null;
/*
* sub-collections represent the implementation of a 1:M relationship at the record-entity level in mongoDB.
*
* A great example of a sub-collection implementation would be a parent collection called questions and
* a sub-collection called answers.
*
* sub-collections are declared as key->value pairs where each key value is, itself, an array of field names:
*
* public $subC = [
* FIELD_ONE => [
* SUB_COLLECTION_FIELD_ONE,
* SUB_COLLECTION_FIELD_TWO,
* ...
* ],
* ...
* ];
*
* Each sub-collection field should also appear in both the fields list (to define the types), and in the
* cacheMap (if used). If you're not using a cacheMap, and you're limiting the exposed fields, then each
* sub-collection field exposed must be listed in the exposed field list. (e.g.: normal rules for exposure
* for a collection are applied the same way to a sub-collection.)
*
* Note that if a sub-Collection key is not listed in either the cacheMap or the exposed field list, then
* the entire sub-collection will be invisible to the client. If you list the sub-collection key, you can
* limit the sub-collection fields that are exposed by not listing them in either the cacheMap or the
* exposed-field lists, respectively.
*
* Sub-collections are managed within Namaste to allow the sub-collection elements to be either inserted,
* or deleted (an update is a delete + insert) without changing the parent field values and, accordingly,
* are enabled via discrete class methods.
*
*/
// sub-collection fields must be declared here (need not be indexed)
public ?array $subC = [
SESSION_ACTION => [ SESSION_ACTION, SESSION_CREATED_WITH ]
];
//=================================================================================================================
// WAREHOUSE DECLARATIONS
// ----------------------
// This section handles the warehousing configuration for the class. If a data table is eligible to be ware-
// housed, then this section contains all the configuration information, including permissions, for the destination
// repository. Note that we need to support bi-directional flow for data.
//
// Terms/Definitions:
// ------------------
// HOT -- data is in production
// COOL -- data has been warehoused, maintains schema, but with indexing changes.
// COLD -- data has been warehoused but formatted to the destination schema, usually CSV.
// WARM -- indicates any data moving from COLD -> HOT
//
// Design Features:
// ----------------
// Supported
// This is a boolean value that indicates if the class supports warehousing. If this is set to false, then
// warehousing requests for the class will be rejected.
//
// Remote Support
// --------------
// This is a boolean value that indicates if the class will support a warehouse source outside of the Namaste
// framework. If this is set to false, and a user submits a request defining the data source as a remote
// repository, the request will be rejected.
//
// Automated
// This is a boolean value that indicates if the class allows automated warehousing, meaning that data will be
// warehoused once the qualifying condition has been met.
//
// Dynamic
// Boolean value that, if set to true, indicates that the class will accept dynamic requests. Otherwise, the
// warehousing operations will follow the interval schedule. Defaults to false.
//
// Interval
// This is a string value that tells the AT_micro-service how often to run automated warehousing on the data.
// D = Daily, M = 1st of every month, Q = 1st of every quarter, Y = 1st of every year
// The default setting for this value should be monthly (M).
//
// Qualifier
// This is a query string, similar to what you would provide to Namaste for a fetch operation, that establishes
// the filter/criteria for moving data to the warehouse. If Supported is set to true, this cannot be blank.
//
// Override
// Boolean value indicating if, and only for dynamic event requests, if the Qualifier can be overridden. If
// set to true, the the event request must contain a valid query filter.
//
// Delete
// This is a string value that tells Namaste what to do with the source data once successfully warehoused.
// H = hard delete, S = soft delete
// Note that this value overrides the $setDeletes setting.
//
//=================================================================================================================
public ?array $wareHouse = [
WH_SUPPORTED => false, // must be set to true for data class to support any warehousing
WH_REMOTE_SUPPORT => false, // must be set to true to import data into this class from remote source
WH_AUTOMATED => false, // must be set to true for warehousing to be automatically processed
WH_DYNAMIC => false, // must be set to true to allow non-scheduled event requests
WH_INTERVAL => 'M', // must be either D, M, Q or A, defaults to M
WH_OVERRIDE => false, // must be set to true to allow an ad-hoc query filter
WH_DELETE => 'H', // must be either H, or S. Can be reset to T via meta. Default: H
// default warehouse query to grab records where the date is LT a value and the status is active:
// the null value will be replaced with the value provided by the client in the wh request payload.
WH_QUALIFIER => [
DB_CREATED => [OPERAND_NULL => [OPERATOR_LT => [null]]],
DB_STATUS => [OPERAND_NULL => [OPERATOR_EQ => [STATUS_ACTIVE]]],
OPERAND_AND => null
]
];
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CLASS METHODS...
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* __construct() -- public method
*
* Constructor in this template not only registers the shutdown method, but also allows us to generate a custom
* GUID string during instantiation by use of the input parameters:
*
* $_getGUID - boolean, defaults to false but, if true, will generate a GUID value and store it in the class member
* $_lc - boolean, defaults to false but, if true, will generate a GUID using lower-case alpha characters
*
* If we generate a GUID on instantiation, the GUID will be stored in the class member. This allows us to both
* instantiate a session class object and a GUID value, (the most requested, post-instantiation, action), at the
* same time. All the efficient.
*
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param bool $_getGUID
* @param bool $_lc
*
* HISTORY:
* ========
* 02-03-20 mks DB-147: original coding
*
*/
public function __construct(bool $_getGUID = false, bool $_lc = false)
{
$this->authToken = NULL_TOKEN;
register_shutdown_function([$this, STRING_DESTRUCTOR]);
if ($_getGUID) $this->guid = static::getGUID($_lc);
}
/**
* buildExpireSessionPayload() -- template function
*
* This is a "hidden function" for the session template class requiring the following input parameters:
*
* $_data -- the is the request payload as received by the tercero broker
* $_errors -- call-by-reference container for error messaging
*
* The function will validate that $_data contains the $requiredArrayKeys, generating an error message if any
* are not present in the _data array and returning a null value to the calling client if so.
*
* Depending on the value for STRING_TOK_TYPE stored in $_data, we'll build the query against DB_TOKEN or the
* DB_EVENT_GUID.
*
* Then we just assemble the rest of the query and return the array to the calling client, presumably the sBroker.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param array $_data
* @param array|null $_errors
* @return array|null
*
* HISTORY:
* ========
* 10-02-20 mks DB-168: original coding
*
*/
public function buildExpireSessionPayload(array $_data, ?array &$_errors = null):?array
{
$requiredArrayKeys = [ STRING_GUID_KEY, STRING_TOK_TYPE ];
$missingKey = false;
try {
$logger = new gacErrorLogger();
// _data validation
foreach ($requiredArrayKeys as $requiredKey) {
if (!array_key_exists($requiredKey, $_data)) {
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__);
$msg = ERROR_ARRAY_KEY_404 . $requiredKey;
$_errors[] = $msg;
$logger->data($hdr . $msg);
$missingKey = true;
}
}
if ($missingKey) return null;
switch ($_data[STRING_TOK_TYPE]) {
case STRING_TOK_TYPE_EVE :
$searchDiscriminant = DB_EVENT_GUID;
break;
case STRING_TOK_TYPE_SES :
case STRING_TOK_TYPE_TOK :
$searchDiscriminant = DB_TOKEN;
break;
default :
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__);
$msg = ERROR_DATA_FIELD_NOT_MEMBER . $_data[STRING_TOK_TYPE];
$_errors[] = $msg;
$logger->data($hdr . $msg);
return null;
break;
}
$query = [$searchDiscriminant => [ OPERAND_NULL => [ OPERATOR_EQ => [$_data[STRING_GUID_KEY]]]]];
$update = [ DB_STATUS => STATUS_EXPIRED, SESSION_CLOSED => time() ];
return [STRING_QUERY_DATA => $query, STRING_UPDATE_DATA => $update];
} catch (Throwable | TypeError $t) {
$hdr = sprintf(INFO_LOC, basename(__METHOD__), __LINE__);
@handleExceptionMessaging($hdr, $t->getMessage(), $_errors, true);
return null;
}
}
/**
* buidlCloseSysEventPayload() -- public function
*
* This function requires a single input parameter:
*
* $_guid -- the session guid (foreign key value) in the system event record we're updating
*
* The method takes this information and build an update-record payload that will be returned to the adminIn
* broker to close the system-event record that recorded the original session-expiry event.
*
* The function returns a string, which is the compress json query payload to be send immediately to the broker.
*
* @author mike@givingassistant.org
* @version 1.0
*
* @param string $_guid
* @return string|null
*
* HISTORY:
* ========
* 10-23-20 mks DB-168: original coding
*
*/
public function buildCloseSysEventPayload(string $_guid):string
{
$payload = [
SYSTEM_EVENT_STATUS => STATUS_CLOSED,
DB_ACCESSED => time(),
];
$meta = [
META_TEMPLATE => TEMPLATE_CLASS_SYS_EVENTS,
META_CLIENT => CLIENT_SYSTEM,
META_DO_CACHE => 0
];
$request = [
BROKER_REQUEST => BROKER_REQUEST_UPDATE,
BROKER_DATA => [
STRING_QUERY_DATA => [ SYSTEM_EVENT_FK_SESSION_GUID => [ OPERAND_NULL => [OPERATOR_EQ => [$_guid]]]],
STRING_UPDATE_DATA => $payload
],
BROKER_META_DATA => $meta
];
return gzcompress(json_encode($request));
}
/**
* getGUID() -- public static template method
*
* This method calls the generic function method (guid()) which generates a random GUID (36-char format).
*
* The method has one input parameter:
*
* $_lc -- boolean: default set to false but, if true, converts the alpha chars in the guid string to lower-case.
*
* The method returns a string back to the calling client containing the 36-character GUID.
*
*
* @param bool $_lc - defaults to false, submit true if your want the guid's alpha chars converted to lower-case
* @return string
*
*
* HISTORY:
* ========
* 02-04-20 mks DB-147: original coding
*
*/
public static function getGUID(bool $_lc = false):string
{
return (($_lc) ? strtolower(guid()) : guid());
}
/**
* __clone() -- private function
*
* Silently disallows cloning of the object
*
* @return null
*
* HISTORY:
* ========
* 02-03-20 mks DB-147: original coding
*
* @version 1.0
*
* @author mike@givingassistant.org
*/
private function __clone()
{
return (null);
}
/**
* __destruct() -- public function
*
* As of PHP 5.3.10 destructors are not run on shutdown caused by fatal errors.
*
* The destructor is registered as a shut-down function in the constructor -- so any recovery
* efforts should go in this method.
*
* @author mike@givingassistant.org
* @version 1.0
*
* HISTORY:
* ========
* 02-03-20 mks DB-147: original coding
*
*/
public function __destruct()
{
// does nothing
}
}