338 lines
7.3 KiB
PHP
338 lines
7.3 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Base Custom Database Class.
|
||
|
*
|
||
|
* @package Database
|
||
|
* @subpackage Base
|
||
|
* @copyright Copyright (c) 2020
|
||
|
* @license https://opensource.org/licenses/gpl-2.0.php GNU Public License
|
||
|
* @since 1.0.0
|
||
|
*/
|
||
|
namespace EDD\Database;
|
||
|
|
||
|
// Exit if accessed directly
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* The base class that all other database base classes extend.
|
||
|
*
|
||
|
* This class attempts to provide some universal immutability to all other
|
||
|
* classes that extend it, starting with a magic getter, but likely expanding
|
||
|
* into a magic call handler and others.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*/
|
||
|
class Base {
|
||
|
|
||
|
/**
|
||
|
* The name of the PHP global that contains the primary database interface.
|
||
|
*
|
||
|
* For example, WordPress traditionally uses 'wpdb', but other applications
|
||
|
* may use something else, or you may be doing something really cool that
|
||
|
* requires a custom interface.
|
||
|
*
|
||
|
* A future version of this utility may abstract this out entirely, so
|
||
|
* custom calls to the get_db() should be avoided if at all possible.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $db_global = 'wpdb';
|
||
|
|
||
|
/** Global Properties *****************************************************/
|
||
|
|
||
|
/**
|
||
|
* Global prefix used for tables/hooks/cache-groups/etc...
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $prefix = 'edd';
|
||
|
|
||
|
/**
|
||
|
* The last database error, if any.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
* @var mixed
|
||
|
*/
|
||
|
protected $last_error = false;
|
||
|
|
||
|
/** Public ****************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Magic isset'ter for immutability.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @param string $key
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function __isset( $key = '' ) {
|
||
|
|
||
|
// No more uppercase ID properties ever
|
||
|
if ( 'ID' === $key ) {
|
||
|
$key = 'id';
|
||
|
}
|
||
|
|
||
|
// Class method to try and call
|
||
|
$method = "get_{$key}";
|
||
|
|
||
|
// Return property if exists
|
||
|
if ( method_exists( $this, $method ) ) {
|
||
|
return true;
|
||
|
|
||
|
// Return get method results if exists
|
||
|
} elseif ( property_exists( $this, $key ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Return false if not exists
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Magic getter for immutability.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @param string $key
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function __get( $key = '' ) {
|
||
|
|
||
|
// No more uppercase ID properties ever
|
||
|
if ( 'ID' === $key ) {
|
||
|
$key = 'id';
|
||
|
}
|
||
|
|
||
|
// Class method to try and call
|
||
|
$method = "get_{$key}";
|
||
|
|
||
|
// Return property if exists
|
||
|
if ( method_exists( $this, $method ) ) {
|
||
|
return call_user_func( array( $this, $method ) );
|
||
|
|
||
|
// Return get method results if exists
|
||
|
} elseif ( property_exists( $this, $key ) ) {
|
||
|
return $this->{$key};
|
||
|
}
|
||
|
|
||
|
// Return null if not exists
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts the given object to an array.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @return array Array version of the given object.
|
||
|
*/
|
||
|
public function to_array() {
|
||
|
return get_object_vars( $this );
|
||
|
}
|
||
|
|
||
|
/** Protected *************************************************************/
|
||
|
|
||
|
/**
|
||
|
* Maybe append the prefix to string.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @param string $string
|
||
|
* @param string $sep
|
||
|
* @return string
|
||
|
*/
|
||
|
protected function apply_prefix( $string = '', $sep = '_' ) {
|
||
|
return ! empty( $this->prefix )
|
||
|
? "{$this->prefix}{$sep}{$string}"
|
||
|
: $string;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the first letters of a string of words with a separator.
|
||
|
*
|
||
|
* Used primarily to guess at table aliases when none is manually set.
|
||
|
*
|
||
|
* Applies the following formatting to a string:
|
||
|
* - Trim whitespace
|
||
|
* - No accents
|
||
|
* - No trailing underscores
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @param string $string
|
||
|
* @param string $sep
|
||
|
* @return string
|
||
|
*/
|
||
|
protected function first_letters( $string = '', $sep = '_' ) {
|
||
|
|
||
|
// Set empty default return value
|
||
|
$retval = '';
|
||
|
|
||
|
// Bail if empty or not a string
|
||
|
if ( empty( $string ) || ! is_string( $string ) ) {
|
||
|
return $retval;
|
||
|
}
|
||
|
|
||
|
// Trim spaces off the ends
|
||
|
$unspace = trim( $string );
|
||
|
$accents = remove_accents( $unspace );
|
||
|
$lower = strtolower( $accents );
|
||
|
$parts = explode( $sep, $lower );
|
||
|
|
||
|
// Loop through parts and concatenate the first letters together
|
||
|
foreach ( $parts as $part ) {
|
||
|
$retval .= substr( $part, 0, 1 );
|
||
|
}
|
||
|
|
||
|
// Return the result
|
||
|
return $retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sanitize a table name string.
|
||
|
*
|
||
|
* Used to make sure that a table name value meets MySQL expectations.
|
||
|
*
|
||
|
* Applies the following formatting to a string:
|
||
|
* - Trim whitespace
|
||
|
* - No accents
|
||
|
* - No special characters
|
||
|
* - No hyphens
|
||
|
* - No double underscores
|
||
|
* - No trailing underscores
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @param string $name The name of the database table
|
||
|
*
|
||
|
* @return string Sanitized database table name
|
||
|
*/
|
||
|
protected function sanitize_table_name( $name = '' ) {
|
||
|
|
||
|
// Bail if empty or not a string
|
||
|
if ( empty( $name ) || ! is_string( $name ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Trim spaces off the ends
|
||
|
$unspace = trim( $name );
|
||
|
|
||
|
// Only non-accented table names (avoid truncation)
|
||
|
$accents = remove_accents( $unspace );
|
||
|
|
||
|
// Only lowercase characters, hyphens, and dashes (avoid index corruption)
|
||
|
$lower = sanitize_key( $accents );
|
||
|
|
||
|
// Replace hyphens with single underscores
|
||
|
$under = str_replace( '-', '_', $lower );
|
||
|
|
||
|
// Single underscores only
|
||
|
$single = str_replace( '__', '_', $under );
|
||
|
|
||
|
// Remove trailing underscores
|
||
|
$clean = trim( $single, '_' );
|
||
|
|
||
|
// Bail if table name was garbaged
|
||
|
if ( empty( $clean ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Return the cleaned table name
|
||
|
return $clean;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set class variables from arguments.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
* @param array $args
|
||
|
*/
|
||
|
protected function set_vars( $args = array() ) {
|
||
|
|
||
|
// Bail if empty or not an array
|
||
|
if ( empty( $args ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Cast to an array
|
||
|
if ( ! is_array( $args ) ) {
|
||
|
$args = (array) $args;
|
||
|
}
|
||
|
|
||
|
// Set all properties
|
||
|
foreach ( $args as $key => $value ) {
|
||
|
$this->{$key} = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the global database interface.
|
||
|
*
|
||
|
* See: https://core.trac.wordpress.org/ticket/31556
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @return \wpdb Database interface, or False if not set
|
||
|
*/
|
||
|
protected function get_db() {
|
||
|
|
||
|
// Default database return value (might change)
|
||
|
$retval = false;
|
||
|
|
||
|
// Look for a commonly used global database interface
|
||
|
if ( isset( $GLOBALS[ $this->db_global ] ) ) {
|
||
|
$retval = $GLOBALS[ $this->db_global ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Developer note:
|
||
|
*
|
||
|
* It should be impossible for a database table to be interacted with
|
||
|
* before the primary database interface it is setup.
|
||
|
*
|
||
|
* However, because applications are complicated, it is unsafe to assume
|
||
|
* anything, so this silently returns false instead of halting everything.
|
||
|
*
|
||
|
* If you are here because this method is returning false for you, that
|
||
|
* means the database table is being invoked too early in the lifecycle
|
||
|
* of the application.
|
||
|
*
|
||
|
* In WordPress, that means before the $wpdb global is created; in other
|
||
|
* environments, you will need to adjust accordingly.
|
||
|
*/
|
||
|
|
||
|
// Return the database interface
|
||
|
return $retval;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if an operation succeeded.
|
||
|
*
|
||
|
* @since 1.0.0
|
||
|
*
|
||
|
* @param mixed $result
|
||
|
* @return bool
|
||
|
*/
|
||
|
protected function is_success( $result = false ) {
|
||
|
|
||
|
// Bail if no row exists
|
||
|
if ( empty( $result ) ) {
|
||
|
$retval = false;
|
||
|
|
||
|
// Bail if an error occurred
|
||
|
} elseif ( is_wp_error( $result ) ) {
|
||
|
$this->last_error = $result;
|
||
|
$retval = false;
|
||
|
|
||
|
// No errors
|
||
|
} else {
|
||
|
$retval = true;
|
||
|
}
|
||
|
|
||
|
// Return the result
|
||
|
return (bool) $retval;
|
||
|
}
|
||
|
}
|