示例#1
0
 private function _generate_iv()
 {
     // Initialize pseudo random generator
     // seed rand: (double)microtime()*1000000 // no more needed
     // Collect very random data.
     // Add as many "pseudo" random sources as you can find.
     // Possible sources: Memory usage, diskusage, file and directory content...
     $iv = Smart::random_number();
     $iv .= Smart::unique_entropy();
     $iv .= SmartUtils::get_visitor_tracking_uid();
     $iv .= implode("\r", (array) $_SERVER);
     $iv .= implode("\r", (array) $_COOKIES);
     return $this->_hash($iv);
 }
    /**
     * PgSQL Query :: Write Ignore - Catch Duplicate Key Violation or Foreign Key Violation Errors (This is the equivalent of MySQL's INSERT IGNORE / UPDATE IGNORE / DELETE IGNORE, but it can catch UNIQUE violations on both: INSERT / UPDATE / DELETE statements and also can catch FOREIGN KEY violations).
     * This function is intended to be used only for write type queries like: INSERT / UPDATE / DELETE which can be ignored if unique violations or foreign key violations and will return the # of affected rows or zero if an exception raised.
     * The catch of PostgreSQL exceptions is handled completely by this function so there is no need for a catch errors outside.
     *
     * IMPORTANT:
     * This function needs the pgsql notice message tracking enabled in PHP (not ignored); This must be set in php.ini (pgsql.ignore_notice = 0).
     * The internal mechanism of this function to catch UNIQUE or FOREIGN KEYS violations is that the EXCEPTIONS are catch at the PostgreSQL level in a DO block.
     * This is the best approach to handle safe UPSERT or INSERT IGNORE / UPDATE IGNORE / DELETE IGNORE like queries in high load envionments or to avoid fatal errors when a INSERT / UPDATE / DELETE violates a unique key or a foreign key with PostgreSQL.
     * This function can be used inside transactions blocks but never use this function to execute statements as: BEGIN, START TRANSACTION, COMMIT, ROLLBACK or SET statements, as the context is incompatible.
     * HINTS:
     * On PostgreSQL 9.5/later there is an alternative which can be used directly with write_data() without the need of this function as the following statement: INSERT ... ON CONFLICT DO NOTHING/UPDATE ... (as the equivalent of INSERT IGNORE / UPSERT), but the following statements are still missing (not implemented): UPDATE ... ON CONFLICT DO NOTHING / DELETE ... ON CONFLICT DO NOTHING .
     * This function will remain in the future to offer backward compatibility with PostgreSQL 8.4 ... 9.5 even if PostgreSQL at some moment will have ON CONFLICT DO implemented for all 3 INSERT / UPDATE / DELETE.
     *
     * @param STRING $queryval						:: the query
     * @param STRING $params_or_title 				:: *optional* array of parameters ($1, $2, ... $n) or query title for easy debugging
     * @param RESOURCE $y_connection				:: the connection
     * @return ARRAY 								:: [0 => 'control-message', 1 => #affected-rows]
     */
    public static function write_igdata($queryval, $params_or_title = '', $y_connection = 'DEFAULT')
    {
        //==
        $y_connection = self::check_connection($y_connection, 'WRITE-IG-DATA');
        //==
        //-- samples
        // $queryval = 'UPDATE "tablename" SET "field" = \'value\' WHERE ("id_field" = \'val1\')';
        // $queryval = 'INSERT INTO "tablename" ("desiredfield1", "desiredfield2") VALUES (\'val1\', \'val2\')';
        //--
        // ##### 'pgsql.ignore_notice' must be set to 0 in PHP.INI (checked via connect) #####
        //--
        /* PRE-CHECK (DO NOT ALLOW IN TRANSACTION BLOCKS) - No More Necessary !!, now can be safe used also in transactions as the exceptions are catch in the DO block
        	$transact_status = @pg_transaction_status($y_connection);
        	if(($transact_status === PGSQL_TRANSACTION_INTRANS) OR ($transact_status === PGSQL_TRANSACTION_INERROR)) {
        		self::error($y_connection, 'WRITE-IG-DATA', 'ERROR: Write Ignore cannot be used inside Transaction Blocks ...', $queryval, '');
        		return array('errortransact: '.'Write Ignore cannot be used inside Transaction Blocks', 0);
        	} //end if
        	*/
        //--
        //--
        $time_start = 0;
        if ((string) SMART_FRAMEWORK_DEBUG_MODE == 'yes') {
            $time_start = microtime(true);
        }
        //end if
        //--
        //--
        $use_param_query = false;
        if (strpos((string) $queryval, '$') !== false and Smart::array_size($params_or_title) > 0) {
            $use_param_query = true;
        }
        //end if
        //--
        if ($use_param_query === true) {
            $the_query_title = '';
        } else {
            $the_query_title = (string) $params_or_title;
        }
        //end if else
        //--
        //--
        /* At the moment, in PgSQL 9.5 only works ON CONFLICT DO NOTHING for INSERT (for UPDATE statements fails ...)
        	if(version_compare(self::check_server_version($y_connection), '9.6') >= 0) {
        		//--
        		$xmode = 'affected';
        		$vmode = '[ON CONFLICT DO NOTHING]';
        		//--
        		$prep_query = (string) $queryval.' ON CONFLICT DO NOTHING'; // fix for PostgreSQL >= 9.5 :: RETURNING *
        		//--
        		if($use_param_query === true) {
        			$result = @pg_query_params($y_connection, $prep_query, $params_or_title); // NOTICE: parameters are only allowed in ONE command not combined statements
        		} else {
        			$result = @pg_query($y_connection, $prep_query);
        		} //end if else
        		//--
        	} else {
        	*/
        //--
        if ((string) ini_get('pgsql.ignore_notice') != '0') {
            // {{{SYNC-PGSQL-NOTIF-CHECK}}}
            self::error($y_connection, 'WRITE-IG-DATA', 'Check PgSQL PHP.INI Settings', 'SETTINGS: PostgreSQL Notifications need to be ENABLED in PHP.INI !', 'SET in PHP.INI this: pgsql.ignore_notice = 0');
            return array('errorinits: PostgreSQL Notifications need to be ENABLED in PHP.INI', 0);
        }
        //end if
        //--
        $xmode = 'notice';
        $vmode = '[Catch EXCEPTION on Violations for: Unique / Foreign Key]';
        //--
        if ($use_param_query === true) {
            $queryval = (string) self::prepare_param_query((string) $queryval, (array) $params_or_title, $y_connection);
        }
        //end if
        //--
        $unique_id = 'WrIgData_PgSQL_' . Smart::uuid_10_seq() . '_' . Smart::uuid_10_str() . '_' . Smart::uuid_10_num() . '_' . sha1(SmartUtils::client_ident_private_key()) . '_' . sha1(SmartUtils::get_visitor_tracking_uid() . ':' . Smart::uuid_36('pgsql-write-ig') . ':' . Smart::uuid_45('pgsql-write-ig')) . '_Func';
        // this must be a unique that cannot guess to avoid dollar escaping injections
        //--
        $prep_query = (string) '
	DO LANGUAGE plpgsql
	$' . $unique_id . '$
	DECLARE affected_rows BIGINT;
	BEGIN
		-- do the query an safe catch exceptions (unique key, foreign key)
			affected_rows := 0;
	' . "\t\t" . trim(rtrim($queryval, ';')) . ';' . '
			GET DIAGNOSTICS affected_rows = ROW_COUNT;
			RAISE NOTICE \'SMART-FRAMEWORK-PGSQL-NOTICE: AFFECTED ROWS #%\', affected_rows;
			RETURN;
		EXCEPTION
			WHEN unique_violation THEN RAISE NOTICE \'SMART-FRAMEWORK-PGSQL-NOTICE: AFFECTED ROWS #0\';
			WHEN foreign_key_violation THEN RAISE NOTICE \'SMART-FRAMEWORK-PGSQL-NOTICE: AFFECTED ROWS #0\'; -- this is a different behaviour than ON CONFLICT DO NOTHING in PgSQL 9.5 or later versions ...
	END
	$' . $unique_id . '$;
	';
        //--
        $result = @pg_query($y_connection, $prep_query);
        //--
        //} //end if else
        //--
        //--
        $error = '';
        $affected = 0;
        if (!$result) {
            $error = 'Query FAILED:' . "\n" . @pg_last_error($y_connection);
        } else {
            //if((string)$xmode == 'notice') {
            $affected = (int) self::get_notice_smart_affected_rows(@pg_last_notice($y_connection));
            // in this case we can only monitor affected rows via a custom notice (the only possible way to return something from anonymous pgsql functions ...)
            //} else { // affected
            //	$affected = @pg_affected_rows($result); // for PostgreSQL >= 9.5
            //} //end if else
        }
        //end if else
        //--
        //--
        $time_end = 0;
        if ((string) SMART_FRAMEWORK_DEBUG_MODE == 'yes') {
            $time_end = (double) (microtime(true) - (double) $time_start);
        }
        //end if
        //--
        //--
        if ((string) SMART_FRAMEWORK_DEBUG_MODE == 'yes') {
            //--
            SmartFrameworkRegistry::setDebugMsg('db', 'pgsql|total-queries', 1, '+');
            //--
            SmartFrameworkRegistry::setDebugMsg('db', 'pgsql|total-time', $time_end, '+');
            //--
            $dbg_query_params = '';
            //--
            if (strtoupper(substr(trim($queryval), 0, 5)) == 'BEGIN' or strtoupper(substr(trim($queryval), 0, 17)) == 'START TRANSACTION' or strtoupper(substr(trim($queryval), 0, 6)) == 'COMMIT' or strtoupper(substr(trim($queryval), 0, 8)) == 'ROLLBACK') {
                // ERROR
                self::error($y_connection, 'WRITE-IG-DATA ' . $vmode, 'ERROR: This function cannot handle TRANSACTION Specific Statements ...', $queryval, $the_query_title);
                return array('errorsqlstatement: ' . 'This function cannot handle TRANSACTION Specific Statements', 0);
            } elseif (strtoupper(substr(trim($queryval), 0, 4)) == 'SET ') {
                // ERROR
                self::error($y_connection, 'WRITE-IG-DATA ' . $vmode, 'ERROR: This function cannot handle SET Statements ...', $queryval, $the_query_title);
                return array('errorsqlstatement: ' . 'This function cannot handle SET Statements', 0);
            } else {
                SmartFrameworkRegistry::setDebugMsg('db', 'pgsql|log', ['type' => 'write', 'data' => 'WRITE / IGNORE ' . $vmode . ' :: ' . $the_query_title, 'query' => $queryval, 'params' => $dbg_query_params, 'rows' => $affected, 'time' => Smart::format_number_dec($time_end, 9, '.', ''), 'connection' => (string) $y_connection]);
            }
            //end if else
            //--
        }
        //end if
        //--
        //--
        if (strlen($error) > 0) {
            //--
            $message = 'errorsqlwriteoperation: ' . $error;
            //--
            self::error($y_connection, 'WRITE-IG-DATA ' . $vmode, $error, $queryval, $the_query_title);
            return array($message, 0);
            //--
        } else {
            //--
            $record = @pg_fetch_row($result);
            //--
            $message = 'oksqlwriteoperation';
            // this can be extended to detect extra notices
            //--
        }
        //end else
        //--
        //--
        if (is_resource($result)) {
            // check in case of error
            @pg_free_result($result);
        }
        //end if
        //--
        //--
        return array($message, Smart::format_number_int($affected, '+'));
        //--
    }