966 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			966 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Base Custom Database Table Column Class.
 | |
|  *
 | |
|  * @package     Database
 | |
|  * @subpackage  Column
 | |
|  * @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;
 | |
| 
 | |
| /**
 | |
|  * Base class used for each column for a custom table.
 | |
|  *
 | |
|  * @since 1.0.0
 | |
|  *
 | |
|  * @see Column::__construct() for accepted arguments.
 | |
|  */
 | |
| class Column extends Base {
 | |
| 
 | |
| 	/** Table Attributes ******************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Name for the database column.
 | |
| 	 *
 | |
| 	 * Required. Must contain lowercase alphabetical characters only. Use of any
 | |
| 	 * other character (number, ascii, unicode, emoji, etc...) will result in
 | |
| 	 * fatal application errors.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $name = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * Type of database column.
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/data-types.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $type = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * Length of database column.
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/storage-requirements.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $length = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is integer unsigned?
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/numeric-type-overview.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $unsigned = true;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is integer filled with zeroes?
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/numeric-type-overview.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $zerofill = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is data in a binary format?
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/binary-varbinary.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $binary = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is null an allowed value?
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/data-type-defaults.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $allow_null = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Typically empty/null, or date value.
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/data-type-defaults.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $default = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * auto_increment, etc...
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/en/data-type-defaults.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $extra = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * Typically inherited from the database interface (wpdb).
 | |
| 	 *
 | |
| 	 * By default, this will use the globally available database encoding. You
 | |
| 	 * most likely do not want to change this; if you do, you already know what
 | |
| 	 * to do.
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/mysql/en/charset-column.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $encoding = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * Typically inherited from the database interface (wpdb).
 | |
| 	 *
 | |
| 	 * By default, this will use the globally available database collation. You
 | |
| 	 * most likely do not want to change this; if you do, you already know what
 | |
| 	 * to do.
 | |
| 	 *
 | |
| 	 * See: https://dev.mysql.com/doc/mysql/en/charset-column.html
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $collation = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * Typically empty; probably ignore.
 | |
| 	 *
 | |
| 	 * By default, columns do not have comments. This is unused by any other
 | |
| 	 * relative code, but you can include less than 1024 characters here.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $comment = '';
 | |
| 
 | |
| 	/** Special Attributes ****************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Is this the primary column?
 | |
| 	 *
 | |
| 	 * By default, columns are not the primary column. This is used by the Query
 | |
| 	 * class for several critical functions, including (but not limited to) the
 | |
| 	 * cache key, meta-key relationships, auto-incrementing, etc...
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $primary = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is this the column used as a created date?
 | |
| 	 *
 | |
| 	 * By default, columns do not represent the date a value was first entered.
 | |
| 	 * This is used by the Query class to set its value automatically to the
 | |
| 	 * current datetime value immediately before insert.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $created = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is this the column used as a modified date?
 | |
| 	 *
 | |
| 	 * By default, columns do not represent the date a value was last changed.
 | |
| 	 * This is used by the Query class to update its value automatically to the
 | |
| 	 * current datetime value immediately before insert|update.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $modified = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is this the column used as a unique universal identifier?
 | |
| 	 *
 | |
| 	 * By default, columns are not UUIDs. This is used by the Query class to
 | |
| 	 * generate a unique string that can be used to identify a row in a database
 | |
| 	 * table, typically in such a way that is unrelated to the row data itself.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $uuid = false;
 | |
| 
 | |
| 	/** Query Attributes ******************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * What is the string-replace pattern?
 | |
| 	 *
 | |
| 	 * By default, column patterns will be guessed based on their type. Set this
 | |
| 	 * manually to `%s|%d|%f` only if you are doing something weird, or are
 | |
| 	 * explicitly storing numeric values in text-based column types.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $pattern = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * Is this column searchable?
 | |
| 	 *
 | |
| 	 * By default, columns are not searchable. When `true`, the Query class will
 | |
| 	 * add this column to the results of search queries.
 | |
| 	 *
 | |
| 	 * Avoid setting to `true` on large blobs of text, unless you've optimized
 | |
| 	 * your database server to accommodate these kinds of queries.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $searchable = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is this column a date?
 | |
| 	 *
 | |
| 	 * By default, columns do not support date queries. When `true`, the Query
 | |
| 	 * class will accept complex statements to help narrow results down to
 | |
| 	 * specific periods of time for values in this column.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $date_query = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is this column used in orderby?
 | |
| 	 *
 | |
| 	 * By default, columns are not sortable. This ensures that the database
 | |
| 	 * table does not perform costly operations on unindexed columns or columns
 | |
| 	 * of an inefficient type.
 | |
| 	 *
 | |
| 	 * You can safely turn this on for most numeric columns, indexed columns,
 | |
| 	 * and text columns with intentionally limited lengths.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $sortable = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is __in supported?
 | |
| 	 *
 | |
| 	 * By default, columns support being queried using an `IN` statement. This
 | |
| 	 * allows the Query class to retrieve rows that match your array of values.
 | |
| 	 *
 | |
| 	 * Consider setting this to `false` for longer text columns.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $in = true;
 | |
| 
 | |
| 	/**
 | |
| 	 * Is __not_in supported?
 | |
| 	 *
 | |
| 	 * By default, columns support being queried using a `NOT IN` statement.
 | |
| 	 * This allows the Query class to retrieve rows that do not match your array
 | |
| 	 * of values.
 | |
| 	 *
 | |
| 	 * Consider setting this to `false` for longer text columns.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $not_in = true;
 | |
| 
 | |
| 	/** Cache Attributes ******************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Does this column have its own cache key?
 | |
| 	 *
 | |
| 	 * By default, only primary columns are used as cache keys. If this column
 | |
| 	 * is unique, or is frequently used to get database results, you may want to
 | |
| 	 * consider setting this to true.
 | |
| 	 *
 | |
| 	 * Use in conjunction with a database index for speedy queries.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $cache_key = false;
 | |
| 
 | |
| 	/** Action Attributes *****************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Does this column fire a transition action when it's value changes?
 | |
| 	 *
 | |
| 	 * By default, columns do not fire transition actions. In some cases, it may
 | |
| 	 * be desirable to know when a database value changes, and what the old and
 | |
| 	 * new values are when that happens.
 | |
| 	 *
 | |
| 	 * The Query class is responsible for triggering the event action.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   bool
 | |
| 	 */
 | |
| 	public $transition = false;
 | |
| 
 | |
| 	/** Callback Attributes ***************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Maybe validate this data before it is written to the database.
 | |
| 	 *
 | |
| 	 * By default, column data is validated based on the type of column that it
 | |
| 	 * is. You can set this to a callback function of your choice to override
 | |
| 	 * the default validation behavior.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   string
 | |
| 	 */
 | |
| 	public $validate = '';
 | |
| 
 | |
| 	/**
 | |
| 	 * Array of capabilities used to interface with this column.
 | |
| 	 *
 | |
| 	 * These are used by the Query class to allow and disallow CRUD access to
 | |
| 	 * column data, typically based on roles or capabilities.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   array
 | |
| 	 */
 | |
| 	public $caps = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Array of possible aliases this column can be referred to as.
 | |
| 	 *
 | |
| 	 * These are used by the Query class to allow for columns to be renamed
 | |
| 	 * without requiring complex architectural backwards compatibility support.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   array
 | |
| 	 */
 | |
| 	public $aliases = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Array of possible relationships this column has with columns in other
 | |
| 	 * database tables.
 | |
| 	 *
 | |
| 	 * These are typically unenforced foreign keys, and are used by the Query
 | |
| 	 * class to help prime related items.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @var   array
 | |
| 	 */
 | |
| 	public $relationships = array();
 | |
| 
 | |
| 	/** Methods ***************************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Sets up the order query, based on the query vars passed.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 *
 | |
| 	 * @param string|array $args {
 | |
| 	 *     Optional. Array or query string of order query parameters. Default empty.
 | |
| 	 *
 | |
| 	 *     @type string   $name           Name of database column
 | |
| 	 *     @type string   $type           Type of database column
 | |
| 	 *     @type int      $length         Length of database column
 | |
| 	 *     @type bool     $unsigned       Is integer unsigned?
 | |
| 	 *     @type bool     $zerofill       Is integer filled with zeroes?
 | |
| 	 *     @type bool     $binary         Is data in a binary format?
 | |
| 	 *     @type bool     $allow_null     Is null an allowed value?
 | |
| 	 *     @type mixed    $default        Typically empty/null, or date value
 | |
| 	 *     @type string   $extra          auto_increment, etc...
 | |
| 	 *     @type string   $encoding       Typically inherited from wpdb
 | |
| 	 *     @type string   $collation      Typically inherited from wpdb
 | |
| 	 *     @type string   $comment        Typically empty
 | |
| 	 *     @type bool     $pattern        What is the string-replace pattern?
 | |
| 	 *     @type bool     $primary        Is this the primary column?
 | |
| 	 *     @type bool     $created        Is this the column used as a created date?
 | |
| 	 *     @type bool     $modified       Is this the column used as a modified date?
 | |
| 	 *     @type bool     $uuid           Is this the column used as a universally unique identifier?
 | |
| 	 *     @type bool     $searchable     Is this column searchable?
 | |
| 	 *     @type bool     $sortable       Is this column used in orderby?
 | |
| 	 *     @type bool     $date_query     Is this column a datetime?
 | |
| 	 *     @type bool     $in             Is __in supported?
 | |
| 	 *     @type bool     $not_in         Is __not_in supported?
 | |
| 	 *     @type bool     $cache_key      Is this column queried independently?
 | |
| 	 *     @type bool     $transition     Does this column transition between changes?
 | |
| 	 *     @type string   $validate       A callback function used to validate on save.
 | |
| 	 *     @type array    $caps           Array of capabilities to check.
 | |
| 	 *     @type array    $aliases        Array of possible column name aliases.
 | |
| 	 *     @type array    $relationships  Array of columns in other tables this column relates to.
 | |
| 	 * }
 | |
| 	 */
 | |
| 	public function __construct( $args = array() ) {
 | |
| 
 | |
| 		// Parse arguments
 | |
| 		$r = $this->parse_args( $args );
 | |
| 
 | |
| 		// Maybe set variables from arguments
 | |
| 		if ( ! empty( $r ) ) {
 | |
| 			$this->set_vars( $r );
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/** Argument Handlers *****************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Parse column arguments
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param array $args Default empty array.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function parse_args( $args = array() ) {
 | |
| 
 | |
| 		// Parse arguments
 | |
| 		$r = wp_parse_args( $args, array(
 | |
| 
 | |
| 			// Table
 | |
| 			'name'       => '',
 | |
| 			'type'       => '',
 | |
| 			'length'     => '',
 | |
| 			'unsigned'   => false,
 | |
| 			'zerofill'   => false,
 | |
| 			'binary'     => false,
 | |
| 			'allow_null' => false,
 | |
| 			'default'    => '',
 | |
| 			'extra'      => '',
 | |
| 			'encoding'   => $this->get_db()->charset,
 | |
| 			'collation'  => $this->get_db()->collate,
 | |
| 			'comment'    => '',
 | |
| 
 | |
| 			// Query
 | |
| 			'pattern'    => false,
 | |
| 			'searchable' => false,
 | |
| 			'sortable'   => false,
 | |
| 			'date_query' => false,
 | |
| 			'transition' => false,
 | |
| 			'in'         => true,
 | |
| 			'not_in'     => true,
 | |
| 
 | |
| 			// Special
 | |
| 			'primary'    => false,
 | |
| 			'created'    => false,
 | |
| 			'modified'   => false,
 | |
| 			'uuid'       => false,
 | |
| 
 | |
| 			// Cache
 | |
| 			'cache_key'  => false,
 | |
| 
 | |
| 			// Validation
 | |
| 			'validate'   => '',
 | |
| 
 | |
| 			// Capabilities
 | |
| 			'caps'          => array(),
 | |
| 
 | |
| 			// Backwards Compatibility
 | |
| 			'aliases'       => array(),
 | |
| 
 | |
| 			// Column Relationships
 | |
| 			'relationships' => array()
 | |
| 		) );
 | |
| 
 | |
| 		// Force some arguments for special column types
 | |
| 		$r = $this->special_args( $r );
 | |
| 
 | |
| 		// Set the args before they are sanitized
 | |
| 		$this->set_vars( $r );
 | |
| 
 | |
| 		// Return array
 | |
| 		return $this->validate_args( $r );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Validate arguments after they are parsed.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param array $args Default empty array.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function validate_args( $args = array() ) {
 | |
| 
 | |
| 		// Sanitization callbacks
 | |
| 		$callbacks = array(
 | |
| 			'name'          => 'sanitize_key',
 | |
| 			'type'          => 'strtoupper',
 | |
| 			'length'        => 'intval',
 | |
| 			'unsigned'      => 'wp_validate_boolean',
 | |
| 			'zerofill'      => 'wp_validate_boolean',
 | |
| 			'binary'        => 'wp_validate_boolean',
 | |
| 			'allow_null'    => 'wp_validate_boolean',
 | |
| 			'default'       => array( $this, 'sanitize_default' ),
 | |
| 			'extra'         => 'wp_kses_data',
 | |
| 			'encoding'      => 'wp_kses_data',
 | |
| 			'collation'     => 'wp_kses_data',
 | |
| 			'comment'       => 'wp_kses_data',
 | |
| 
 | |
| 			'primary'       => 'wp_validate_boolean',
 | |
| 			'created'       => 'wp_validate_boolean',
 | |
| 			'modified'      => 'wp_validate_boolean',
 | |
| 			'uuid'          => 'wp_validate_boolean',
 | |
| 
 | |
| 			'searchable'    => 'wp_validate_boolean',
 | |
| 			'sortable'      => 'wp_validate_boolean',
 | |
| 			'date_query'    => 'wp_validate_boolean',
 | |
| 			'transition'    => 'wp_validate_boolean',
 | |
| 			'in'            => 'wp_validate_boolean',
 | |
| 			'not_in'        => 'wp_validate_boolean',
 | |
| 			'cache_key'     => 'wp_validate_boolean',
 | |
| 
 | |
| 			'pattern'       => array( $this, 'sanitize_pattern'       ),
 | |
| 			'validate'      => array( $this, 'sanitize_validation'    ),
 | |
| 			'caps'          => array( $this, 'sanitize_capabilities'  ),
 | |
| 			'aliases'       => array( $this, 'sanitize_aliases'       ),
 | |
| 			'relationships' => array( $this, 'sanitize_relationships' )
 | |
| 		);
 | |
| 
 | |
| 		// Default args array
 | |
| 		$r = array();
 | |
| 
 | |
| 		// Loop through and try to execute callbacks
 | |
| 		foreach ( $args as $key => $value ) {
 | |
| 
 | |
| 			// Callback is callable
 | |
| 			if ( isset( $callbacks[ $key ] ) && is_callable( $callbacks[ $key ] ) ) {
 | |
| 				$r[ $key ] = call_user_func( $callbacks[ $key ], $value );
 | |
| 
 | |
| 			// Callback is malformed so just let it through to avoid breakage
 | |
| 			} else {
 | |
| 				$r[ $key ] = $value;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Return sanitized arguments
 | |
| 		return $r;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Force column arguments for special column types
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param array $args Default empty array.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function special_args( $args = array() ) {
 | |
| 
 | |
| 		// Primary key columns are always used as cache keys
 | |
| 		if ( ! empty( $args['primary'] ) ) {
 | |
| 			$args['cache_key'] = true;
 | |
| 
 | |
| 		// All UUID columns need to follow a very specific pattern
 | |
| 		} elseif ( ! empty( $args['uuid'] ) ) {
 | |
| 			$args['name']       = 'uuid';
 | |
| 			$args['type']       = 'varchar';
 | |
| 			$args['length']     = '100';
 | |
| 			$args['in']         = false;
 | |
| 			$args['not_in']     = false;
 | |
| 			$args['searchable'] = false;
 | |
| 			$args['sortable']   = false;
 | |
| 		}
 | |
| 
 | |
| 		// Return args
 | |
| 		return (array) $args;
 | |
| 	}
 | |
| 
 | |
| 	/** Public Helpers ********************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Return if a column type is numeric or not.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @return bool
 | |
| 	 */
 | |
| 	public function is_numeric() {
 | |
| 		return $this->is_type( array(
 | |
| 			'tinyint',
 | |
| 			'int',
 | |
| 			'mediumint',
 | |
| 			'bigint'
 | |
| 		) );
 | |
| 	}
 | |
| 
 | |
| 	/** Private Helpers *******************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Return if this column is of a certain type.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param mixed $type Default empty string. The type to check. Also accepts an array.
 | |
| 	 * @return bool True if of type, False if not
 | |
| 	 */
 | |
| 	private function is_type( $type = '' ) {
 | |
| 
 | |
| 		// If string, cast to array
 | |
| 		if ( is_string( $type ) ) {
 | |
| 			$type = (array) $type;
 | |
| 		}
 | |
| 
 | |
| 		// Make them lowercase
 | |
| 		$types = array_map( 'strtolower', $type );
 | |
| 
 | |
| 		// Return if match or not
 | |
| 		return (bool) in_array( strtolower( $this->type ), $types, true );
 | |
| 	}
 | |
| 
 | |
| 	/** Private Sanitizers ****************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Sanitize capabilities array
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param array $caps Default empty array.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function sanitize_capabilities( $caps = array() ) {
 | |
| 		return wp_parse_args( $caps, array(
 | |
| 			'select' => 'exist',
 | |
| 			'insert' => 'exist',
 | |
| 			'update' => 'exist',
 | |
| 			'delete' => 'exist'
 | |
| 		) );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sanitize aliases array using `sanitize_key()`
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param array $aliases Default empty array.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function sanitize_aliases( $aliases = array() ) {
 | |
| 		return array_map( 'sanitize_key', $aliases );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sanitize relationships array
 | |
| 	 *
 | |
| 	 * @todo
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param array $relationships Default empty array.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function sanitize_relationships( $relationships = array() ) {
 | |
| 		return array_filter( $relationships );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sanitize the default value
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param string $default
 | |
| 	 * @return string|null
 | |
| 	 */
 | |
| 	private function sanitize_default( $default = '' ) {
 | |
| 
 | |
| 		// Null
 | |
| 		if ( ( true === $this->allow_null ) && is_null( $default ) ) {
 | |
| 			return null;
 | |
| 
 | |
| 			// String
 | |
| 		} elseif ( is_string( $default ) ) {
 | |
| 			return wp_kses_data( $default );
 | |
| 
 | |
| 			// Integer
 | |
| 		} elseif ( $this->is_numeric( $default ) ) {
 | |
| 			return (int) $default;
 | |
| 		}
 | |
| 
 | |
| 		// @todo datetime, decimal, and other column types
 | |
| 
 | |
| 		// Unknown, so return the default's default
 | |
| 		return '';
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sanitize the pattern
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param mixed $pattern
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	private function sanitize_pattern( $pattern = false ) {
 | |
| 
 | |
| 		// Allowed patterns
 | |
| 		$allowed_patterns = array( '%s', '%d', '%f' );
 | |
| 
 | |
| 		// Return pattern if allowed
 | |
| 		if ( in_array( $pattern, $allowed_patterns, true ) ) {
 | |
| 			return $pattern;
 | |
| 		}
 | |
| 
 | |
| 		// Fallback to digit or string
 | |
| 		return $this->is_numeric()
 | |
| 			? '%d'
 | |
| 			: '%s';
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Sanitize the validation callback
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param string $callback Default empty string. A callable PHP function name or method
 | |
| 	 * @return string The most appropriate callback function for the value
 | |
| 	 */
 | |
| 	private function sanitize_validation( $callback = '' ) {
 | |
| 
 | |
| 		// Return callback if it's callable
 | |
| 		if ( is_callable( $callback ) ) {
 | |
| 			return $callback;
 | |
| 		}
 | |
| 
 | |
| 		// UUID special column
 | |
| 		if ( true === $this->uuid ) {
 | |
| 			$callback = array( $this, 'validate_uuid' );
 | |
| 
 | |
| 		// Datetime fallback
 | |
| 		} elseif ( $this->is_type( 'datetime' ) ) {
 | |
| 			$callback = array( $this, 'validate_datetime' );
 | |
| 
 | |
| 		// Decimal fallback
 | |
| 		} elseif ( $this->is_type( 'decimal' ) ) {
 | |
| 			$callback = array( $this, 'validate_decimal' );
 | |
| 
 | |
| 		// Intval fallback
 | |
| 		} elseif ( $this->is_numeric() ) {
 | |
| 			$callback = 'intval';
 | |
| 		}
 | |
| 
 | |
| 		// Return the callback
 | |
| 		return $callback;
 | |
| 	}
 | |
| 
 | |
| 	/** Public Validators *****************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Fallback to validate a datetime value if no other is set.
 | |
| 	 *
 | |
| 	 * This assumes NO_ZERO_DATES is off or overridden.
 | |
| 	 *
 | |
| 	 * If MySQL drops support for zero dates, this method will need to be
 | |
| 	 * updated to support different default values based on the environment.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param string $value Default ''. A datetime value that needs validating
 | |
| 	 * @return string A valid datetime value
 | |
| 	 */
 | |
| 	public function validate_datetime( $value = '' ) {
 | |
| 
 | |
| 		// Handle "empty" values
 | |
| 		if ( empty( $value ) || ( '0000-00-00 00:00:00' === $value ) ) {
 | |
| 			$value = ! empty( $this->default )
 | |
| 				? $this->default
 | |
| 				: '';
 | |
| 
 | |
| 		// Convert to MySQL datetime format via date() && strtotime
 | |
| 		} elseif ( function_exists( 'date' ) ) {
 | |
| 			$value = date( 'Y-m-d H:i:s', strtotime( $value ) );
 | |
| 		}
 | |
| 
 | |
| 		// Return the validated value
 | |
| 		return $value;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Validate a decimal
 | |
| 	 *
 | |
| 	 * (Recommended decimal column length is '18,9'.)
 | |
| 	 *
 | |
| 	 * This is used to validate a mixed value before it is saved into a decimal
 | |
| 	 * column in a database table.
 | |
| 	 *
 | |
| 	 * Uses number_format() which does rounding to the last decimal if your
 | |
| 	 * value is longer than specified.
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param mixed $value    Default empty string. The decimal value to validate
 | |
| 	 * @param int   $decimals Default 9. The number of decimal points to accept
 | |
| 	 * @return float
 | |
| 	 */
 | |
| 	public function validate_decimal( $value = 0, $decimals = 9 ) {
 | |
| 
 | |
| 		// Protect against non-numeric values
 | |
| 		if ( ! is_numeric( $value ) ) {
 | |
| 			$value = 0;
 | |
| 		}
 | |
| 
 | |
| 		// Protect against non-numeric decimals
 | |
| 		if ( ! is_numeric( $decimals ) ) {
 | |
| 			$decimals = 9;
 | |
| 		}
 | |
| 
 | |
| 		// Is the value negative?
 | |
| 		$negative_exponent = ( $value < 0 )
 | |
| 			? -1
 | |
| 			: 1;
 | |
| 
 | |
| 		// Only numbers and period
 | |
| 		$value = preg_replace( '/[^0-9\.]/', '', (string) $value );
 | |
| 
 | |
| 		// Format to number of decimals, and cast as float
 | |
| 		$formatted = number_format( $value, $decimals, '.', '' );
 | |
| 
 | |
| 		// Adjust for negative values
 | |
| 		$retval = $formatted * $negative_exponent;
 | |
| 
 | |
| 		// Return
 | |
| 		return $retval;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Validate a UUID.
 | |
| 	 *
 | |
| 	 * This uses the v4 algorithm to generate a UUID that is used to uniquely
 | |
| 	 * and universally identify a given database row without any direct
 | |
| 	 * connection or correlation to the data in that row.
 | |
| 	 *
 | |
| 	 * From http://php.net/manual/en/function.uniqid.php#94959
 | |
| 	 *
 | |
| 	 * @since 1.0.0
 | |
| 	 * @param string $value The UUID value (empty on insert, string on update)
 | |
| 	 * @return string Generated UUID.
 | |
| 	 */
 | |
| 	public function validate_uuid( $value = '' ) {
 | |
| 
 | |
| 		// Default URN UUID prefix
 | |
| 		$prefix = 'urn:uuid:';
 | |
| 
 | |
| 		// Bail if not empty and correctly prefixed
 | |
| 		// (UUIDs should _never_ change once they are set)
 | |
| 		if ( ! empty( $value ) && ( 0 === strpos( $value, $prefix ) ) ) {
 | |
| 			return $value;
 | |
| 		}
 | |
| 
 | |
| 		// Put the pieces together
 | |
| 		$value = sprintf( "{$prefix}%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
 | |
| 
 | |
| 			// 32 bits for "time_low"
 | |
| 			mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
 | |
| 
 | |
| 			// 16 bits for "time_mid"
 | |
| 			mt_rand( 0, 0xffff ),
 | |
| 
 | |
| 			// 16 bits for "time_hi_and_version",
 | |
| 			// four most significant bits holds version number 4
 | |
| 			mt_rand( 0, 0x0fff ) | 0x4000,
 | |
| 
 | |
| 			// 16 bits, 8 bits for "clk_seq_hi_res",
 | |
| 			// 8 bits for "clk_seq_low",
 | |
| 			// two most significant bits holds zero and one for variant DCE1.1
 | |
| 			mt_rand( 0, 0x3fff ) | 0x8000,
 | |
| 
 | |
| 			// 48 bits for "node"
 | |
| 			mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
 | |
| 		);
 | |
| 
 | |
| 		// Return the new UUID
 | |
| 		return $value;
 | |
| 	}
 | |
| 
 | |
| 	/** Table Helpers *********************************************************/
 | |
| 
 | |
| 	/**
 | |
| 	 * Return a string representation of what this column's properties look like
 | |
| 	 * in a MySQL.
 | |
| 	 *
 | |
| 	 * @todo
 | |
| 	 * @since 1.0.0
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public function get_create_string() {
 | |
| 
 | |
| 		// Default return val
 | |
| 		$retval = '';
 | |
| 
 | |
| 		// Bail if no name
 | |
| 		if ( ! empty( $this->name ) ) {
 | |
| 			$retval .= $this->name;
 | |
| 		}
 | |
| 
 | |
| 		// Type
 | |
| 		if ( ! empty( $this->type ) ) {
 | |
| 			$retval .= " {$this->type}";
 | |
| 		}
 | |
| 
 | |
| 		// Length
 | |
| 		if ( ! empty( $this->length ) ) {
 | |
| 			$retval .= '(' . $this->length . ')';
 | |
| 		}
 | |
| 
 | |
| 		// Unsigned
 | |
| 		if ( ! empty( $this->unsigned ) ) {
 | |
| 			$retval .= " unsigned";
 | |
| 		}
 | |
| 
 | |
| 		// Zerofill
 | |
| 		if ( ! empty( $this->zerofill ) ) {
 | |
| 			// TBD
 | |
| 		}
 | |
| 
 | |
| 		// Binary
 | |
| 		if ( ! empty( $this->binary ) ) {
 | |
| 			// TBD
 | |
| 		}
 | |
| 
 | |
| 		// Allow null
 | |
| 		if ( ! empty( $this->allow_null ) ) {
 | |
| 			$retval .= " NOT NULL ";
 | |
| 		}
 | |
| 
 | |
| 		// Default
 | |
| 		if ( ! empty( $this->default ) ) {
 | |
| 			$retval .= " default '{$this->default}'";
 | |
| 
 | |
| 		// A literal false means no default value
 | |
| 		} elseif ( false !== $this->default ) {
 | |
| 
 | |
| 			// Numeric
 | |
| 			if ( $this->is_numeric() ) {
 | |
| 				$retval .= " default '0'";
 | |
| 			} elseif ( $this->is_type( 'datetime' ) ) {
 | |
| 				$retval .= " default '0000-00-00 00:00:00'";
 | |
| 			} else {
 | |
| 				$retval .= " default ''";
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Extra
 | |
| 		if ( ! empty( $this->extra ) ) {
 | |
| 			$retval .= " {$this->extra}";
 | |
| 		}
 | |
| 
 | |
| 		// Encoding
 | |
| 		if ( ! empty( $this->encoding ) ) {
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		// Collation
 | |
| 		if ( ! empty( $this->collation ) ) {
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		// Return the create string
 | |
| 		return $retval;
 | |
| 	}
 | |
| }
 |