0U7>VP?PzY7>VP``QbW7>V`PpGPzY7>VP@Q aW7>V0pHPzY7>VP  Q aW7>Vp HPzY7>VP!Q aW7>Vp!HPzY7>VP"Q aW7>Vp"HY7>VP#sV7>V#+PzY7>VP#Q0W7>V#SU7>V@#*U7>V@# aW7>V0p#HV7>Vp#>U7>V%>xȻ@xȻ@@@(m8@@mZ@mA@@"@@mPATH' ) To2y@X7>V.P9RV7>VPp9p@'V7>VpP9t@'V7>V``;tU7>V9<PX7>V0(Ap`YV7>V08pADX7>VA-X7>VPABU7>VA<*X7>VpPA2U7>VA<PX7>V@Bp`YV7>VPBDX7>VB-X7>VPBBU7>VB<*X7>VPB2U7>VB<U7>VC>@ mLmLm@WmJmJmim jmJmJm mpmmu_Item() ); } } To2}Z@X7>V`DRPV7>V`pD{V7>VpD+@X7>V$pERpW7>V0E|V7>VE>`YV7>V 8HDPX7>Ve߹ @HpPU7>VH<*X7>VPH2U7>VH<&Z7>VPH`YV7>VhID@OZ7>VPPIBU7>VI<X7>Vm@PI)7>VI@X7>VP0pKRpW7>VK|V7>VK>(7>VL|U7>VL>MmNmmmZmZmNmmmNmmconnecteTo2p0U7>VP?`iZ7>VPpV7>Vp@+U7>V@>U7>V0 E@OZ7>VPP B@'V7>V ` tPU7>V <&Z7>V` Y7>V`"sV7>V@"+U7>V#>U7>V&EX7>V&P2W7>VP&tX7>V&d|Y7>V`P&]/X7>V`&U7>V&<U7>V)>U7>V,>@?mD?m`D?mƹ@0@@?mD?mE?mJ@@\m0@SmmU7>V`^E@'V7>VpP^tX7>VP^sW7>VP`^G[V7>V `^HP2W7>V``^t@'V7>Vp^t@'V7>V^tU7>V^<U7>V_E@'V7>VP_tX7>V_sW7>V_G[V7>Vp_HP2W7>V`_t@'V7>V@p_t */ do_action( 'stellarwp_pre_drop_table', $base_table_name, $this_table, $this ); global $wpdb; /** * Disable foreign key checks so we can drop without issues. * @var \stdClass $key_check */ $key_check = $this->db::get_row( "SHOW VARIABLES LIKE 'foreign_key_checks'" ); if ( strtolower( $key_check->Value ) === 'on' ) { $this->db::query( "SET foreign_key_checks = 'OFF'" ); } $result = $this->db::query( "DROP TABLE `{$this_table}`" ); // Put setting back to original value. $this->db::query( $this->db::prepare( "SET foreign_key_checks = %s", $key_check->Value ) ); /** * Runs after the custom table has been dropped. * * @since 1.0.0 * * @param string $base_table_name The base table name. * @param string $table_name The full table name. * @param Schema_Interface $table_schema The table schema to be dropped. */ do_action( 'stellarwp_post_drop_table', $base_table_name, $this_table, $this ); $base_table_name = static::base_table_name(); if ( isset( $wpdb->$base_table_name ) ) { unset( $wpdb->$base_table_name ); } /** * Runs after the custom table has been removed from $wpdb. * * @since 1.0.0 * * @param string $base_table_name The base table name. * @param string $table_name The full table name. * @param Schema_Interface $table_schema The table schema to be dropped. */ do_action( 'stellarwp_post_drop_table_wpdb_update', $base_table_name, $this_table, $this ); return $result; } /** * Empties the custom table. * * @since 1.0.0 * * @throws \TEC\Common\StellarWP\DB\Database\Exceptions\DatabaseQueryException If the query fails. * * @return int|false The number of removed rows, or `false` to indicate a failure. */ public function empty_table() { if ( ! $this->exists() ) { // There is really nothing to empty here. return 0; } $this_table = static::table_name( true ); $this->db::query( "SET foreign_key_checks = 0" ); $result = $this->db::query( "DELETE FROM {$this_table}" ); $this->db::query( "SET foreign_key_checks = 1" ); return $result; } /** * Truncates the custom table. * * @since 1.1.8 * * @throws \TEC\Common\StellarWP\DB\Database\Exceptions\DatabaseQueryException If the query fails. * * @return int|false The number of removed rows, or `false` to indicate a failure. */ public function truncate() { if ( ! $this->exists() ) { // There is really nothing to empty here. return 0; } $this_table = static::table_name( true ); $this->db::query( "SET foreign_key_checks = 0" ); $result = $this->db::query( "TRUNCATE {$this_table}" ); $this->db::query( "SET foreign_key_checks = 1" ); return $result; } /** * Returns whether a table exists in the database or not. * * @since 1.0.0 * * @throws \TEC\Common\StellarWP\DB\Database\Exceptions\DatabaseQueryException If the query fails. * * @return bool Whether a table exists in the database or not. */ public function exists() { $table_name = static::table_name( true ); return count( $this->db::get_col( $this->db::prepare( 'SHOW TABLES LIKE %s', $table_name ) ) ) === 1; } /** * Gets the defined fields schemas for the table. * * @since 1.0.0 * * @param bool $force Force a refresh of the field collection. * * @return \Iterator */ public function get_field_schemas( bool $force = false ) { if ( $this->field_schemas === null || $force ) { $this->field_schemas = $this->container->get( Fields\Collection::class )->get_by_table( static::base_table_name() ); } return $this->field_schemas; } /** * {@inheritdoc} */ public static function get_schema_slug() { return static::$schema_slug; } /** * Gets the properly namespaced schema version option key. * * @since 1.0.0 * * @return string The properly namespaced schema version option key. */ public function get_schema_previous_version_option(): string { return 'stellar_schema_previous_version_' . static::get_schema_slug(); } /** * Gets the properly namespaced schema version option key. * * @since 1.0.0 * * @return string The properly namespaced schema version option key. */ public function get_schema_version_option(): string { return 'stellar_schema_version_' . static::get_schema_slug(); } /** * {@inheritdoc} */ public function get_sql() { $sql = $this->get_definition(); $field_schemas = $this->get_field_schemas(); foreach ( $field_schemas as $field_schema ) { $sql = $this->inject_field_schema( $field_schema, $sql ); } return $sql; } /** * Gets the previous schema version option value. * * @since 1.0.0 * * @return string|null The previous version stored in wp_options. */ public function get_stored_previous_version() { return get_option( $this->get_schema_previous_version_option(), null ); } /** * Gets the current schema version option value. * * @since 1.0.0 * * @return string|null The current version stored in wp_options. */ public function get_stored_version() { return get_option( $this->get_schema_version_option(), null ); } /** * Returns the table creation SQL in the format supported * by the `dbDelta` function. * * @since 1.0.0 * * @return string The table creation SQL, in the format supported * by the `dbDelta` function. */ abstract protected function get_definition(); /** * {@inheritdoc} */ public function get_version(): string { $field_versions = []; $schema_fields = $this->get_field_schemas( true ); foreach ( $schema_fields as $field ) { $field_versions[] = $field->get_version(); } return static::SCHEMA_VERSION . ( $field_versions ? '-' . md5( implode( ':', $field_versions ) ) : '' ); } /** * {@inheritdoc} */ public static function group_name() { return static::$group; } /** * Checks if an index already exists on the table. * * @since 1.0.0 * * @param string $index The name of the index to check for. * @param string|null $table_name The table name to search the index for, or `null` * to use this table name. * * @throws \TEC\Common\StellarWP\DB\Database\Exceptions\DatabaseQueryException If the query fails. * * @return bool Whether the table already has an index or not. */ public function has_index( $index, $table_name = null ) { $table_name = $table_name ?: static::table_name( true ); $count = $this->db::table( $this->db::raw( 'information_schema.statistics' ) ) ->whereRaw( 'WHERE TABLE_SCHEMA = DATABASE()' ) ->where( 'TABLE_NAME', $table_name ) ->where( 'INDEX_NAME', $index ) ->count(); return $count >= 1; } /** * Inject field schema definitions into the CREATE TABLE SQL. * * @since 1.0.0 * * @param Field_Schema_Interface $field_schema The field schema to inject. * @param string $sql The CREATE TABLE SQL to inject into. * * @return string */ protected function inject_field_schema( Field_Schema_Interface $field_schema, $sql ): string { $fields = trim( $field_schema->get_sql() ); // Inject any extra fields into the table's definition. // phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation $find_first_index_regex = '/' . '(,' // 1) Final comma before indexes. . '(\s*)' // 2) Capture whitespace before indexes. . '(?=' // Followed by the indexes. . '(?:' . 'PRIMARY\s+KEY|(?:UNIQUE|FULLTEXT|SPACIAL)\s+(?:KEY|INDEX)|KEY|INDEX' . ')' . ')' . '[^\n]+' // Followed by indice columns and names. . '(?!' // Not followed by another index. . '(?:' . 'PRIMARY\s+KEY|(?:UNIQUE|FULLTEXT|SPACIAL)\s+(?:KEY|INDEX)|KEY|INDEX' . ')' . ')' . ')' .'/im'; // Case insensitive and multi-line. if ( preg_match( $find_first_index_regex, $sql ) ) { // Inject additional fields before the indexes. $sql = preg_replace( $find_first_index_regex, ",$2{$fields}$1", // $2 is the captured whitespace. $1 is the whitespace PLUS the first index after the last field. $sql ); } else { // Inject additional fields before the closing parenthesis of the CREATE TABLE statement. $sql = preg_replace( '/(?get_schema_version_option() ) { // @todo Error? } $version_applied = $this->get_stored_version() ?: ''; $current_version = $this->get_version(); return version_compare( $version_applied, $current_version, '==' ); } /** * Update our stored version with what we have defined. */ public function sync_stored_version() { $current_version = $this->get_version(); if ( ! add_option( $this->get_schema_version_option(), $current_version ) ) { update_option( $this->get_schema_version_option(), $current_version ); } } /** * {@inheritdoc} */ public static function table_name( $with_prefix = true ) { $table_name = static::base_table_name(); if ( $with_prefix ) { global $wpdb; $table_name = $wpdb->prefix . $table_name; } return $table_name; } /** * {@inheritdoc} */ public static function uid_column() { return static::$uid_column; } /** * {@inheritdoc} */ public function update() { // @phpstan-ignore-next-line require_once ABSPATH . 'wp-admin/includes/upgrade.php'; $sql = $this->get_sql(); $field_schemas = $this->get_field_schemas(); /** * Hookable action before the table schema has updated. * * @since 1.1.0 * * @param string $table_name The prefix-less table name. * @param Table $table The table object. * @param \Iterator $field_schemas An iterable collection of field schemas associated with this table. */ do_action( 'stellarwp_schema_table_before_updete', static::table_name(), $this, $field_schemas ); $results = (array) $this->db::delta( $sql ); $this->archive_previous_version(); $this->sync_stored_version(); $results = $this->after_update( $results ); /** * Hookable action before the field schemas have updated. * * @since 1.1.0 * * @param string $table_name The prefix-less table name. * @param array $results The results of the schema update. * @param Table $table The table object. * @param \Iterator $field_schemas An iterable collection of field schemas associated with this table. */ do_action( 'stellarwp_schema_table_before_field_schema_updete', static::table_name(), $results, $this, $field_schemas ); foreach ( $field_schemas as $field_schema ) { $results[] = $field_schema->after_update( $results ); } /** * Hookable action after the table schema has updated. * * @since 1.1.0 * * @param string $table_name The prefix-less table name. * @param array $results The results of the table and field schema updates. * @param Table $table The table object. * @param \Iterator $field_schemas An iterable collection of field schemas associated with this table. */ do_action( 'stellarwp_schema_table_after_updete', static::table_name(), $results, $this, $field_schemas ); return $results; } /** * Checks if a foreign key exists on a table. * * @since 1.1.3 * * @param string $foreign_key The foreign key to check for. * @param string $table_name The table name to check. Defaults to the current table. * * @return bool Whether the foreign key exists on the table. */ public function has_foreign_key( string $foreign_key, string $table_name = '' ): bool { $table_name = $table_name ?: static::table_name(); $count = $this->db::table( $this->db::raw( 'information_schema.statistics' ) ) ->whereRaw( 'WHERE TABLE_SCHEMA = DATABASE()' ) ->where( 'TABLE_NAME', $table_name ) ->where( 'INDEX_NAME', $foreign_key ) ->count(); return $count >= 1; } }