$v) { $obj->{$k}($v); } $obj->existsInDB = true; return $obj; } /** * Duplicates an object. * * @param array $field_exceptions Fields to skip. * @param string $class_str Class name. * * @return object */ public function duplicate( array $field_exceptions=[], string $class_str=__CLASS__ ) { $keys = array_keys($this->toArray()); $new = new $class_str(); foreach ($keys as $k) { if (in_array($k, $field_exceptions) === false) { $new->$k($this->$k()); } } return $new; } /** * Defines a generic constructor to extract information of the object. * * @param string $table Table. * @param array|null $filters Filters, for instance ['id' => $id]. * @param string|null $enterprise_class Enterprise class name. * @param boolean $cache Use cache or not. * * @throws \Exception On error. */ public function __construct( string $table, ?array $filters=null, ?string $enterprise_class=null, bool $cache=true ) { if (empty($table) === true) { throw new \Exception( get_class($this).' error, table name is not defined' ); } $this->table = $table; if (is_array($filters) === true) { // New one. $this->primaryKeys = array_keys($filters); $data = \db_get_row_filter( $this->table, $filters, false, 'AND', false, $cache ); if ($data === false) { throw new \Exception( get_class($this).' error, entity not found' ); } // Map fields. foreach ($data as $k => $v) { $this->fields[$k] = $v; } // Mark as existing object. $this->existsInDB = true; } else { // Empty one. $data = \db_get_all_rows_sql( sprintf( 'SHOW COLUMNS FROM %s', $this->table ) ); foreach ($data as $row) { $this->fields[$row['Field']] = null; } // Mark as virtual object. $this->existsInDB = false; } if (\enterprise_installed() === true && $enterprise_class !== null ) { $this->enterprise = new $enterprise_class($this); } } /** * Dynamically call methods in this object. * * To dynamically switch between community methods and prioritize * enterprise ones, define method visibility as 'protected' in both * classes. * * For instance, in following situation: * protected PandoraFMS\Agent::test() * protected PandoraFMS\Enterprise\Agent::test() * * If enterprise is available, then PandoraFMS\Enterprise\Agent::test() * will be executed, community method otherwise. * * @param string $methodName Name of target method or attribute. * @param array $params Arguments for target method. * * @return mixed Return of method. * @throws \Exception On error. */ public function __call(string $methodName, ?array $params=null) { // Enterprise capabilities. // Prioritize enterprise written methods over dynamic fields. if (\enterprise_installed() === true && $this->enterprise !== null && method_exists($this->enterprise, $methodName) === true ) { return $this->enterprise->$methodName(...$params); } if (method_exists($this, $methodName) === false) { if (array_key_exists($methodName, $this->fields) === true) { if (empty($params) === true) { return $this->fields[$methodName]; } else { $this->fields[$methodName] = $params[0]; } return null; } throw new \Exception( get_class($this).' error, method '.$methodName.' does not exist' ); } // Do not return nor throw exceptions after this point, allow php // default __call behaviour to continue working with object method // defined. // If you're receiving NULL as result of the method invocation, ensure // it is not private, take in mind this method will mask any access // level error or notification since it is public and has limited access // to the object (public|protected). } /** * Returns current object as array. * * @return array Of fields. */ public function toArray() { return $this->fields; } /** * Connects to current nodeId target. * If no nodeId is defined, then returns without doing anything. * * @return void * @throws \Exception On error. */ public function connectNode() { if ($this->nodeId === null) { return; } \enterprise_include_once('include/functions_metaconsole.php'); $r = \enterprise_hook( 'metaconsole_connect', [ null, $this->nodeId, ] ); if ($r !== NOERR) { throw new \Exception( __('Cannot connect to node %d', $this->nodeId) ); } $this->connected = true; } /** * Restore connection after connectNode. * * @return void */ public function restoreConnection() { if ($this->connected === true) { \enterprise_include_once('include/functions_metaconsole.php'); \enterprise_hook('metaconsole_restore_db'); } } /** * Saves current object definition to database. * * @return boolean Success or not. * @throws \Exception On error. */ public function save() { $updates = $this->fields; // Clean null fields. foreach ($updates as $k => $v) { if ($v === null) { unset($updates[$k]); } } if ($this->existsInDB === true) { // Update. $where = []; foreach ($this->primaryKeys as $key) { $where[$key] = $this->fields[$key]; } if (empty($where) === true) { throw new \Exception( __METHOD__.' error: Cannot identify object' ); } $rs = \db_process_sql_update( $this->table, $updates, $where ); if ($rs === false) { global $config; throw new \Exception( __METHOD__.' error: '.$config['dbconnection']->error ); } } else { // New register. $rs = \db_process_sql_insert( $this->table, $updates ); if ($rs === false) { global $config; throw new \Exception( __METHOD__.' error: '.$config['dbconnection']->error ); } $this->existsInDB = true; } return true; } /** * Remove this entity. * * @return void * @throws \Exception If no primary keys are defined. */ public function delete() { if ($this->existsInDB === true) { $where = []; foreach ($this->primaryKeys as $key) { $where[$key] = $this->fields[$key]; } if (empty($where) === true) { throw new \Exception( __METHOD__.' error: Cannot identify object on deletion' ); } \db_process_sql_delete( $this->table, $where ); } } }