Changeset 2960316
- Timestamp:
- 08/29/2023 11:37:08 PM (2 years ago)
- Location:
- meta-optimizer/trunk
- Files:
-
- 15 edited
-
WPMetaOptimizer.php (modified) (3 diffs)
-
assets/wpmo.js (modified) (3 diffs)
-
inc/Actions.php (modified) (1 diff)
-
inc/Base.php (modified) (1 diff)
-
inc/CommentQueries.php (modified) (1 diff)
-
inc/Helpers.php (modified) (1 diff)
-
inc/Install.php (modified) (2 diffs)
-
inc/Integration.php (modified) (1 diff)
-
inc/MetaQuery.php (modified) (44 diffs)
-
inc/Options.php (modified) (3 diffs)
-
inc/PostQueries.php (modified) (1 diff)
-
inc/Queries.php (modified) (1 diff)
-
inc/TermQueries.php (modified) (1 diff)
-
inc/UserQueries.php (modified) (2 diffs)
-
readme.txt (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
meta-optimizer/trunk/WPMetaOptimizer.php
r2802191 r2960316 1 1 <?php 2 2 3 /* *3 /*! 4 4 * Plugin Name: Meta Optimizer 5 * Version: 1. 05 * Version: 1.1 6 6 * Plugin URI: https://parsakafi.github.io/wp-meta-optimizer 7 7 * Description: You can use Meta Optimizer to make your WordPress website load faster if you use meta information, for example Post/Comment/User/Term metas. … … 14 14 15 15 // Check run from WP 16 defined( 'ABSPATH') || die();16 defined( 'ABSPATH' ) || die(); 17 17 18 18 require_once __DIR__ . '/inc/Base.php'; … … 29 29 require_once __DIR__ . '/inc/Integration.php'; 30 30 31 define( 'WPMETAOPTIMIZER_PLUGIN_KEY', 'wp-meta-optimizer');32 define( 'WPMETAOPTIMIZER_OPTION_KEY', 'wp_meta_optimizer');33 define( 'WPMETAOPTIMIZER_PLUGIN_NAME', 'Meta Optimizer');34 define( 'WPMETAOPTIMIZER_PLUGIN_FILE_PATH', __FILE__);35 define( 'WPMETAOPTIMIZER_CACHE_EXPIRE', 30);31 define( 'WPMETAOPTIMIZER_PLUGIN_KEY', 'wp-meta-optimizer' ); 32 define( 'WPMETAOPTIMIZER_OPTION_KEY', 'wp_meta_optimizer' ); 33 define( 'WPMETAOPTIMIZER_PLUGIN_NAME', 'Meta Optimizer' ); 34 define( 'WPMETAOPTIMIZER_PLUGIN_FILE_PATH', __FILE__ ); 35 define( 'WPMETAOPTIMIZER_CACHE_EXPIRE', 30 ); 36 36 37 37 /** 38 38 * Main class run Meta Optimizer plugin 39 39 */ 40 class WPMetaOptimizer extends Base 41 { 42 public static $instance = null; 43 protected $Helpers, $Options; 44 45 function __construct() 46 { 47 parent::__construct(); 48 49 $this->Helpers = Helpers::getInstance(); 50 $this->Options = Options::getInstance(); 51 Actions::getInstance(); 52 Queries::getInstance(); 53 Integration::getInstance(); 54 55 $actionPriority = 99999999; 56 57 $types = array_keys($this->Options->getOption('meta_save_types', [])); 58 foreach ($types as $type) { 59 if ($type == 'hidden') 60 continue; 61 add_filter('get_' . $type . '_metadata', [$this, 'getMeta'], $actionPriority, 5); 62 add_filter('add_' . $type . '_metadata', [$this, 'add' . ucwords($type) . 'Meta'], $actionPriority, 5); 63 add_filter('update_' . $type . '_metadata', [$this, 'update' . ucwords($type) . 'Meta'], $actionPriority, 5); 64 add_filter('delete_' . $type . '_metadata', [$this, 'delete' . ucwords($type) . 'Meta'], $actionPriority, 5); 65 } 66 } 67 68 /** 69 * Adds a meta field to the given post. 70 * 71 * @param null|bool $check Whether to allow adding metadata for the given type. 72 * @param int $objectID ID of the object metadata is for. 73 * @param string $metaKey Metadata key. 74 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 75 * @param bool $unique Whether the specified meta key should be unique for the object. 76 * 77 * @return int|false The meta ID on success, false on failure. 78 */ 79 function addPostMeta($check, $objectID, $metaKey, $metaValue, $unique) 80 { 81 return $this->addMeta('post', $check, $objectID, $metaKey, $metaValue, $unique); 82 } 83 84 /** 85 * Updates a post meta field based on the given post ID. 86 * 87 * @param null|bool $check Whether to allow updating metadata for the given type. 88 * @param int $objectID ID of the object metadata is for. 89 * @param string $metaKey Metadata key. 90 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 91 * @param mixed $prevValue Optional. Previous value to check before updating. 92 * If specified, only update existing metadata entries with 93 * this value. Otherwise, update all entries. 94 * 95 * @return int|bool The new meta field ID if a field with the given key didn't exist 96 * and was therefore added, true on successful update, 97 * false on failure or if the value passed to the function 98 * is the same as the one that is already in the database. 99 */ 100 function updatePostMeta($check, $objectID, $metaKey, $metaValue, $prevValue) 101 { 102 return $this->updateMeta('post', $check, $objectID, $metaKey, $metaValue, $prevValue); 103 } 104 105 /** 106 * Removes metadata matching criteria from a post. 107 * Fires after WordPress meta removed 108 * 109 * @param null|bool $delete Whether to allow metadata deletion of the given type. 110 * @param int $objectID ID of the object metadata is for. 111 * @param string $metaKey Metadata key. 112 * @param mixed $metaValue Metadata value. 113 * @param bool $deleteAll Whether to delete the matching metadata entries 114 * for all objects, ignoring the specified $object_id. 115 * Default false. 116 * 117 * @return null|bool return $delete value 118 */ 119 function deletePostMeta($delete, $objectID, $metaKey, $metaValue, $deleteAll) 120 { 121 $this->deleteMeta('post', $objectID, $metaKey, $metaValue, $deleteAll); 122 return $delete; 123 } 124 125 /** 126 * Adds a meta field to the given comment. 127 * 128 * @param null|bool $check Whether to allow adding metadata for the given type. 129 * @param int $objectID ID of the object metadata is for. 130 * @param string $metaKey Metadata key. 131 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 132 * @param bool $unique Whether the specified meta key should be unique for the object. 133 * 134 * @return int|false The meta ID on success, false on failure. 135 */ 136 function addCommentMeta($check, $objectID, $metaKey, $metaValue, $unique) 137 { 138 return $this->addMeta('comment', $check, $objectID, $metaKey, $metaValue, $unique); 139 } 140 141 /** 142 * Updates a comment meta field based on the given post ID. 143 * 144 * @param null|bool $check Whether to allow updating metadata for the given type. 145 * @param int $objectID ID of the object metadata is for. 146 * @param string $metaKey Metadata key. 147 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 148 * @param mixed $prevValue Optional. Previous value to check before updating. 149 * If specified, only update existing metadata entries with 150 * this value. Otherwise, update all entries. 151 * 152 * @return int|bool The new meta field ID if a field with the given key didn't exist 153 * and was therefore added, true on successful update, 154 * false on failure or if the value passed to the function 155 * is the same as the one that is already in the database. 156 */ 157 function updateCommentMeta($check, $objectID, $metaKey, $metaValue, $prevValue) 158 { 159 return $this->updateMeta('comment', $check, $objectID, $metaKey, $metaValue, $prevValue); 160 } 161 162 /** 163 * Removes metadata matching criteria from a comment. 164 * Fires after WordPress meta removed 165 * 166 * @param null|bool $delete Whether to allow metadata deletion of the given type. 167 * @param int $objectID ID of the object metadata is for. 168 * @param string $metaKey Metadata key. 169 * @param mixed $metaValue Metadata value. 170 * @param bool $deleteAll Whether to delete the matching metadata entries 171 * for all objects, ignoring the specified $object_id. 172 * Default false. 173 * 174 * @return null|bool return $delete value 175 */ 176 function deleteCommentMeta($delete, $objectID, $metaKey, $metaValue, $deleteAll) 177 { 178 $this->deleteMeta('comment', $objectID, $metaKey, $metaValue, $deleteAll); 179 return $delete; 180 } 181 182 /** 183 * Adds a meta field to the given term. 184 * 185 * @param null|bool $check Whether to allow adding metadata for the given type. 186 * @param int $objectID ID of the object metadata is for. 187 * @param string $metaKey Metadata key. 188 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 189 * @param bool $unique Whether the specified meta key should be unique for the object. 190 * 191 * @return int|false The meta ID on success, false on failure. 192 */ 193 function addTermMeta($check, $objectID, $metaKey, $metaValue, $unique) 194 { 195 return $this->addMeta('term', $check, $objectID, $metaKey, $metaValue, $unique); 196 } 197 198 /** 199 * Updates a term meta field based on the given term ID. 200 * 201 * @param null|bool $check Whether to allow updating metadata for the given type. 202 * @param int $objectID ID of the object metadata is for. 203 * @param string $metaKey Metadata key. 204 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 205 * @param mixed $prevValue Optional. Previous value to check before updating. 206 * If specified, only update existing metadata entries with 207 * this value. Otherwise, update all entries. 208 * 209 * @return int|bool The new meta field ID if a field with the given key didn't exist 210 * and was therefore added, true on successful update, 211 * false on failure or if the value passed to the function 212 * is the same as the one that is already in the database. 213 */ 214 function updateTermMeta($check, $objectID, $metaKey, $metaValue, $prevValue) 215 { 216 return $this->updateMeta('term', $check, $objectID, $metaKey, $metaValue, $prevValue); 217 } 218 219 /** 220 * Removes metadata matching criteria from a term. 221 * Fires after WordPress meta removed 222 * 223 * @param null|bool $delete Whether to allow metadata deletion of the given type. 224 * @param int $objectID ID of the object metadata is for. 225 * @param string $metaKey Metadata key. 226 * @param mixed $metaValue Metadata value. 227 * @param bool $deleteAll Whether to delete the matching metadata entries 228 * for all objects, ignoring the specified $object_id. 229 * Default false. 230 * 231 * @return null|bool return $delete value 232 */ 233 function deleteTermMeta($delete, $objectID, $metaKey, $metaValue, $deleteAll) 234 { 235 $this->deleteMeta('term', $objectID, $metaKey, $metaValue, $deleteAll); 236 return $delete; 237 } 238 239 /** 240 * Adds a meta field to the given user. 241 * 242 * @param null|bool $check Whether to allow adding metadata for the given type. 243 * @param int $objectID ID of the object metadata is for. 244 * @param string $metaKey Metadata key. 245 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 246 * @param bool $unique Whether the specified meta key should be unique for the object. 247 * 248 * @return int|false The meta ID on success, false on failure. 249 */ 250 function addUserMeta($check, $objectID, $metaKey, $metaValue, $unique) 251 { 252 return $this->addMeta('user', $check, $objectID, $metaKey, $metaValue, $unique); 253 } 254 255 /** 256 * Updates a user meta field based on the given user ID. 257 * 258 * @param null|bool $check Whether to allow updating metadata for the given type. 259 * @param int $objectID ID of the object metadata is for. 260 * @param string $metaKey Metadata key. 261 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 262 * @param mixed $prevValue Optional. Previous value to check before updating. 263 * If specified, only update existing metadata entries with 264 * this value. Otherwise, update all entries. 265 * 266 * @return int|bool The new meta field ID if a field with the given key didn't exist 267 * and was therefore added, true on successful update, 268 * false on failure or if the value passed to the function 269 * is the same as the one that is already in the database. 270 */ 271 function updateUserMeta($check, $objectID, $metaKey, $metaValue, $prevValue) 272 { 273 return $this->updateMeta('user', $check, $objectID, $metaKey, $metaValue, $prevValue); 274 } 275 276 /** 277 * Removes metadata matching criteria from a user. 278 * Fires after WordPress meta removed 279 * 280 * @param null|bool $delete Whether to allow metadata deletion of the given type. 281 * @param int $objectID ID of the object metadata is for. 282 * @param string $metaKey Metadata key. 283 * @param mixed $metaValue Metadata value. 284 * @param bool $deleteAll Whether to delete the matching metadata entries 285 * for all objects, ignoring the specified $object_id. 286 * Default false. 287 * 288 * @return null|bool return $delete value 289 */ 290 function deleteUserMeta($delete, $objectID, $metaKey, $metaValue, $deleteAll) 291 { 292 $this->deleteMeta('user', $objectID, $metaKey, $metaValue, $deleteAll); 293 return $delete; 294 } 295 296 /** 297 * Retrieves raw metadata value for the specified object. 298 * 299 * @param mixed $value The value to return, either a single metadata value or an array 300 * of values depending on the value of `$single`. Default null. 301 * @param int $objectID ID of the object metadata is for. 302 * @param string $metaKey Metadata key. 303 * @param bool $single Whether to return only the first value of the specified `$metaKey`. 304 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 305 * or any other object type with an associated meta table. 306 * 307 * @return mixed An array of values if `$single` is false. 308 * The value of the meta field if `$single` is true. 309 * False for an invalid `$objectID` (non-numeric, zero, or negative value), 310 * or if `$metaType` is not specified. 311 * Null if the value does not exist. 312 */ 313 function getMeta($value, $objectID, $metaKey, $single, $metaType) 314 { 315 global $wpdb; 316 317 //if ($metaKey === '') 318 // return $value; 319 320 if (defined('IMPORT_PROCESS_WPMO')) 321 return $value; 322 323 $tableName = $this->Helpers->getMetaTableName($metaType); 324 if (!$tableName) 325 return $value; 326 327 if (!$this->Helpers->checkMetaType($metaType)) 328 return $value; 329 330 if ($metaType === 'post' && !$this->Helpers->checkPostType($objectID)) 331 return $value; 332 333 if ($this->Helpers->checkInBlackWhiteList($metaType, $metaKey, 'black_list') === true || $this->Helpers->checkInBlackWhiteList($metaType, $metaKey, 'white_list') === false) 334 return $value; 335 336 if (substr($metaKey, - (strlen($this->reservedKeysSuffix))) !== $this->reservedKeysSuffix) 337 $metaKey = $this->Helpers->translateColumnName($metaType, $metaKey); 338 339 if (!$this->Helpers->checkColumnExists($tableName, $metaType, $metaKey)) 340 return $value; 341 342 $metaRow = wp_cache_get($tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY); 343 if ($metaRow === false) { 344 $tableColumns = $this->Helpers->getTableColumns($tableName, $metaType, true); 345 $tableColumns = '`' . implode('`,`', $tableColumns) . '`'; 346 $sql = "SELECT {$tableColumns} FROM `{$tableName}` WHERE {$metaType}_id = {$objectID}"; 347 $metaRow = $wpdb->get_row($sql, ARRAY_A); 348 wp_cache_set($tableName . '_' . $metaType . '_' . $objectID . '_row', $metaRow, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE); 349 } 350 351 if (is_array($metaRow) && isset($metaRow[$metaKey])) { 352 $metaValue = maybe_unserialize($metaRow[$metaKey]); 353 return $single && is_array($metaValue) && isset($metaValue[0]) ? $metaValue[0] : $metaValue; 354 } 355 356 return $value; 357 } 358 359 /** 360 * Adds metadata for the specified object. 361 * 362 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 363 * or any other object type with an associated meta table. 364 * @param null|bool $check Whether to allow adding metadata for the given type. 365 * @param int $objectID ID of the object metadata is for. 366 * @param string $metaKey Metadata key. 367 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 368 * @param bool $unique Whether the specified meta key should be unique for the object. 369 * 370 * @return int|false The meta ID on success, false on failure. 371 */ 372 function addMeta($metaType, $check, $objectID, $metaKey, $metaValue, $unique) 373 { 374 if (!$this->Helpers->checkMetaType($metaType)) 375 return $check; 376 377 if ($metaType === 'post' && !$this->Helpers->checkPostType($objectID)) 378 return $check; 379 380 if ($this->Helpers->checkInBlackWhiteList($metaType, $metaKey, 'black_list') === true || $this->Helpers->checkInBlackWhiteList($metaType, $metaKey, 'white_list') === false) 381 return $check; 382 383 $metaKey = $this->Helpers->translateColumnName($metaType, $metaKey); 384 385 $result = $this->Helpers->insertMeta( 386 [ 387 'metaType' => $metaType, 388 'objectID' => $objectID, 389 'metaKey' => $metaKey, 390 'metaValue' => $metaValue, 391 'unique' => $unique, 392 'addMeta' => true 393 ] 394 ); 395 396 $tableName = $this->Helpers->getMetaTableName($metaType); 397 wp_cache_delete($tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY); 398 399 return $this->Helpers->checkDontSaveInDefaultTable($metaType) ? $result : $check; 400 } 401 402 /** 403 * Updates metadata for the specified object. If no value already exists for the specified object 404 * ID and metadata key, the metadata will be added. 405 * 406 * @global wpdb $wpdb WordPress database abstraction object. 407 * 408 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 409 * or any other object type with an associated meta table. 410 * @param null|bool $check Whether to allow updating metadata for the given type. 411 * @param int $objectID ID of the object metadata is for. 412 * @param string $metaKey Metadata key. 413 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 414 * @param mixed $prevValue Optional. Previous value to check before updating. 415 * If specified, only update existing metadata entries with 416 * this value. Otherwise, update all entries. Default empty. 417 * 418 * @return int|bool The new meta field ID if a field with the given key didn't exist 419 * and was therefore added, true on successful update, 420 * false on failure or if the value passed to the function 421 * is the same as the one that is already in the database. 422 */ 423 function updateMeta($metaType, $check, $objectID, $metaKey, $metaValue, $prevValue) 424 { 425 if (!$this->Helpers->checkMetaType($metaType)) 426 return $check; 427 428 if ($metaType === 'post' && !$this->Helpers->checkPostType($objectID)) 429 return $check; 430 431 if ($this->Helpers->checkInBlackWhiteList($metaType, $metaKey, 'black_list') === true || $this->Helpers->checkInBlackWhiteList($metaType, $metaKey, 'white_list') === false) 432 return $check; 433 434 $metaKey = $this->Helpers->translateColumnName($metaType, $metaKey); 435 436 $result = $this->Helpers->insertMeta( 437 [ 438 'metaType' => $metaType, 439 'objectID' => $objectID, 440 'metaKey' => $metaKey, 441 'metaValue' => $metaValue, 442 'unique' => false, 443 'prevValue' => $prevValue 444 ] 445 ); 446 447 $tableName = $this->Helpers->getMetaTableName($metaType); 448 wp_cache_delete($tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY); 449 450 return $this->Helpers->checkDontSaveInDefaultTable($metaType) ? $result : $check; 451 } 452 453 /** 454 * Deletes metadata for the specified object. 455 * 456 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 457 * or any other object type with an associated meta table. 458 * @param int $objectID ID of the object metadata is for. 459 * @param string $metaKey Metadata key. 460 * @param mixed $metaValue Metadata value. 461 * @param bool $deleteAll Whether to delete the matching metadata entries 462 * for all objects, ignoring the specified $objectID. 463 * 464 * @return boolean|int 465 */ 466 467 private function deleteMeta($metaType, $objectID, $metaKey, $metaValue, $deleteAll) 468 { 469 global $wpdb; 470 471 $tableName = $this->Helpers->getMetaTableName($metaType); 472 if (!$tableName) 473 return false; 474 475 $column = sanitize_key($metaType . '_id'); 476 477 $metaKey = $this->Helpers->translateColumnName($metaType, $metaKey); 478 479 $newValue = null; 480 481 if (!$deleteAll && '' !== $metaValue && null !== $metaValue && false !== $metaValue) { 482 $metaValue = maybe_unserialize($metaValue); 483 $newValue = $currentValue = $this->getMeta($newValue, $objectID, $metaKey, false, $metaType); 484 if (is_array($currentValue) && ($indexValue = array_search($metaValue, $currentValue, false)) !== false) { 485 unset($currentValue[$indexValue]); 486 $newValue = $currentValue; 487 } 488 489 $newValue = maybe_serialize($newValue); 490 } 491 492 $result = $wpdb->update( 493 $tableName, 494 [$metaKey => $newValue, 'updated_at' => $this->now], 495 [$column => $objectID] 496 ); 497 498 wp_cache_delete($tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY); 499 500 return $result; 501 } 502 503 /** 504 * Returns an instance of class 505 * @return WPMetaOptimizer 506 */ 507 static function getInstance() 508 { 509 if (self::$instance == null) 510 self::$instance = new WPMetaOptimizer(); 511 512 return self::$instance; 513 } 40 class WPMetaOptimizer extends Base { 41 public static $instance = null; 42 protected $Helpers, $Options; 43 44 function __construct() { 45 parent::__construct(); 46 47 $this->Helpers = Helpers::getInstance(); 48 $this->Options = Options::getInstance(); 49 Actions::getInstance(); 50 Queries::getInstance(); 51 Integration::getInstance(); 52 53 $actionPriority = 99999999; 54 55 $types = array_keys( $this->Options->getOption( 'meta_save_types', [] ) ); 56 foreach ( $types as $type ) { 57 if ( $type == 'hidden' ) 58 continue; 59 add_filter( 'get_' . $type . '_metadata', [ $this, 'getMeta' ], $actionPriority, 5 ); 60 add_filter( 'add_' . $type . '_metadata', [ 61 $this, 62 'add' . ucwords( $type ) . 'Meta' 63 ], $actionPriority, 5 ); 64 add_filter( 'update_' . $type . '_metadata', [ 65 $this, 66 'update' . ucwords( $type ) . 'Meta' 67 ], $actionPriority, 5 ); 68 add_filter( 'delete_' . $type . '_metadata', [ 69 $this, 70 'delete' . ucwords( $type ) . 'Meta' 71 ], $actionPriority, 5 ); 72 } 73 } 74 75 /** 76 * Adds a meta field to the given post. 77 * 78 * @param null|bool $check Whether to allow adding metadata for the given type. 79 * @param int $objectID ID of the object metadata is for. 80 * @param string $metaKey Metadata key. 81 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 82 * @param bool $unique Whether the specified meta key should be unique for the object. 83 * 84 * @return int|false The meta ID on success, false on failure. 85 */ 86 function addPostMeta( $check, $objectID, $metaKey, $metaValue, $unique ) { 87 return $this->addMeta( 'post', $check, $objectID, $metaKey, $metaValue, $unique ); 88 } 89 90 /** 91 * Updates a post meta field based on the given post ID. 92 * 93 * @param null|bool $check Whether to allow updating metadata for the given type. 94 * @param int $objectID ID of the object metadata is for. 95 * @param string $metaKey Metadata key. 96 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 97 * @param mixed $prevValue Optional. Previous value to check before updating. 98 * If specified, only update existing metadata entries with 99 * this value. Otherwise, update all entries. 100 * 101 * @return int|bool The new meta field ID if a field with the given key didn't exist 102 * and was therefore added, true on successful update, 103 * false on failure or if the value passed to the function 104 * is the same as the one that is already in the database. 105 */ 106 function updatePostMeta( $check, $objectID, $metaKey, $metaValue, $prevValue ) { 107 return $this->updateMeta( 'post', $check, $objectID, $metaKey, $metaValue, $prevValue ); 108 } 109 110 /** 111 * Removes metadata matching criteria from a post. 112 * Fires after WordPress meta removed 113 * 114 * @param null|bool $delete Whether to allow metadata deletion of the given type. 115 * @param int $objectID ID of the object metadata is for. 116 * @param string $metaKey Metadata key. 117 * @param mixed $metaValue Metadata value. 118 * @param bool $deleteAll Whether to delete the matching metadata entries 119 * for all objects, ignoring the specified $object_id. 120 * Default false. 121 * 122 * @return null|bool return $delete value 123 */ 124 function deletePostMeta( $delete, $objectID, $metaKey, $metaValue, $deleteAll ) { 125 $this->deleteMeta( 'post', $objectID, $metaKey, $metaValue, $deleteAll ); 126 127 return $delete; 128 } 129 130 /** 131 * Adds a meta field to the given comment. 132 * 133 * @param null|bool $check Whether to allow adding metadata for the given type. 134 * @param int $objectID ID of the object metadata is for. 135 * @param string $metaKey Metadata key. 136 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 137 * @param bool $unique Whether the specified meta key should be unique for the object. 138 * 139 * @return int|false The meta ID on success, false on failure. 140 */ 141 function addCommentMeta( $check, $objectID, $metaKey, $metaValue, $unique ) { 142 return $this->addMeta( 'comment', $check, $objectID, $metaKey, $metaValue, $unique ); 143 } 144 145 /** 146 * Updates a comment meta field based on the given post ID. 147 * 148 * @param null|bool $check Whether to allow updating metadata for the given type. 149 * @param int $objectID ID of the object metadata is for. 150 * @param string $metaKey Metadata key. 151 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 152 * @param mixed $prevValue Optional. Previous value to check before updating. 153 * If specified, only update existing metadata entries with 154 * this value. Otherwise, update all entries. 155 * 156 * @return int|bool The new meta field ID if a field with the given key didn't exist 157 * and was therefore added, true on successful update, 158 * false on failure or if the value passed to the function 159 * is the same as the one that is already in the database. 160 */ 161 function updateCommentMeta( $check, $objectID, $metaKey, $metaValue, $prevValue ) { 162 return $this->updateMeta( 'comment', $check, $objectID, $metaKey, $metaValue, $prevValue ); 163 } 164 165 /** 166 * Removes metadata matching criteria from a comment. 167 * Fires after WordPress meta removed 168 * 169 * @param null|bool $delete Whether to allow metadata deletion of the given type. 170 * @param int $objectID ID of the object metadata is for. 171 * @param string $metaKey Metadata key. 172 * @param mixed $metaValue Metadata value. 173 * @param bool $deleteAll Whether to delete the matching metadata entries 174 * for all objects, ignoring the specified $object_id. 175 * Default false. 176 * 177 * @return null|bool return $delete value 178 */ 179 function deleteCommentMeta( $delete, $objectID, $metaKey, $metaValue, $deleteAll ) { 180 $this->deleteMeta( 'comment', $objectID, $metaKey, $metaValue, $deleteAll ); 181 182 return $delete; 183 } 184 185 /** 186 * Adds a meta field to the given term. 187 * 188 * @param null|bool $check Whether to allow adding metadata for the given type. 189 * @param int $objectID ID of the object metadata is for. 190 * @param string $metaKey Metadata key. 191 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 192 * @param bool $unique Whether the specified meta key should be unique for the object. 193 * 194 * @return int|false The meta ID on success, false on failure. 195 */ 196 function addTermMeta( $check, $objectID, $metaKey, $metaValue, $unique ) { 197 return $this->addMeta( 'term', $check, $objectID, $metaKey, $metaValue, $unique ); 198 } 199 200 /** 201 * Updates a term meta field based on the given term ID. 202 * 203 * @param null|bool $check Whether to allow updating metadata for the given type. 204 * @param int $objectID ID of the object metadata is for. 205 * @param string $metaKey Metadata key. 206 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 207 * @param mixed $prevValue Optional. Previous value to check before updating. 208 * If specified, only update existing metadata entries with 209 * this value. Otherwise, update all entries. 210 * 211 * @return int|bool The new meta field ID if a field with the given key didn't exist 212 * and was therefore added, true on successful update, 213 * false on failure or if the value passed to the function 214 * is the same as the one that is already in the database. 215 */ 216 function updateTermMeta( $check, $objectID, $metaKey, $metaValue, $prevValue ) { 217 return $this->updateMeta( 'term', $check, $objectID, $metaKey, $metaValue, $prevValue ); 218 } 219 220 /** 221 * Removes metadata matching criteria from a term. 222 * Fires after WordPress meta removed 223 * 224 * @param null|bool $delete Whether to allow metadata deletion of the given type. 225 * @param int $objectID ID of the object metadata is for. 226 * @param string $metaKey Metadata key. 227 * @param mixed $metaValue Metadata value. 228 * @param bool $deleteAll Whether to delete the matching metadata entries 229 * for all objects, ignoring the specified $object_id. 230 * Default false. 231 * 232 * @return null|bool return $delete value 233 */ 234 function deleteTermMeta( $delete, $objectID, $metaKey, $metaValue, $deleteAll ) { 235 $this->deleteMeta( 'term', $objectID, $metaKey, $metaValue, $deleteAll ); 236 237 return $delete; 238 } 239 240 /** 241 * Adds a meta field to the given user. 242 * 243 * @param null|bool $check Whether to allow adding metadata for the given type. 244 * @param int $objectID ID of the object metadata is for. 245 * @param string $metaKey Metadata key. 246 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 247 * @param bool $unique Whether the specified meta key should be unique for the object. 248 * 249 * @return int|false The meta ID on success, false on failure. 250 */ 251 function addUserMeta( $check, $objectID, $metaKey, $metaValue, $unique ) { 252 return $this->addMeta( 'user', $check, $objectID, $metaKey, $metaValue, $unique ); 253 } 254 255 /** 256 * Updates a user meta field based on the given user ID. 257 * 258 * @param null|bool $check Whether to allow updating metadata for the given type. 259 * @param int $objectID ID of the object metadata is for. 260 * @param string $metaKey Metadata key. 261 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 262 * @param mixed $prevValue Optional. Previous value to check before updating. 263 * If specified, only update existing metadata entries with 264 * this value. Otherwise, update all entries. 265 * 266 * @return int|bool The new meta field ID if a field with the given key didn't exist 267 * and was therefore added, true on successful update, 268 * false on failure or if the value passed to the function 269 * is the same as the one that is already in the database. 270 */ 271 function updateUserMeta( $check, $objectID, $metaKey, $metaValue, $prevValue ) { 272 return $this->updateMeta( 'user', $check, $objectID, $metaKey, $metaValue, $prevValue ); 273 } 274 275 /** 276 * Removes metadata matching criteria from a user. 277 * Fires after WordPress meta removed 278 * 279 * @param null|bool $delete Whether to allow metadata deletion of the given type. 280 * @param int $objectID ID of the object metadata is for. 281 * @param string $metaKey Metadata key. 282 * @param mixed $metaValue Metadata value. 283 * @param bool $deleteAll Whether to delete the matching metadata entries 284 * for all objects, ignoring the specified $object_id. 285 * Default false. 286 * 287 * @return null|bool return $delete value 288 */ 289 function deleteUserMeta( $delete, $objectID, $metaKey, $metaValue, $deleteAll ) { 290 $this->deleteMeta( 'user', $objectID, $metaKey, $metaValue, $deleteAll ); 291 292 return $delete; 293 } 294 295 /** 296 * Retrieves raw metadata value for the specified object. 297 * 298 * @param mixed $value The value to return, either a single metadata value or an array 299 * of values depending on the value of `$single`. Default null. 300 * @param int $objectID ID of the object metadata is for. 301 * @param string $metaKey Metadata key. 302 * @param bool $single Whether to return only the first value of the specified `$metaKey`. 303 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 304 * or any other object type with an associated meta table. 305 * 306 * @return mixed An array of values if `$single` is false. 307 * The value of the meta field if `$single` is true. 308 * False for an invalid `$objectID` (non-numeric, zero, or negative value), 309 * or if `$metaType` is not specified. 310 * Null if the value does not exist. 311 */ 312 function getMeta( $value, $objectID, $metaKey, $single, $metaType ) { 313 global $wpdb; 314 315 //if ($metaKey === '') 316 // return $value; 317 318 if ( defined( 'IMPORT_PROCESS_WPMO' ) ) 319 return $value; 320 321 $tableName = $this->Helpers->getMetaTableName( $metaType ); 322 if ( ! $tableName ) 323 return $value; 324 325 if ( ! $this->Helpers->checkMetaType( $metaType ) ) 326 return $value; 327 328 if ( $metaType === 'post' && ! $this->Helpers->checkPostType( $objectID ) ) 329 return $value; 330 331 if ( $this->Helpers->checkInBlackWhiteList( $metaType, $metaKey, 'black_list' ) === true || $this->Helpers->checkInBlackWhiteList( $metaType, $metaKey, 'white_list' ) === false ) 332 return $value; 333 334 if ( substr( $metaKey, - ( strlen( $this->reservedKeysSuffix ) ) ) !== $this->reservedKeysSuffix ) 335 $metaKey = $this->Helpers->translateColumnName( $metaType, $metaKey ); 336 337 if ( ! $this->Helpers->checkColumnExists( $tableName, $metaType, $metaKey ) ) 338 return $value; 339 340 $metaRow = wp_cache_get( $tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY ); 341 if ( $metaRow === false ) { 342 $tableColumns = $this->Helpers->getTableColumns( $tableName, $metaType, true ); 343 $tableColumns = '`' . implode( '`,`', $tableColumns ) . '`'; 344 $sql = "SELECT {$tableColumns} FROM `{$tableName}` WHERE {$metaType}_id = {$objectID}"; 345 $metaRow = $wpdb->get_row( $sql, ARRAY_A ); 346 wp_cache_set( $tableName . '_' . $metaType . '_' . $objectID . '_row', $metaRow, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE ); 347 } 348 349 if ( is_array( $metaRow ) && isset( $metaRow[ $metaKey ] ) ) { 350 $metaValue = maybe_unserialize( $metaRow[ $metaKey ] ); 351 352 if ( $single && is_array( $metaValue ) && isset( $metaValue[0] ) ) 353 return $metaValue; 354 elseif ( $single && is_array( $metaValue ) ) 355 return array( $metaValue ); 356 else 357 return $metaValue; 358 } 359 360 return $value; 361 } 362 363 /** 364 * Adds metadata for the specified object. 365 * 366 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 367 * or any other object type with an associated meta table. 368 * @param null|bool $check Whether to allow adding metadata for the given type. 369 * @param int $objectID ID of the object metadata is for. 370 * @param string $metaKey Metadata key. 371 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 372 * @param bool $unique Whether the specified meta key should be unique for the object. 373 * 374 * @return int|false The meta ID on success, false on failure. 375 */ 376 function addMeta( $metaType, $check, $objectID, $metaKey, $metaValue, $unique ) { 377 if ( ! $this->Helpers->checkMetaType( $metaType ) ) 378 return $check; 379 380 if ( $metaType === 'post' && ! $this->Helpers->checkPostType( $objectID ) ) 381 return $check; 382 383 if ( $this->Helpers->checkInBlackWhiteList( $metaType, $metaKey, 'black_list' ) === true || $this->Helpers->checkInBlackWhiteList( $metaType, $metaKey, 'white_list' ) === false ) 384 return $check; 385 386 $metaKey = $this->Helpers->translateColumnName( $metaType, $metaKey ); 387 $metaValue = maybe_unserialize( $metaValue ); 388 389 $result = $this->Helpers->insertMeta( 390 [ 391 'metaType' => $metaType, 392 'objectID' => $objectID, 393 'metaKey' => $metaKey, 394 'metaValue' => $metaValue, 395 'unique' => $unique, 396 'addMeta' => true 397 ] 398 ); 399 400 $tableName = $this->Helpers->getMetaTableName( $metaType ); 401 wp_cache_delete( $tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY ); 402 403 return $this->Helpers->checkDontSaveInDefaultTable( $metaType ) ? $result : $check; 404 } 405 406 /** 407 * Updates metadata for the specified object. If no value already exists for the specified object 408 * ID and metadata key, the metadata will be added. 409 * 410 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 411 * or any other object type with an associated meta table. 412 * @param null|bool $check Whether to allow updating metadata for the given type. 413 * @param int $objectID ID of the object metadata is for. 414 * @param string $metaKey Metadata key. 415 * @param mixed $metaValue Metadata value. Must be serializable if non-scalar. 416 * @param mixed $prevValue Optional. Previous value to check before updating. 417 * If specified, only update existing metadata entries with 418 * this value. Otherwise, update all entries. Default empty. 419 * 420 * @return int|bool The new meta field ID if a field with the given key didn't exist 421 * and was therefore added, true on successful update, 422 * false on failure or if the value passed to the function 423 * is the same as the one that is already in the database. 424 * @global \wpdb $wpdb WordPress database abstraction object. 425 * 426 */ 427 function updateMeta( $metaType, $check, $objectID, $metaKey, $metaValue, $prevValue ) { 428 if ( ! $this->Helpers->checkMetaType( $metaType ) ) 429 return $check; 430 431 if ( $metaType === 'post' && ! $this->Helpers->checkPostType( $objectID ) ) 432 return $check; 433 434 if ( $this->Helpers->checkInBlackWhiteList( $metaType, $metaKey, 'black_list' ) === true || $this->Helpers->checkInBlackWhiteList( $metaType, $metaKey, 'white_list' ) === false ) 435 return $check; 436 437 $metaKey = $this->Helpers->translateColumnName( $metaType, $metaKey ); 438 $metaValue = maybe_unserialize( $metaValue ); 439 440 $result = $this->Helpers->insertMeta( 441 [ 442 'metaType' => $metaType, 443 'objectID' => $objectID, 444 'metaKey' => $metaKey, 445 'metaValue' => $metaValue, 446 'unique' => false, 447 'prevValue' => $prevValue 448 ] 449 ); 450 451 $tableName = $this->Helpers->getMetaTableName( $metaType ); 452 wp_cache_delete( $tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY ); 453 454 return $this->Helpers->checkDontSaveInDefaultTable( $metaType ) ? $result : $check; 455 } 456 457 /** 458 * Deletes metadata for the specified object. 459 * 460 * @param string $metaType Type of object metadata is for. Accepts 'post', 'comment', 'term', 'user', 461 * or any other object type with an associated meta table. 462 * @param int $objectID ID of the object metadata is for. 463 * @param string $metaKey Metadata key. 464 * @param mixed $metaValue Metadata value. 465 * @param bool $deleteAll Whether to delete the matching metadata entries 466 * for all objects, ignoring the specified $objectID. 467 * 468 * @return boolean|int 469 */ 470 471 private function deleteMeta( $metaType, $objectID, $metaKey, $metaValue, $deleteAll ) { 472 global $wpdb; 473 474 $tableName = $this->Helpers->getMetaTableName( $metaType ); 475 if ( ! $tableName ) 476 return false; 477 478 $column = sanitize_key( $metaType . '_id' ); 479 480 $metaKey = $this->Helpers->translateColumnName( $metaType, $metaKey ); 481 482 $newValue = null; 483 484 if ( ! $deleteAll && '' !== $metaValue && null !== $metaValue && false !== $metaValue ) { 485 $metaValue = maybe_unserialize( $metaValue ); 486 $newValue = $currentValue = $this->getMeta( $newValue, $objectID, $metaKey, false, $metaType ); 487 if ( is_array( $currentValue ) && ( $indexValue = array_search( $metaValue, $currentValue, false ) ) !== false ) { 488 unset( $currentValue[ $indexValue ] ); 489 $newValue = $currentValue; 490 } 491 492 $newValue = maybe_serialize( $newValue ); 493 } 494 495 $result = $wpdb->update( 496 $tableName, 497 [ $metaKey => $newValue, 'updated_at' => $this->now ], 498 [ $column => $objectID ] 499 ); 500 501 wp_cache_delete( $tableName . '_' . $metaType . '_' . $objectID . '_row', WPMETAOPTIMIZER_PLUGIN_KEY ); 502 503 return $result; 504 } 505 506 /** 507 * Returns an instance of class 508 * 509 * @return WPMetaOptimizer 510 */ 511 static function getInstance() { 512 if ( self::$instance == null ) 513 self::$instance = new WPMetaOptimizer(); 514 515 return self::$instance; 516 } 514 517 } 515 518 516 519 WPMetaOptimizer::getInstance(); 517 register_activation_hook( __FILE__, array('WPMetaOptimizer\Install', 'install'));520 register_activation_hook( __FILE__, array( 'WPMetaOptimizer\Install', 'install' ) ); -
meta-optimizer/trunk/assets/wpmo.js
r2802191 r2960316 1 2 1 jQuery(function ($) { 3 2 $('.tooltip-title').hover(function (e) { // Hover event … … 66 65 67 66 if (confirm(wpmoObject.deleteColumnMessage + "\n" + $this.data('column'))) { 68 if ($metaTable == 'origin' && !confirm(wpmoObject.deleteOriginMetaMessage + "\n" + $this.data('column')))67 if ($metaTable === 'origin' && !confirm(wpmoObject.deleteOriginMetaMessage + "\n" + $this.data('column'))) 69 68 return; 70 69 … … 102 101 103 102 if ($newName != null && $newName != '' && $newName !== $oldName && confirm(wpmoObject.renameConfirmColumnMessage + "\n" + wpmoObject.oldName + ': ' + $oldName + "\n" + wpmoObject.newName + ': ' + $newName)) { 104 if ($metaTable == 'origin' && !confirm(wpmoObject.renameConfirmOriginMetaMessage + "\n" + wpmoObject.oldName + ': ' + $oldName + "\n" + wpmoObject.newName + ': ' + $newName))103 if ($metaTable === 'origin' && !confirm(wpmoObject.renameConfirmOriginMetaMessage + "\n" + wpmoObject.oldName + ': ' + $oldName + "\n" + wpmoObject.newName + ': ' + $newName)) 105 104 return; 106 105 -
meta-optimizer/trunk/inc/Actions.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 class Actions extends Base 6 { 7 public static $instance = null; 8 protected $Helpers, $Options; 9 10 function __construct() 11 { 12 $this->Helpers = Helpers::getInstance(); 13 $this->Options = Options::getInstance(); 14 15 add_action('wp_ajax_wpmo_delete_table_column', [$this, 'deleteTableColumn']); 16 add_action('wp_ajax_wpmo_rename_table_column', [$this, 'renameTableColumn']); 17 add_action('wp_ajax_wpmo_add_remove_black_list', [$this, 'addRemoveBlackList']); 18 19 add_action('deleted_post', [$this, 'deletePostMetas']); 20 add_action('deleted_comment', [$this, 'deleteCommentMetas']); 21 add_action('deleted_user', [$this, 'deleteUserMetas']); 22 add_action('delete_term', [$this, 'deleteTermMetas']); 23 24 add_filter('cron_schedules', [$this, 'addIntervalToCron']); 25 add_action('init', [$this, 'initScheduler']); 26 add_action('import_metas_wpmo', [$this, 'importMetas']); 27 add_action('admin_enqueue_scripts', [$this, 'enqueueScripts']); 28 add_filter('plugin_action_links_' . plugin_basename(WPMETAOPTIMIZER_PLUGIN_FILE_PATH), array($this, 'addPluginActionLinks'), 10, 4); 29 } 30 31 /** 32 * Delete post metas after delete post 33 * 34 * @param int $postID Post ID 35 */ 36 function deletePostMetas($postID) 37 { 38 $this->Helpers->deleteMetaRow($postID, 'post'); 39 } 40 41 /** 42 * Delete comment metas after delete comment 43 * 44 * @param int $commentID Comment ID 45 */ 46 function deleteCommentMetas($commentID) 47 { 48 $this->Helpers->deleteMetaRow($commentID, 'comment'); 49 } 50 51 /** 52 * Delete user metas after delete user 53 * 54 * @param int $userID User ID 55 */ 56 function deleteUserMetas($userID) 57 { 58 $this->Helpers->deleteMetaRow($userID, 'user'); 59 } 60 61 /** 62 * Delete term metas after delete term 63 * 64 * @param int $termID Term ID 65 */ 66 function deleteTermMetas($termID) 67 { 68 $this->Helpers->deleteMetaRow($termID, 'term'); 69 } 70 71 /** 72 * Add or remove meta key from black list 73 */ 74 function addRemoveBlackList() 75 { 76 if (current_user_can('manage_options') && check_admin_referer('wpmo_ajax_nonce', 'nonce')) { 77 $type = sanitize_text_field($_POST['type']); 78 $column = sanitize_text_field($_POST['column']); 79 $listAction = sanitize_text_field($_POST['list_action']); 80 81 $table = $this->Helpers->getMetaTableName($type); 82 if ($table && in_array($listAction, ['insert', 'remove'])) { 83 $list = $this->Options->getOption($type . '_black_list', ''); 84 $list = explode("\n", $list); 85 $list = str_replace(["\n", "\r"], '', $list); 86 $list = array_map('trim', $list); 87 $listCount = count($list); 88 $listItemInex = array_search($column, $list); 89 $newAction = $listAction === 'insert' ? 'remove' : 'insert'; 90 91 if ($listAction === 'insert' && $listItemInex === false) 92 $list[] = $column; 93 elseif ($listAction === 'remove' && $listItemInex !== false) 94 unset($list[$listItemInex]); 95 96 if (count($list) !== $listCount) { 97 $list = implode("\n", $list); 98 $list = trim($list, "\n"); 99 $this->Options->setOption($type . '_black_list', $list); 100 wp_send_json_success(['newAction' => $newAction, 'list' => $list]); 101 } 102 } 103 104 wp_send_json_error(); 105 } 106 } 107 108 /** 109 * Rename meta key and table column 110 */ 111 function renameTableColumn() 112 { 113 global $wpdb; 114 if (current_user_can('manage_options') && check_admin_referer('wpmo_ajax_nonce', 'nonce')) { 115 $type = sanitize_text_field($_POST['type']); 116 $column = wp_unslash(sanitize_text_field($_POST['column'])); 117 $newColumnName = $_newColumnName = wp_unslash(sanitize_text_field($_POST['newColumnName'])); 118 $metaTable = sanitize_text_field($_POST['meta_table']); 119 $collate = ''; 120 121 $renameOriginMetaKey = false; 122 if ($metaTable == 'origin' && $this->Helpers->checkCanChangeWPMetaKey($type, $column)) { 123 $checkMetaKeyExists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = '%s';", $newColumnName)); 124 125 if ($checkMetaKeyExists) 126 wp_send_json_error(); 127 else 128 $renameOriginMetaKey = $wpdb->query($wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_key = '%s' WHERE meta_key = '%s';", $newColumnName, $column)); 129 } 130 131 $table = $this->Helpers->getMetaTableName($type); 132 $column = $this->Helpers->translateColumnName($type, $column); 133 $newColumnName = $this->Helpers->translateColumnName($type, $newColumnName); 134 135 if (($metaTable == 'origin' && $renameOriginMetaKey || $metaTable == 'plugin') && $table && $this->Helpers->checkColumnExists($table, $type, $column) && !$this->Helpers->checkColumnExists($table, $type, $newColumnName)) { 136 $currentColumnType = $this->Helpers->getTableColumnType($table, $column); 137 138 if (in_array($currentColumnType, $this->charTypes)) 139 $collate = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci'; 140 141 if ($currentColumnType == 'VARCHAR') { 142 $currentFieldMaxLengthValue = intval($wpdb->get_var("SELECT MAX(LENGTH({$column})) as length FROM {$table}")); 143 $currentColumnType = 'VARCHAR(' . $currentFieldMaxLengthValue . ')'; 144 } 145 146 $sql = "ALTER TABLE `{$table}` CHANGE `{$column}` `{$newColumnName}` {$currentColumnType} {$collate} NULL DEFAULT NULL"; 147 $result = $wpdb->query($sql); 148 149 if ($result) 150 wp_send_json_success(['blackListAction' => $this->Helpers->checkInBlackWhiteList($type, $_newColumnName) ? 'insert' : 'remove']); 151 } 152 153 wp_send_json_error(); 154 } 155 } 156 157 /** 158 * Delete meta key and table column 159 */ 160 function deleteTableColumn() 161 { 162 global $wpdb; 163 if (current_user_can('manage_options') && check_admin_referer('wpmo_ajax_nonce', 'nonce')) { 164 $type = sanitize_text_field($_POST['type']); 165 $column = wp_unslash(sanitize_text_field($_POST['column'])); 166 $metaTable = sanitize_text_field($_POST['meta_table']); 167 168 $deleteOriginMetaKey = false; 169 if ($metaTable == 'origin' && $this->Helpers->checkCanChangeWPMetaKey($type, $column)) 170 $deleteOriginMetaKey = delete_metadata($type, null, $column, '', true); 171 172 $table = $this->Helpers->getMetaTableName($type); 173 $column = $this->Helpers->translateColumnName($type, $column); 174 175 if (($metaTable == 'origin' && $deleteOriginMetaKey || $metaTable == 'plugin') && $table && $this->Helpers->checkColumnExists($table, $type, $column)) { 176 $result = $wpdb->query("ALTER TABLE `{$table}` DROP COLUMN `{$column}`"); 177 if ($result) 178 wp_send_json_success(); 179 } 180 181 wp_send_json_error(); 182 } 183 } 184 185 /** 186 * Import meta scheduler 187 */ 188 function importMetas() 189 { 190 define('IMPORT_PROCESS_WPMO', true); 191 192 $importTables = $this->Options->getOption('import', []); 193 if (isset($importTables['hidden'])) 194 unset($importTables['hidden']); 195 if (is_array($importTables) && count($importTables)) 196 $importTables = array_keys($importTables); 197 198 $importItemsNumber = intval($this->Options->getOption('import_items_number', 1)); 199 if (!$importItemsNumber) 200 $importItemsNumber = 1; 201 202 foreach ($importTables as $type) { 203 if (!$this->Helpers->checkMetaType($type)) 204 continue; 205 206 $latestObjectID = $this->Options->getOption('import_' . $type . '_latest_id', null, false); 207 208 if ($latestObjectID === 'finished') 209 continue; 210 211 $objectID_ = $objectID = false; 212 for ($c = 1; $c <= $importItemsNumber; $c++) { 213 $objectID = $this->Helpers->getLatestObjectID($type, $c == 1 ? $latestObjectID : $objectID); 214 215 if (!is_null($objectID)) { 216 $objectID = $objectID_ = intval($objectID); 217 218 $objectMetas = get_metadata($type, $objectID); 219 220 foreach ($objectMetas as $metaKey => $metaValue) { 221 if ($this->Helpers->checkInBlackWhiteList($type, $metaKey, 'black_list') === true || $this->Helpers->checkInBlackWhiteList($type, $metaKey, 'white_list') === false) 222 continue; 223 224 $metaKey = $this->Helpers->translateColumnName($type, $metaKey); 225 226 if (is_array($metaValue) && count($metaValue) === 1) 227 $metaValue = current($metaValue); 228 229 $this->Helpers->insertMeta( 230 [ 231 'metaType' => $type, 232 'objectID' => $objectID, 233 'metaKey' => $metaKey, 234 'metaValue' => $metaValue, 235 'checkCurrentValue' => false 236 ] 237 ); 238 } 239 } else { 240 $this->Options->setOption('import_' . $type . '_latest_id', 'finished'); 241 break; 242 } 243 } 244 245 if ($objectID_ && !is_null($objectID)) 246 $this->Options->setOption('import_' . $type . '_latest_id', $objectID_); 247 248 $this->Options->setOption('import_' . $type . '_checked_date', date('Y-m-d H:i:s')); 249 } 250 251 $this->Helpers->activeAutomaticallySupportWPQuery(); 252 } 253 254 /** 255 * Add schedule event 256 */ 257 function initScheduler() 258 { 259 if (!wp_next_scheduled('import_metas_wpmo')) 260 wp_schedule_event(time(), 'every_1_minutes', 'import_metas_wpmo'); 261 } 262 263 /** 264 * Add custom interval to WP cron 265 */ 266 function addIntervalToCron($schedules) 267 { 268 $i = 1; 269 if (!isset($schedules['every_' . $i . '_minutes'])) { 270 $title = "Every %d Minutes"; 271 $schedules['every_' . $i . '_minutes'] = array( 272 'interval' => $i * 60, 273 'display' => sprintf($title, $i) 274 ); 275 } 276 277 return $schedules; 278 } 279 280 /** 281 * Register plugin js/css 282 */ 283 function enqueueScripts() 284 { 285 if (!function_exists('get_plugin_data')) 286 require_once(ABSPATH . 'wp-admin/includes/plugin.php'); 287 $pluginData = get_plugin_data(WPMETAOPTIMIZER_PLUGIN_FILE_PATH); 288 $pluginVersion = $pluginData['Version']; 289 290 wp_enqueue_style(WPMETAOPTIMIZER_PLUGIN_KEY, plugin_dir_url(dirname(__FILE__)) . 'assets/style.min.css', array(), $pluginVersion, false); 291 wp_enqueue_script( 292 WPMETAOPTIMIZER_PLUGIN_KEY, 293 plugin_dir_url(dirname(__FILE__)) . 'assets/wpmo.js', 294 array('jquery'), 295 $pluginVersion, 296 true 297 ); 298 wp_localize_script(WPMETAOPTIMIZER_PLUGIN_KEY, 'wpmoObject', array( 299 'ajaxurl' => admin_url('admin-ajax.php'), 300 'nonce' => wp_create_nonce('wpmo_ajax_nonce'), 301 'deleteColumnMessage' => __('Are you sure you want to delete this column?', 'meta-optimizer'), 302 'deleteOriginMetaMessage' => __('Second confirmation, Are you sure you want to delete this meta?', 'meta-optimizer'), 303 'renamePromptColumnMessage' => __('Enter new column name', 'meta-optimizer'), 304 'renameConfirmColumnMessage' => __('Are you sure you want to rename this column?', 'meta-optimizer'), 305 'renameConfirmOriginMetaMessage' => __('Second confirmation, Are you sure you want to rename this meta?', 'meta-optimizer'), 306 'oldName' => __('Old name', 'meta-optimizer'), 307 'newName' => __('New name', 'meta-optimizer'), 308 'removeFromBlackList' => __('Remove from black list', 'meta-optimizer'), 309 'addToBlackList' => __('Add to black list', 'meta-optimizer') 310 )); 311 } 312 313 /** 314 * Add action links to plugin section in WP plugins admin page 315 */ 316 function addPluginActionLinks($actions) 317 { 318 $actions[] = '<a href="' . admin_url('options-general.php?page=' . WPMETAOPTIMIZER_PLUGIN_KEY) . '">' . __('Settings') . '</a>'; 319 return $actions; 320 } 321 322 /** 323 * Returns an instance of class 324 * @return Actions 325 */ 326 static function getInstance() 327 { 328 if (self::$instance == null) 329 self::$instance = new Actions(); 330 331 return self::$instance; 332 } 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 7 8 class Actions extends Base { 9 public static $instance = null; 10 protected $Helpers, $Options; 11 12 function __construct() { 13 $this->Helpers = Helpers::getInstance(); 14 $this->Options = Options::getInstance(); 15 16 add_action( 'wp_ajax_wpmo_delete_table_column', [ $this, 'deleteTableColumn' ] ); 17 add_action( 'wp_ajax_wpmo_rename_table_column', [ $this, 'renameTableColumn' ] ); 18 add_action( 'wp_ajax_wpmo_add_remove_black_list', [ $this, 'addRemoveBlackList' ] ); 19 20 add_action( 'deleted_post', [ $this, 'deletePostMetas' ] ); 21 add_action( 'deleted_comment', [ $this, 'deleteCommentMetas' ] ); 22 add_action( 'deleted_user', [ $this, 'deleteUserMetas' ] ); 23 add_action( 'delete_term', [ $this, 'deleteTermMetas' ] ); 24 25 add_filter( 'cron_schedules', [ $this, 'addIntervalToCron' ] ); 26 add_action( 'init', [ $this, 'initScheduler' ] ); 27 add_action( 'import_metas_wpmo', [ $this, 'importMetas' ] ); 28 add_action( 'admin_enqueue_scripts', [ $this, 'enqueueScripts' ] ); 29 add_filter( 'plugin_action_links_' . plugin_basename( WPMETAOPTIMIZER_PLUGIN_FILE_PATH ), array( 30 $this, 31 'addPluginActionLinks' 32 ), 10, 4 ); 33 } 34 35 /** 36 * Delete post metas after delete post 37 * 38 * @param int $postID Post ID 39 */ 40 function deletePostMetas( $postID ) { 41 $this->Helpers->deleteMetaRow( $postID, 'post' ); 42 } 43 44 /** 45 * Delete comment metas after delete comment 46 * 47 * @param int $commentID Comment ID 48 */ 49 function deleteCommentMetas( $commentID ) { 50 $this->Helpers->deleteMetaRow( $commentID, 'comment' ); 51 } 52 53 /** 54 * Delete user metas after delete user 55 * 56 * @param int $userID User ID 57 */ 58 function deleteUserMetas( $userID ) { 59 $this->Helpers->deleteMetaRow( $userID, 'user' ); 60 } 61 62 /** 63 * Delete term metas after delete term 64 * 65 * @param int $termID Term ID 66 */ 67 function deleteTermMetas( $termID ) { 68 $this->Helpers->deleteMetaRow( $termID, 'term' ); 69 } 70 71 /** 72 * Add or remove meta key from black list 73 */ 74 function addRemoveBlackList() { 75 if ( current_user_can( 'manage_options' ) && check_admin_referer( 'wpmo_ajax_nonce', 'nonce' ) ) { 76 $type = sanitize_text_field( $_POST['type'] ); 77 $column = sanitize_text_field( $_POST['column'] ); 78 $listAction = sanitize_text_field( $_POST['list_action'] ); 79 80 $table = $this->Helpers->getMetaTableName( $type ); 81 if ( $table && in_array( $listAction, [ 'insert', 'remove' ] ) ) { 82 $list = $this->Options->getOption( $type . '_black_list', '' ); 83 $list = explode( "\n", $list ); 84 $list = str_replace( [ "\n", "\r" ], '', $list ); 85 $list = array_map( 'trim', $list ); 86 $listCount = count( $list ); 87 $listItemInex = array_search( $column, $list ); 88 $newAction = $listAction === 'insert' ? 'remove' : 'insert'; 89 90 if ( $listAction === 'insert' && $listItemInex === false ) 91 $list[] = $column; 92 elseif ( $listAction === 'remove' && $listItemInex !== false ) 93 unset( $list[ $listItemInex ] ); 94 95 if ( count( $list ) !== $listCount ) { 96 $list = implode( "\n", $list ); 97 $list = trim( $list, "\n" ); 98 $this->Options->setOption( $type . '_black_list', $list ); 99 wp_send_json_success( [ 'newAction' => $newAction, 'list' => $list ] ); 100 } 101 } 102 103 wp_send_json_error(); 104 } 105 } 106 107 /** 108 * Rename meta key and table column 109 */ 110 function renameTableColumn() { 111 global $wpdb; 112 if ( current_user_can( 'manage_options' ) && check_admin_referer( 'wpmo_ajax_nonce', 'nonce' ) ) { 113 $type = sanitize_text_field( $_POST['type'] ); 114 $column = wp_unslash( sanitize_text_field( $_POST['column'] ) ); 115 $newColumnName = $_newColumnName = wp_unslash( sanitize_text_field( $_POST['newColumnName'] ) ); 116 $metaTable = sanitize_text_field( $_POST['meta_table'] ); 117 $collate = ''; 118 119 $renameOriginMetaKey = false; 120 if ( $metaTable == 'origin' && $this->Helpers->checkCanChangeWPMetaKey( $type, $column ) ) { 121 $checkMetaKeyExists = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = '%s';", $newColumnName ) ); 122 123 if ( $checkMetaKeyExists ) 124 wp_send_json_error(); 125 else 126 $renameOriginMetaKey = $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_key = '%s' WHERE meta_key = '%s';", $newColumnName, $column ) ); 127 } 128 129 $table = $this->Helpers->getMetaTableName( $type ); 130 $column = $this->Helpers->translateColumnName( $type, $column ); 131 $newColumnName = $this->Helpers->translateColumnName( $type, $newColumnName ); 132 133 if ( ( $metaTable == 'origin' && $renameOriginMetaKey || $metaTable == 'plugin' ) && $table && $this->Helpers->checkColumnExists( $table, $type, $column ) && ! $this->Helpers->checkColumnExists( $table, $type, $newColumnName ) ) { 134 $currentColumnType = $this->Helpers->getTableColumnType( $table, $column ); 135 136 if ( in_array( $currentColumnType, $this->charTypes ) ) 137 $collate = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci'; 138 139 if ( $currentColumnType == 'VARCHAR' ) { 140 $currentFieldMaxLengthValue = intval( $wpdb->get_var( "SELECT MAX(LENGTH({$column})) as length FROM {$table}" ) ); 141 $currentColumnType = 'VARCHAR(' . $currentFieldMaxLengthValue . ')'; 142 } 143 144 $sql = "ALTER TABLE `{$table}` CHANGE `{$column}` `{$newColumnName}` {$currentColumnType} {$collate} NULL DEFAULT NULL"; 145 $result = $wpdb->query( $sql ); 146 147 if ( $result ) 148 wp_send_json_success( [ 'blackListAction' => $this->Helpers->checkInBlackWhiteList( $type, $_newColumnName ) ? 'insert' : 'remove' ] ); 149 } 150 151 wp_send_json_error(); 152 } 153 } 154 155 /** 156 * Delete meta key and table column 157 */ 158 function deleteTableColumn() { 159 global $wpdb; 160 if ( current_user_can( 'manage_options' ) && check_admin_referer( 'wpmo_ajax_nonce', 'nonce' ) ) { 161 $type = sanitize_text_field( $_POST['type'] ); 162 $column = wp_unslash( sanitize_text_field( $_POST['column'] ) ); 163 $metaTable = sanitize_text_field( $_POST['meta_table'] ); 164 165 $deleteOriginMetaKey = false; 166 if ( $metaTable == 'origin' && $this->Helpers->checkCanChangeWPMetaKey( $type, $column ) ) 167 $deleteOriginMetaKey = delete_metadata( $type, null, $column, '', true ); 168 169 $table = $this->Helpers->getMetaTableName( $type ); 170 $column = $this->Helpers->translateColumnName( $type, $column ); 171 172 if ( ( $metaTable == 'origin' && $deleteOriginMetaKey || $metaTable == 'plugin' ) && $table && $this->Helpers->checkColumnExists( $table, $type, $column ) ) { 173 $result = $wpdb->query( "ALTER TABLE `{$table}` DROP COLUMN `{$column}`" ); 174 if ( $result ) 175 wp_send_json_success(); 176 } 177 178 wp_send_json_error(); 179 } 180 } 181 182 /** 183 * Import meta scheduler 184 */ 185 function importMetas() { 186 define( 'IMPORT_PROCESS_WPMO', true ); 187 188 $importTables = $this->Options->getOption( 'import', [] ); 189 if ( isset( $importTables['hidden'] ) ) 190 unset( $importTables['hidden'] ); 191 if ( is_array( $importTables ) && count( $importTables ) ) 192 $importTables = array_keys( $importTables ); 193 194 $importItemsNumber = intval( $this->Options->getOption( 'import_items_number', 1 ) ); 195 if ( ! $importItemsNumber ) 196 $importItemsNumber = 1; 197 198 foreach ( $importTables as $type ) { 199 if ( ! $this->Helpers->checkMetaType( $type ) ) 200 continue; 201 202 $latestObjectID = $this->Options->getOption( 'import_' . $type . '_latest_id', null, false ); 203 204 if ( $latestObjectID === 'finished' ) 205 continue; 206 207 $objectID_ = $objectID = false; 208 for ( $c = 1; $c <= $importItemsNumber; $c ++ ) { 209 $objectID = $this->Helpers->getLatestObjectID( $type, $c == 1 ? $latestObjectID : $objectID ); 210 211 if ( ! is_null( $objectID ) ) { 212 $objectID = $objectID_ = intval( $objectID ); 213 214 $objectMetas = get_metadata( $type, $objectID ); 215 216 foreach ( $objectMetas as $metaKey => $metaValue ) { 217 if ( $this->Helpers->checkInBlackWhiteList( $type, $metaKey, 'black_list' ) === true || $this->Helpers->checkInBlackWhiteList( $type, $metaKey, 'white_list' ) === false ) 218 continue; 219 220 $metaKey = $this->Helpers->translateColumnName( $type, $metaKey ); 221 $metaValue = maybe_unserialize( $metaValue ); 222 223 if ( is_array( $metaValue ) && count( $metaValue ) === 1 ) 224 $metaValue = maybe_unserialize( current( $metaValue ) ); 225 226 $this->Helpers->insertMeta( 227 [ 228 'metaType' => $type, 229 'objectID' => $objectID, 230 'metaKey' => $metaKey, 231 'metaValue' => $metaValue, 232 'checkCurrentValue' => false 233 ] 234 ); 235 } 236 } else { 237 $this->Options->setOption( 'import_' . $type . '_latest_id', 'finished' ); 238 break; 239 } 240 } 241 242 if ( $objectID_ && ! is_null( $objectID ) ) 243 $this->Options->setOption( 'import_' . $type . '_latest_id', $objectID_ ); 244 245 $this->Options->setOption( 'import_' . $type . '_checked_date', date( 'Y-m-d H:i:s' ) ); 246 } 247 248 $this->Helpers->activeAutomaticallySupportWPQuery(); 249 } 250 251 /** 252 * Add schedule event 253 */ 254 function initScheduler() { 255 if ( ! wp_next_scheduled( 'import_metas_wpmo' ) ) 256 wp_schedule_event( time(), 'every_1_minutes', 'import_metas_wpmo' ); 257 } 258 259 /** 260 * Add a custom interval to WP cron 261 */ 262 function addIntervalToCron( $schedules ) { 263 $i = 1; 264 if ( ! isset( $schedules[ 'every_' . $i . '_minutes' ] ) ) { 265 $title = "Every %d Minutes"; 266 $schedules[ 'every_' . $i . '_minutes' ] = array( 267 'interval' => $i * 60, 268 'display' => sprintf( $title, $i ) 269 ); 270 } 271 272 return $schedules; 273 } 274 275 /** 276 * Register plugin js/css 277 */ 278 function enqueueScripts() { 279 if ( ! function_exists( 'get_plugin_data' ) ) 280 require_once( ABSPATH . 'wp-admin/includes/plugin.php' ); 281 $pluginData = get_plugin_data( WPMETAOPTIMIZER_PLUGIN_FILE_PATH ); 282 $pluginVersion = $pluginData['Version']; 283 284 wp_enqueue_style( WPMETAOPTIMIZER_PLUGIN_KEY, plugin_dir_url( dirname( __FILE__ ) ) . 'assets/style.min.css', array(), $pluginVersion, false ); 285 wp_enqueue_script( 286 WPMETAOPTIMIZER_PLUGIN_KEY, 287 plugin_dir_url( dirname( __FILE__ ) ) . 'assets/wpmo.js', 288 array( 'jquery' ), 289 $pluginVersion, 290 true 291 ); 292 wp_localize_script( WPMETAOPTIMIZER_PLUGIN_KEY, 'wpmoObject', array( 293 'ajaxurl' => admin_url( 'admin-ajax.php' ), 294 'nonce' => wp_create_nonce( 'wpmo_ajax_nonce' ), 295 'deleteColumnMessage' => __( 'Are you sure you want to delete this column?', 'meta-optimizer' ), 296 'deleteOriginMetaMessage' => __( 'Second confirmation, Are you sure you want to delete this meta?', 'meta-optimizer' ), 297 'renamePromptColumnMessage' => __( 'Enter new column name', 'meta-optimizer' ), 298 'renameConfirmColumnMessage' => __( 'Are you sure you want to rename this column?', 'meta-optimizer' ), 299 'renameConfirmOriginMetaMessage' => __( 'Second confirmation, Are you sure you want to rename this meta?', 'meta-optimizer' ), 300 'oldName' => __( 'Old name', 'meta-optimizer' ), 301 'newName' => __( 'New name', 'meta-optimizer' ), 302 'removeFromBlackList' => __( 'Remove from black list', 'meta-optimizer' ), 303 'addToBlackList' => __( 'Add to black list', 'meta-optimizer' ) 304 ) ); 305 } 306 307 /** 308 * Add action links to a plugin section in WP plugins admin page 309 */ 310 function addPluginActionLinks( $actions ) { 311 $actions[] = '<a href="' . admin_url( 'options-general.php?page=' . WPMETAOPTIMIZER_PLUGIN_KEY ) . '">' . __( 'Settings' ) . '</a>'; 312 313 return $actions; 314 } 315 316 /** 317 * Returns an instance of class 318 * 319 * @return Actions 320 */ 321 static function getInstance() { 322 if ( self::$instance == null ) 323 self::$instance = new Actions(); 324 325 return self::$instance; 326 } 333 327 } -
meta-optimizer/trunk/inc/Base.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 class Base 6 { 7 public $now, $tables, $wpMetaTables, 8 $intTypes = ['TINYINT', 'SMALLINT', 'MEDIUMINT', 'INT', 'BIGINT'], 9 $floatTypes = ['FLOAT', 'DOUBLE', 'DECIMAL'], 10 $charTypes = ['CHAR', 'VARCHAR', 'TINYTEXT', 'TEXT', 'MEDIUMTEXT', 'LONGTEXT'], 11 $dateTypes = ['DATE', 'DATETIME', 'TIMESTAMP', 'TIME', 'YEAR'], 12 $ignoreTableColumns = ['meta_id', 'created_at', 'updated_at'], 13 $ignorePostTypes = ['wp_block', 'wp_navigation', 'acf-field-group'], 14 $ignoreWPMetaKeys = array( 15 'post' => ['_edit_lock', '_edit_last'], 16 'comment' => [], 17 'user' => ['session_tokens', 'wp_capabilities'], 18 'term' => [] 19 ), 20 $cantChangeWPMetaKeys = array( 21 'post' => ['_thumbnail_id', '_encloseme', '_wp_old_slug', '_pingme', '_wp_page_template'], 22 'comment' => [], 23 'user' => [ 24 'session_tokens', 'wp_capabilities', 'admin_color', 'community-events-location', 25 'comment_shortcuts', 'first_name', 'last_name', 'nickname', 'description', 26 'locale', 'metaboxhidden_nav-menus', 'nav_menu_recently_edited', 27 'show_admin_bar_front', 'syntax_highlighting', 'show_welcome_panel', 28 'use_ssl', 'wp_dashboard_quick_press_last_post_id', 'wp_user-settings', 'wp_user-settings-time', 29 'wp_user_level', 'rich_editing', 'managenav-menuscolumnshidden', 'dismissed_wp_pointers' 30 ], 31 'term' => [] 32 ), 33 $reservedKeysSuffix = '_wpmork'; 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 34 7 35 function __construct() 36 { 37 global $wpdb; 8 class Base { 9 public $now, $tables, $wpMetaTables, 10 $intTypes = [ 'TINYINT', 'SMALLINT', 'MEDIUMINT', 'INT', 'BIGINT' ], 11 $floatTypes = [ 'FLOAT', 'DOUBLE', 'DECIMAL' ], 12 $charTypes = [ 'CHAR', 'VARCHAR', 'TINYTEXT', 'TEXT', 'MEDIUMTEXT', 'LONGTEXT' ], 13 $dateTypes = [ 'DATE', 'DATETIME', 'TIMESTAMP', 'TIME', 'YEAR' ], 14 $ignoreTableColumns = [ 'meta_id', 'created_at', 'updated_at' ], 15 $ignorePostTypes = [ 'wp_block', 'wp_navigation', 'acf-field-group' ], 16 $ignoreWPMetaKeys = array( 17 'post' => [ '_edit_lock', '_edit_last' ], 18 'comment' => [], 19 'user' => [ 'session_tokens', 'wp_capabilities' ], 20 'term' => [] 21 ), 22 $cantChangeWPMetaKeys = array( 23 'post' => [ '_thumbnail_id', '_encloseme', '_wp_old_slug', '_pingme', '_wp_page_template' ], 24 'comment' => [], 25 'user' => [ 26 'session_tokens', 27 'wp_capabilities', 28 'admin_color', 29 'community-events-location', 30 'comment_shortcuts', 31 'first_name', 32 'last_name', 33 'nickname', 34 'description', 35 'locale', 36 'metaboxhidden_nav-menus', 37 'nav_menu_recently_edited', 38 'show_admin_bar_front', 39 'syntax_highlighting', 40 'show_welcome_panel', 41 'use_ssl', 42 'wp_dashboard_quick_press_last_post_id', 43 'wp_user-settings', 44 'wp_user-settings-time', 45 'wp_user_level', 46 'rich_editing', 47 'managenav-menuscolumnshidden', 48 'dismissed_wp_pointers' 49 ], 50 'term' => [] 51 ), 52 $reservedKeysSuffix = '_wpmork'; 38 53 39 $this->now = current_time('mysql'); 54 function __construct() { 55 global $wpdb; 40 56 41 $this->wpPrimaryTables = array( 42 'post' => $wpdb->posts, 43 'comment' => $wpdb->comments, 44 'user' => $wpdb->users, 45 'term' => $wpdb->terms 46 ); 57 $this->now = current_time( 'mysql' ); 47 58 48 $this->wpMetaTables = array(49 'post' => $wpdb->postmeta,50 'comment' => $wpdb->commentmeta,51 'user' => $wpdb->usermeta,52 'term' => $wpdb->termmeta 53 );59 $this->wpPrimaryTables = array( 60 'post' => $wpdb->posts, 61 'comment' => $wpdb->comments, 62 'user' => $wpdb->users, 63 'term' => $wpdb->terms 64 ); 54 65 55 $this->tables = array( 56 'post' => [ 57 'table' => $wpdb->postmeta . '_wpmo', 58 'name' => __('Post'), 59 'title' => __('Post Meta', 'meta-optimizer') 60 ], 61 'comment' => [ 62 'table' => $wpdb->commentmeta . '_wpmo', 63 'name' => __('Comment'), 64 'title' => __('Comment Meta', 'meta-optimizer') 65 ], 66 'user' => [ 67 'table' => $wpdb->usermeta . '_wpmo', 68 'name' => __('User'), 69 'title' => __('User Meta', 'meta-optimizer') 70 ], 71 'term' => [ 72 'table' => $wpdb->termmeta . '_wpmo', 73 'name' => __('Term'), 74 'title' => __('Term Meta', 'meta-optimizer') 75 ] 76 ); 77 } 66 $this->wpMetaTables = array( 67 'post' => $wpdb->postmeta, 68 'comment' => $wpdb->commentmeta, 69 'user' => $wpdb->usermeta, 70 'term' => $wpdb->termmeta 71 ); 72 73 $this->tables = array( 74 'post' => [ 75 'table' => $wpdb->postmeta . '_wpmo', 76 'name' => __( 'Post' ), 77 'title' => __( 'Post Meta', 'meta-optimizer' ) 78 ], 79 'comment' => [ 80 'table' => $wpdb->commentmeta . '_wpmo', 81 'name' => __( 'Comment' ), 82 'title' => __( 'Comment Meta', 'meta-optimizer' ) 83 ], 84 'user' => [ 85 'table' => $wpdb->usermeta . '_wpmo', 86 'name' => __( 'User' ), 87 'title' => __( 'User Meta', 'meta-optimizer' ) 88 ], 89 'term' => [ 90 'table' => $wpdb->termmeta . '_wpmo', 91 'name' => __( 'Term' ), 92 'title' => __( 'Term Meta', 'meta-optimizer' ) 93 ] 94 ); 95 } 78 96 } -
meta-optimizer/trunk/inc/CommentQueries.php
r2802191 r2960316 2 2 3 3 namespace WPMetaOptimizer; 4 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 4 7 5 8 /** 6 9 * Comment API: CommentQueries class. 7 10 * 8 * @package WPMetaOptimizer11 * @package WPMetaOptimizer 9 12 * @subpackage Comments 10 * @since 1.013 * @since 1.0 11 14 */ 12 13 class CommentQueries 14 { 15 public static $instance = null; 16 private $Queries; 17 18 function __construct($Queries) 19 { 20 $this->Queries = $Queries; 21 22 add_filter('comments_clauses', [$this, 'changeCommentsClauses'], 9999, 2); 23 } 24 25 /** 26 * Filters the comment query clauses. 27 * @copyright Base on WP_Comment_Query:get_comments method. 28 * 29 * @param string[] $clauses An associative array of comment query clauses. 30 * @param \WP_Comment_Query $query Current instance of WP_Comment_Query (passed by reference). 31 */ 32 function changeCommentsClauses($clauses, $query) 33 { 34 global $wpdb; 35 36 // Change GroupBy 37 if (isset($clauses['groupby'])) 38 $clauses['groupby'] = ''; 39 40 // Change OrderBy 41 $order = ('ASC' === strtoupper($this->Queries->queryVars['order'])) ? 'ASC' : 'DESC'; 42 43 // Disable ORDER BY with 'none', an empty array, or boolean false. 44 if (in_array($this->Queries->queryVars['orderby'], array('none', array(), false), true)) { 45 $orderby = ''; 46 } elseif (!empty($this->Queries->queryVars['orderby'])) { 47 $ordersby = is_array($this->Queries->queryVars['orderby']) ? 48 $this->Queries->queryVars['orderby'] : 49 preg_split('/[,\s]/', $this->Queries->queryVars['orderby']); 50 51 $orderby_array = array(); 52 $found_orderby_comment_id = false; 53 foreach ($ordersby as $_key => $_value) { 54 if (!$_value) { 55 continue; 56 } 57 58 if (is_int($_key)) { 59 $_orderby = $_value; 60 $_order = $order; 61 } else { 62 $_orderby = $_key; 63 $_order = $_value; 64 } 65 66 if (!$found_orderby_comment_id && in_array($_orderby, array('comment_ID', 'comment__in'), true)) { 67 $found_orderby_comment_id = true; 68 } 69 70 $parsed = $this->commentParseOrderby($_orderby); 71 72 if (!$parsed) 73 continue; 74 75 if ('comment__in' === $_orderby) { 76 $orderby_array[] = $parsed; 77 continue; 78 } 79 80 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder($_order); 81 } 82 83 // If no valid clauses were found, order by comment_date_gmt. 84 if (empty($orderby_array)) { 85 $orderby_array[] = "$wpdb->comments.comment_date_gmt $order"; 86 } 87 88 // To ensure determinate sorting, always include a comment_ID clause. 89 if (!$found_orderby_comment_id) { 90 $comment_id_order = ''; 91 92 // Inherit order from comment_date or comment_date_gmt, if available. 93 foreach ($orderby_array as $orderby_clause) { 94 if (preg_match('/comment_date(?:_gmt)*\ (ASC|DESC)/', $orderby_clause, $match)) { 95 $comment_id_order = $match[1]; 96 break; 97 } 98 } 99 100 // If no date-related order is available, use the date from the first available clause. 101 if (!$comment_id_order) { 102 foreach ($orderby_array as $orderby_clause) { 103 if (false !== strpos('ASC', $orderby_clause)) { 104 $comment_id_order = 'ASC'; 105 } else { 106 $comment_id_order = 'DESC'; 107 } 108 109 break; 110 } 111 } 112 113 // Default to DESC. 114 if (!$comment_id_order) { 115 $comment_id_order = 'DESC'; 116 } 117 118 $orderby_array[] = "$wpdb->comments.comment_ID $comment_id_order"; 119 } 120 121 $orderby = implode(', ', $orderby_array); 122 } else { 123 $orderby = "$wpdb->comments.comment_date_gmt $order"; 124 } 125 126 $clauses['orderby'] = $orderby; 127 128 return $clauses; 129 } 130 131 /** 132 * Parse and sanitize 'orderby' keys passed to the comment query. 133 * @copyright Base on WP_Comment_Query:parse_orderby method. 134 * 135 * @since 4.2.0 136 * 137 * @global wpdb $wpdb WordPress database abstraction object. 138 * 139 * @param string $orderby Alias for the field to order by. 140 * @return string|false Value to used in the ORDER clause. False otherwise. 141 */ 142 protected function commentParseOrderby($orderby) 143 { 144 global $wpdb; 145 146 $allowed_keys = array( 147 'comment_agent', 148 'comment_approved', 149 'comment_author', 150 'comment_author_email', 151 'comment_author_IP', 152 'comment_author_url', 153 'comment_content', 154 'comment_date', 155 'comment_date_gmt', 156 'comment_ID', 157 'comment_karma', 158 'comment_parent', 159 'comment_post_ID', 160 'comment_type', 161 'user_id', 162 ); 163 164 $meta_query_clauses = $this->Queries->metaQuery->get_clauses(); 165 166 $primary_meta_key = ''; 167 $primary_meta_query = false; 168 if (!empty($meta_query_clauses)) { 169 $primary_meta_query = isset($meta_query_clauses[$orderby]) ? $meta_query_clauses[$orderby] : reset($meta_query_clauses); 170 171 if (!empty($primary_meta_query['key'])) { 172 $primary_meta_key = $primary_meta_query['key']; 173 $allowed_keys[] = $primary_meta_key; 174 } 175 176 $allowed_keys[] = $this->Queries->queryVars['meta_key']; 177 $allowed_keys[] = 'meta_value'; 178 $allowed_keys[] = 'meta_value_num'; 179 } 180 181 if ($meta_query_clauses) { 182 $allowed_keys = array_merge($allowed_keys, array_keys($meta_query_clauses)); 183 } 184 185 $parsed = false; 186 if ($this->Queries->queryVars['meta_key'] === $orderby || 'meta_value' === $orderby) { 187 //$parsed = "$commentMetaTable.meta_value"; 188 if (!empty($primary_meta_query['type'])) { 189 $parsed = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})"; 190 } else { 191 $parsed = "{$primary_meta_query['alias']}.{$primary_meta_key}"; 192 } 193 } elseif ('meta_value_num' === $orderby) { 194 // $parsed = "$commentMetaTable.meta_value+0"; 195 $parsed = "{$primary_meta_query['alias']}.{$primary_meta_key}+0"; 196 } elseif ('comment__in' === $orderby) { 197 $comment__in = implode(',', array_map('absint', $this->queryVars['comment__in'])); 198 $parsed = "FIELD( {$wpdb->comments}.comment_ID, $comment__in )"; 199 } elseif (in_array($orderby, $allowed_keys, true)) { 200 if (array_key_exists($orderby, $meta_query_clauses)) { 201 // $orderby corresponds to a meta_query clause. 202 $meta_clause = $meta_query_clauses[$orderby]; 203 $parsed = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})"; 204 } else { 205 // Default: order by post field. 206 $parsed = "{$wpdb->posts}.post_" . sanitize_key($orderby); 207 } 208 } 209 210 return $parsed; 211 } 212 213 /** 214 * Returns an instance of class 215 * @return CommentQueries 216 */ 217 static function getInstance($Queries) 218 { 219 if (self::$instance == null) 220 self::$instance = new CommentQueries($Queries); 221 222 return self::$instance; 223 } 15 class CommentQueries { 16 public static $instance = null; 17 private $Queries; 18 19 function __construct( $Queries ) { 20 $this->Queries = $Queries; 21 22 add_filter( 'comments_clauses', [ $this, 'changeCommentsClauses' ], 9999, 2 ); 23 } 24 25 /** 26 * Filters the comment query clauses. 27 * 28 * @param string[] $clauses An associative array of comment query clauses. 29 * @param \WP_Comment_Query $query Current instance of WP_Comment_Query (passed by reference). 30 * 31 * @copyright Base on WP_Comment_Query:get_comments method. 32 * 33 */ 34 function changeCommentsClauses( $clauses, $query ) { 35 global $wpdb; 36 37 // Change GroupBy 38 if ( isset( $clauses['groupby'] ) ) 39 $clauses['groupby'] = ''; 40 41 // Change OrderBy 42 $order = ( 'ASC' === strtoupper( $this->Queries->queryVars['order'] ) ) ? 'ASC' : 'DESC'; 43 44 // Disable ORDER BY with 'none', an empty array, or boolean false. 45 if ( in_array( $this->Queries->queryVars['orderby'], array( 'none', array(), false ), true ) ) { 46 $orderby = ''; 47 } elseif ( ! empty( $this->Queries->queryVars['orderby'] ) ) { 48 $ordersby = is_array( $this->Queries->queryVars['orderby'] ) ? 49 $this->Queries->queryVars['orderby'] : 50 preg_split( '/[,\s]/', $this->Queries->queryVars['orderby'] ); 51 52 $orderby_array = array(); 53 $found_orderby_comment_id = false; 54 foreach ( $ordersby as $_key => $_value ) { 55 if ( ! $_value ) { 56 continue; 57 } 58 59 if ( is_int( $_key ) ) { 60 $_orderby = $_value; 61 $_order = $order; 62 } else { 63 $_orderby = $_key; 64 $_order = $_value; 65 } 66 67 if ( ! $found_orderby_comment_id && in_array( $_orderby, array( 68 'comment_ID', 69 'comment__in' 70 ), true ) ) { 71 $found_orderby_comment_id = true; 72 } 73 74 $parsed = $this->commentParseOrderby( $_orderby ); 75 76 if ( ! $parsed ) 77 continue; 78 79 if ( 'comment__in' === $_orderby ) { 80 $orderby_array[] = $parsed; 81 continue; 82 } 83 84 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder( $_order ); 85 } 86 87 // If no valid clauses were found, order by comment_date_gmt. 88 if ( empty( $orderby_array ) ) { 89 $orderby_array[] = "$wpdb->comments.comment_date_gmt $order"; 90 } 91 92 // To ensure determinate sorting, always include a comment_ID clause. 93 if ( ! $found_orderby_comment_id ) { 94 $comment_id_order = ''; 95 96 // Inherit order from comment_date or comment_date_gmt, if available. 97 foreach ( $orderby_array as $orderby_clause ) { 98 if ( preg_match( '/comment_date(?:_gmt)*\ (ASC|DESC)/', $orderby_clause, $match ) ) { 99 $comment_id_order = $match[1]; 100 break; 101 } 102 } 103 104 // If no date-related order is available, use the date from the first available clause. 105 if ( ! $comment_id_order ) { 106 foreach ( $orderby_array as $orderby_clause ) { 107 if ( false !== strpos( 'ASC', $orderby_clause ) ) { 108 $comment_id_order = 'ASC'; 109 } else { 110 $comment_id_order = 'DESC'; 111 } 112 113 break; 114 } 115 } 116 117 // Default to DESC. 118 if ( ! $comment_id_order ) { 119 $comment_id_order = 'DESC'; 120 } 121 122 $orderby_array[] = "$wpdb->comments.comment_ID $comment_id_order"; 123 } 124 125 $orderby = implode( ', ', $orderby_array ); 126 } else { 127 $orderby = "$wpdb->comments.comment_date_gmt $order"; 128 } 129 130 $clauses['orderby'] = $orderby; 131 132 return $clauses; 133 } 134 135 /** 136 * Parse and sanitize 'orderby' keys passed to the comment query. 137 * 138 * @param string $orderby Alias for the field to order by. 139 * 140 * @return string|false Value to use in the ORDER clause. False otherwise. 141 * @global \wpdb $wpdb WordPress database abstraction object. 142 * 143 * @copyright Base on WP_Comment_Query: parse_orderby method. 144 * 145 * @since 4.2.0 146 * 147 */ 148 protected function commentParseOrderby( $orderby ) { 149 global $wpdb; 150 151 $allowed_keys = array( 152 'comment_agent', 153 'comment_approved', 154 'comment_author', 155 'comment_author_email', 156 'comment_author_IP', 157 'comment_author_url', 158 'comment_content', 159 'comment_date', 160 'comment_date_gmt', 161 'comment_ID', 162 'comment_karma', 163 'comment_parent', 164 'comment_post_ID', 165 'comment_type', 166 'user_id', 167 ); 168 169 $meta_query_clauses = $this->Queries->metaQuery->get_clauses(); 170 171 $primary_meta_key = ''; 172 $primary_meta_query = false; 173 if ( ! empty( $meta_query_clauses ) ) { 174 $primary_meta_query = isset( $meta_query_clauses[ $orderby ] ) ? $meta_query_clauses[ $orderby ] : reset( $meta_query_clauses ); 175 176 if ( ! empty( $primary_meta_query['key'] ) ) { 177 $primary_meta_key = $primary_meta_query['key']; 178 $allowed_keys[] = $primary_meta_key; 179 } 180 181 $allowed_keys[] = $this->Queries->queryVars['meta_key']; 182 $allowed_keys[] = 'meta_value'; 183 $allowed_keys[] = 'meta_value_num'; 184 } 185 186 if ( $meta_query_clauses ) { 187 $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_query_clauses ) ); 188 } 189 190 $parsed = false; 191 if ( $this->Queries->queryVars['meta_key'] === $orderby || 'meta_value' === $orderby ) { 192 //$parsed = "$commentMetaTable.meta_value"; 193 if ( ! empty( $primary_meta_query['type'] ) ) { 194 $parsed = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})"; 195 } else { 196 $parsed = "{$primary_meta_query['alias']}.{$primary_meta_key}"; 197 } 198 } elseif ( 'meta_value_num' === $orderby ) { 199 // $parsed = "$commentMetaTable.meta_value+0"; 200 $parsed = "{$primary_meta_query['alias']}.{$primary_meta_key}+0"; 201 } elseif ( 'comment__in' === $orderby ) { 202 $comment__in = implode( ',', array_map( 'absint', $this->queryVars['comment__in'] ) ); 203 $parsed = "FIELD( $wpdb->comments.comment_ID, $comment__in )"; 204 } elseif ( in_array( $orderby, $allowed_keys, true ) ) { 205 if ( array_key_exists( $orderby, $meta_query_clauses ) ) { 206 // $orderby corresponds to a meta_query clause. 207 $meta_clause = $meta_query_clauses[ $orderby ]; 208 $parsed = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})"; 209 } else { 210 // Default: order by post-field. 211 $parsed = "$wpdb->posts.post_" . sanitize_key( $orderby ); 212 } 213 } 214 215 return $parsed; 216 } 217 218 /** 219 * Returns an instance of class 220 * 221 * @return CommentQueries 222 */ 223 static function getInstance( $Queries ) { 224 if ( self::$instance == null ) 225 self::$instance = new CommentQueries( $Queries ); 226 227 return self::$instance; 228 } 224 229 } -
meta-optimizer/trunk/inc/Helpers.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 7 5 8 use DateTime; 6 use phpDocumentor\Reflection\DocBlock\Tags\Var_; 7 8 class Helpers extends Base 9 { 10 public static $instance = null; 11 protected $Options = null; 12 13 function __construct() 14 { 15 parent::__construct(); 16 17 $this->Options = Options::getInstance(); 18 } 19 20 /** 21 * Insert meta, called when add or update meta 22 * 23 * @param array $args{ 24 * input args 25 * 26 * @type string $metaType Type of meta 27 * @type int $objectID Object ID 28 * @type string $metaKey Meta key 29 * @type string $metaValue Metadata value. Must be serializable if non-scalar. 30 * @type bool $unique Meta is unique 31 * @type bool $addMeta Add meta status 32 * @type string $prevValue Previous value to check before updating 33 * @type bool $checkCurrentValue Check current value use when import process running 34 * } 35 * 36 * @return null|int|bool 37 */ 38 public function insertMeta($args) 39 { 40 global $wpdb; 41 42 $args = wp_parse_args($args, [ 43 'metaType' => '', 44 'objectID' => 0, 45 'metaKey' => '', 46 'metaValue' => '', 47 'unique' => true, 48 'addMeta' => false, 49 'prevValue' => '', 50 'checkCurrentValue' => true 51 ]); 52 53 extract($args); 54 55 if (!$objectID || empty($metaType) || empty($metaKey)) 56 return null; 57 58 $tableName = $this->getMetaTableName($metaType); 59 if (!$tableName) 60 return null; 61 62 $column = sanitize_key($metaType . '_id'); 63 64 // WP check for exists meta key for object id 65 // Checked because update_metadata function checked again and call add_metadata function 66 if (!$addMeta) { 67 $_metaKey = $this->translateColumnName($metaType, $metaKey); 68 $wpMetaTable = $this->getWPMetaTableName($metaType); 69 $idColumn = 'user' === $metaType ? 'umeta_id' : 'meta_id'; 70 $meta_ids = $wpdb->get_col($wpdb->prepare("SELECT $idColumn FROM $wpMetaTable WHERE meta_key = %s AND $column = %d", $_metaKey, $objectID)); 71 72 if (empty($meta_ids)) 73 return null; 74 } 75 76 $addTableColumn = $this->addTableColumn($tableName, $metaType, $metaKey, $metaValue); 77 if (!$addTableColumn) 78 return null; 79 80 $checkInserted = intval($wpdb->get_var( 81 $wpdb->prepare( 82 "SELECT COUNT(*) FROM {$tableName} WHERE {$column} = %d", 83 $objectID 84 ) 85 )); 86 87 if (is_bool($metaValue)) 88 $metaValue = intval($metaValue); 89 90 if ($checkInserted) { 91 if ($checkCurrentValue) { 92 $currentValue = $wpdb->get_var( 93 $wpdb->prepare( 94 "SELECT {$metaKey} FROM {$tableName} WHERE {$column} = %d", 95 $objectID 96 ) 97 ); 98 99 $currentValue = maybe_unserialize($currentValue); 100 101 if ($unique && $currentValue !== null) 102 return null; 103 104 elseif (!$unique && empty($prevValue) && $addMeta) { 105 if (is_array($currentValue)) 106 $metaValue = array_merge($currentValue, [$metaValue]); 107 elseif (!is_null($currentValue)) 108 $metaValue = [$currentValue, $metaValue]; 109 // 110 } elseif (!$unique && !empty($prevValue) && $currentValue !== null) { 111 if (is_array($currentValue)) { 112 $indexValue = array_search($prevValue, $currentValue, false); 113 114 if ($indexValue === false) 115 return null; 116 else { 117 $currentValue[$indexValue] = $metaValue; 118 $metaValue = $currentValue; 119 } 120 } elseif ($prevValue !== $currentValue) 121 return null; 122 } 123 124 $addTableColumn = $this->addTableColumn($tableName, $metaType, $metaKey, $metaValue); 125 if (!$addTableColumn) 126 return null; 127 } 128 129 $metaValue = maybe_serialize($metaValue); 130 131 if ($metaValue === '') 132 $metaValue = null; 133 134 $result = $wpdb->update( 135 $tableName, 136 [$metaKey => $metaValue, 'updated_at' => $this->now], 137 [$column => $objectID] 138 ); 139 140 wp_cache_delete($objectID . '_' . $metaKey, WPMETAOPTIMIZER_PLUGIN_KEY . '_post_meta'); 141 142 return $result; 143 } else { 144 $metaValue = maybe_serialize($metaValue); 145 146 $result = $wpdb->insert( 147 $tableName, 148 [ 149 $column => $objectID, 150 'created_at' => $this->now, 151 'updated_at' => $this->now, 152 $metaKey => $metaValue 153 ] 154 ); 155 if (!$result) 156 return false; 157 158 return (int) $wpdb->insert_id; 159 } 160 } 161 162 /** 163 * Delete meta row from plugin tables 164 * 165 * @param int $objectID Object ID 166 * @param string $type Meta type 167 * 168 * @return bool|int 169 */ 170 public function deleteMetaRow($objectID, $type) 171 { 172 global $wpdb; 173 $table = $this->getMetaTableName($type); 174 if ($table) 175 return $wpdb->query("DELETE FROM {$table} WHERE {$type}_id = {$objectID}"); 176 177 return false; 178 } 179 180 /** 181 * Add column to plugin tables 182 * 183 * @param string $table Table name 184 * @param string $type Meta type 185 * @param string $field Meta field 186 * @param string $metaValue Meta value 187 * 188 * @return bool|int|null 189 */ 190 public function addTableColumn($table, $type, $field, $metaValue) 191 { 192 global $wpdb; 193 $addTableColumn = true; 194 $collate = ''; 195 196 $value = maybe_serialize($metaValue); 197 $columnType = $this->getFieldType($value); 198 $valueLength = mb_strlen($value); 199 200 if (in_array($columnType, $this->charTypes)) 201 $collate = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci'; 202 203 if ($this->checkColumnExists($table, $type, $field, false)) { 204 $currentColumnType = $this->getTableColumnType($table, $field); 205 $newColumnType = $this->getNewColumnType($currentColumnType, $columnType); 206 207 if ($newColumnType == 'VARCHAR') { 208 $currentFieldMaxLengthValue = intval($wpdb->get_var("SELECT MAX(LENGTH({$field})) as length FROM {$table}")); 209 210 if ($currentFieldMaxLengthValue >= $valueLength && $currentColumnType === 'VARCHAR') 211 return $addTableColumn; 212 else 213 $newColumnType = 'VARCHAR(' . ($valueLength > $currentFieldMaxLengthValue ? $valueLength : $currentFieldMaxLengthValue) . ')'; 214 } elseif ($newColumnType == $currentColumnType) 215 return $addTableColumn; 216 217 $sql = "ALTER TABLE `{$table}` CHANGE `{$field}` `{$field}` {$newColumnType} {$collate} NULL DEFAULT NULL"; 218 } else { 219 if ($columnType == 'VARCHAR') 220 $columnType = 'VARCHAR(' . $valueLength . ')'; 221 222 $sql = "ALTER TABLE `{$table}` ADD COLUMN `{$field}` {$columnType} {$collate} NULL AFTER `{$type}_id`"; 223 } 224 225 $addTableColumn = $wpdb->query($sql); 226 227 return $addTableColumn; 228 } 229 230 /** 231 * Check column exists in plugin tables 232 * 233 * @param string $table Table name 234 * @param string $type Meta type 235 * @param string $field Meta field 236 * @param bool $useCache Use cache 237 * 238 * @return bool 239 */ 240 public function checkColumnExists($table, $type, $field, $useCache = true) 241 { 242 $tableColumns = $this->getTableColumns($table, $type, $useCache); 243 return in_array($field, $tableColumns); 244 } 245 246 /** 247 * Get list of plugin table columns 248 * 249 * @param string $table Table name 250 * @param string $type Meta type 251 * @param bool $useCache Use cache 252 * 253 * @return array 254 */ 255 public function getTableColumns($table, $type, $useCache = false) 256 { 257 global $wpdb; 258 $tableColumns = false; 259 260 if ($useCache) 261 $tableColumns = wp_cache_get('table_columns_' . $table . '_' . $type, WPMETAOPTIMIZER_PLUGIN_KEY); 262 263 if ($tableColumns === false) { 264 $columns = $wpdb->get_results("SHOW COLUMNS FROM $table", ARRAY_A); 265 $columns = array_map(function ($column) { 266 return $column['Field']; 267 }, $columns); 268 $tableColumns = array_diff($columns, array_merge($this->ignoreTableColumns, [$type . '_id'])); 269 270 wp_cache_set('table_columns_' . $table . '_' . $type, $tableColumns, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE); 271 } 272 273 return $tableColumns; 274 } 275 276 /** 277 * Changed column name if in reserved column keys 278 * 279 * @param string $type Meta type 280 * @param string $columnName Column name 281 * @return string Changed column name 282 */ 283 public function translateColumnName($type, $columnName) 284 { 285 $suffix = $this->reservedKeysSuffix; 286 $reservedKeys = array_merge($this->ignoreTableColumns, [$type . '_id']); 287 288 if (substr($columnName, - (strlen($suffix))) === $suffix) 289 $columnName = str_replace($suffix, '', $columnName); 290 elseif (in_array($columnName, $reservedKeys)) 291 $columnName = $columnName . $suffix; 292 293 return $columnName; 294 } 295 296 /** 297 * Get new column type for exists columns 298 * 299 * @param string $currentColumnType Current column type 300 * @param string $valueType New value type 301 * @return string 302 */ 303 public function getNewColumnType($currentColumnType, $valueType) 304 { 305 if ($currentColumnType === $valueType) 306 return $currentColumnType; 307 elseif (in_array($currentColumnType, $this->intTypes) && in_array($valueType, $this->floatTypes)) 308 return $valueType; 309 elseif (in_array($currentColumnType, $this->intTypes) && in_array($valueType, $this->charTypes)) 310 return $valueType; 311 elseif (in_array($currentColumnType, $this->dateTypes) && in_array($valueType, $this->charTypes)) 312 return $valueType; 313 elseif (in_array($currentColumnType, $this->intTypes) && array_search($currentColumnType, $this->intTypes) < array_search($valueType, $this->intTypes)) 314 return $valueType; 315 elseif (in_array($currentColumnType, $this->floatTypes) && array_search($currentColumnType, $this->floatTypes) < array_search($valueType, $this->floatTypes)) 316 return $valueType; 317 318 return $currentColumnType; 319 } 320 321 /** 322 * Get table column type 323 * 324 * @param string $table Table name 325 * @param string $field Column name 326 * @return string Column type 327 */ 328 public function getTableColumnType($table, $field) 329 { 330 global $wpdb; 331 332 $wpdb->get_results("SELECT {$field} FROM {$table} LIMIT 1"); 333 $columnType = $wpdb->get_col_info('type', 0); 334 335 if ($columnType === 252) 336 return 'TEXT'; 337 else if ($columnType === 253) 338 return 'VARCHAR'; 339 else if ($columnType === 1) 340 return 'TINYINT'; 341 else if ($columnType === 2) 342 return 'SMALLINT'; 343 else if ($columnType === 9) 344 return 'MEDIUMINT'; 345 else if ($columnType === 3) 346 return 'INT'; 347 else if ($columnType === 8) 348 return 'BIGINT'; 349 else if ($columnType === 4) 350 return 'FLOAT'; 351 else if ($columnType === 5) 352 return 'DOUBLE'; 353 else if ($columnType === 10) 354 return 'DATE'; 355 else if ($columnType === 12) 356 return 'DATETIME'; 357 else 358 return false; 359 } 360 361 /** 362 * Return table column type base on value 363 * 364 * @param string $value Meta value 365 * @return string Column type 366 */ 367 public function getFieldType($value) 368 { 369 $valueLength = mb_strlen($value); 370 371 if ($this->isDate($value)) 372 return 'DATE'; 373 elseif ($this->isDateTime($value)) 374 return 'DATETIME'; 375 // elseif ($this->isJson($value)) 376 // return 'LONGTEXT'; 377 elseif (is_string($value) && $valueLength <= 65535 || is_null($value)) 378 return 'TEXT'; // 'VARCHAR'; 379 elseif (is_bool($value)) 380 return 'TINYINT'; 381 elseif (is_float($value)) 382 return 'FLOAT'; 383 elseif (is_double($value)) 384 return 'DOUBLE'; 385 elseif (is_numeric($value) && intval($value) != 0 || is_int($value) || $value == 0) { 386 $value = intval($value); 387 if ($value >= -128 && $value <= 127) 388 return 'TINYINT'; 389 if ($value >= -32768 && $value <= 32767) 390 return 'SMALLINT'; 391 if ($value >= -8388608 && $value <= 8388607) 392 return 'MEDIUMINT'; 393 if ($value >= -2147483648 && $value <= 2147483647) 394 return 'INT'; 395 else 396 return 'BIGINT'; 397 } else 398 return 'TEXT'; 399 } 400 401 /** 402 * Get table rows count 403 * 404 * @param string $table Table name 405 * @return int 406 */ 407 public function getTableRowsCount($table) 408 { 409 global $wpdb; 410 return $wpdb->get_var("SELECT COUNT(*) FROM $table"); 411 } 412 413 /** 414 * Get meta table name base on type 415 * 416 * @param string $type Meta type 417 * @return bool|string 418 */ 419 public function getMetaTableName($type) 420 { 421 if (isset($this->tables[$type])) 422 return $this->tables[$type]['table']; 423 else 424 return false; 425 } 426 427 /** 428 * Get WP table name base on type 429 * 430 * @param string $type Meta type 431 * @return bool|string 432 */ 433 public function getWPPrimaryTableName($type) 434 { 435 if (isset($this->wpPrimaryTables[$type])) 436 return $this->wpPrimaryTables[$type]; 437 else 438 return false; 439 } 440 441 /** 442 * Get WordPress meta table name base on type 443 * 444 * @param string $type Meta type 445 * @return bool|string 446 */ 447 public function getWPMetaTableName($type) 448 { 449 if (isset($this->wpMetaTables[$type])) 450 return $this->wpMetaTables[$type]; 451 else 452 return false; 453 } 454 455 /** 456 * Check if user set dont save in default WordPress meta tables 457 * 458 * @param string $type Meta type 459 * @return bool 460 */ 461 public function checkDontSaveInDefaultTable($type) 462 { 463 $defaultMetaSave = $this->Options->getOption('dont_save_wpmeta', []); 464 return isset($defaultMetaSave[$type]); 465 } 466 467 /** 468 * Check meta can get/add/update 469 * 470 * @param string $type Meta type 471 * @return bool 472 */ 473 public function checkMetaType($type) 474 { 475 $metaSaveTypes = $this->Options->getOption('meta_save_types', []); 476 return isset($metaSaveTypes[$type]); 477 } 478 479 /** 480 * Check supported post type 481 * 482 * @param int $postID Post ID 483 * @return bool 484 */ 485 public function checkPostType($postID) 486 { 487 $postType = wp_cache_get('post_type_value_' . $postID, WPMETAOPTIMIZER_PLUGIN_KEY); 488 if (!$postType) { 489 $postType = get_post_type($postID); 490 wp_cache_set('post_type_value_' . $postID, $postType, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE); 491 } 492 $allowdPostTypes = $this->Options->getOption('post_types', []); 493 return isset($allowdPostTypes[$postType]); 494 } 495 496 /** 497 * Check a meta key exists in black/white list 498 * 499 * @param string $type Meta type 500 * @param string $metaKey Meta key 501 * @param string $listName List name 502 * @return bool 503 */ 504 public function checkInBlackWhiteList($type, $metaKey, $listName = 'black_list') 505 { 506 if ($listName === 'black_list' && isset($this->ignoreWPMetaKeys[$type]) && in_array($metaKey, $this->ignoreWPMetaKeys[$type])) 507 return true; 508 509 $list = $this->Options->getOption($type . '_' . $listName, ''); 510 if (empty($list)) 511 return ''; 512 513 $list = explode("\n", $list); 514 $list = str_replace(["\n", "\r"], '', $list); 515 $list = array_map('trim', $list); 516 return in_array($metaKey, $list); 517 } 518 519 /** 520 * Check can change WordPress meta keys 521 * 522 * @param string $type Meta type 523 * @param string $metaKey Meta key 524 * @return bool 525 */ 526 public function checkCanChangeWPMetaKey($type, $metaKey) 527 { 528 return !(isset($this->cantChangeWPMetaKeys[$type]) && in_array($metaKey, $this->cantChangeWPMetaKeys[$type])); 529 } 530 531 /** 532 * Get object left items count for import process 533 * 534 * @param string $type Meta type 535 * @return int 536 */ 537 public function getObjectLeftItemsCount($type) 538 { 539 $latestObjectID = $this->Options->getOption('import_' . $type . '_latest_id', null); 540 541 if ($latestObjectID === 'finished') 542 return 0; 543 544 return $this->getLatestObjectID($type, $latestObjectID, true); 545 } 546 547 /** 548 * Get latest object ID 549 * 550 * @param string $type Meta type 551 * @param int $latestObjectID Latest changed object ID 552 * @param boolean $findItemsLeft Find items left for import process 553 * @return int|null 554 */ 555 public function getLatestObjectID($type, $latestObjectID = null, $findItemsLeft = false) 556 { 557 global $wpdb; 558 $primaryColumn = 'ID'; 559 $where = []; 560 $wheres = ""; 561 562 $table = $wpdb->prefix . $type . 's'; 563 564 if (in_array($type, ['term', 'comment'])) 565 $primaryColumn = $type . '_ID'; 566 567 if ($latestObjectID !== null) 568 $where[] = "{$primaryColumn} < {$latestObjectID}"; 569 570 if ($type === 'post') { 571 $where[] = "post_status IN ('publish','future','draft','pending','private')"; 572 573 $allowdPostTypes = $this->Options->getOption('post_types', []); 574 $allowdPostTypes = array_keys($allowdPostTypes); 575 if (count($allowdPostTypes)) 576 $where[] = "post_type IN ('" . implode("','", $allowdPostTypes) . "')"; 577 } 578 579 if (count($where)) 580 $wheres = "WHERE " . implode(' AND ', $where); 581 582 if ($findItemsLeft) 583 $query = "SELECT COUNT(*) FROM {$table} {$wheres}"; 584 else 585 $query = "SELECT {$primaryColumn} FROM {$table} {$wheres} ORDER BY {$primaryColumn} DESC LIMIT 1"; 586 return $wpdb->get_var($query); 587 } 588 589 /** 590 * Check active automatically support WP query 591 * 592 * @return void 593 */ 594 public function activeAutomaticallySupportWPQuery() 595 { 596 if ($this->checkImportFinished()) { 597 $supportWPQuery = $this->Options->getOption('support_wp_query', 0) == 1; 598 $activeAutomatically = $this->Options->getOption('support_wp_query_active_automatically', false) == 1; 599 600 if (!$supportWPQuery && $activeAutomatically) 601 $this->Options->setOption('support_wp_query', 1); 602 } 603 } 604 605 /** 606 * Check support WP Query 607 * 608 * @return boolean 609 */ 610 public function checkSupportWPQuery() 611 { 612 $supportWPQuery = $this->Options->getOption('support_wp_query', false) == 1; 613 $deactiveWhileImport = $this->Options->getOption('support_wp_query_deactive_while_import', false) == 1; 614 615 return $supportWPQuery && (!$deactiveWhileImport || $deactiveWhileImport && $this->checkImportFinished()); 616 } 617 618 /** 619 * Check import finished 620 * 621 * @param boolean $type Meta type 622 * @return boolean 623 */ 624 public function checkImportFinished($type = false) 625 { 626 // $types = array_keys($this->tables); 627 $types = $this->Options->getOption('meta_save_types', []); 628 if (isset($types['hidden'])) 629 unset($types['hidden']); 630 631 $types = array_keys($types); 632 633 if ($type && in_array($type, $types)) 634 $types = [$type]; 635 636 if (count($types) == 0) 637 return false; 638 639 foreach ($types as $type) { 640 $latestObjectID = $this->Options->getOption('import_' . $type . '_latest_id', null); 641 if ($latestObjectID !== 'finished') 642 return false; 643 } 644 645 return true; 646 } 647 648 /** 649 * Check is JSON 650 * 651 * @param string $string Input string 652 * @return boolean 653 */ 654 private function isJson($string) 655 { 656 if (!is_string($string)) 657 return false; 658 json_decode($string); 659 return json_last_error() === JSON_ERROR_NONE; 660 } 661 662 /** 663 * Check is Date 664 * 665 * @param string $string Input string 666 * @return boolean 667 */ 668 private function isDate($string) 669 { 670 $time = strtotime($string); 671 672 if ($time) 673 $time = DateTime::createFromFormat('Y-m-d', $string) !== false; 674 675 return $time; 676 } 677 678 /** 679 * Check is DateTime 680 * 681 * @param string $string Input string 682 * @return boolean 683 */ 684 private function isDateTime($string) 685 { 686 return DateTime::createFromFormat('Y-m-d H:i:s', $string) !== false; 687 } 688 689 /** 690 * Returns an instance of class 691 * @return Helpers 692 */ 693 static function getInstance() 694 { 695 if (self::$instance == null) 696 self::$instance = new Helpers(); 697 698 return self::$instance; 699 } 9 10 class Helpers extends Base { 11 public static $instance = null; 12 protected $Options = null; 13 14 function __construct() { 15 parent::__construct(); 16 17 $this->Options = Options::getInstance(); 18 } 19 20 /** 21 * Insert meta, called when add or update meta 22 * 23 * @param array $args { 24 * input args 25 * 26 * @type string $metaType Type of meta 27 * @type int $objectID Object ID 28 * @type string $metaKey Meta key 29 * @type string $metaValue Metadata value. Must be serializable if non-scalar. 30 * @type bool $unique Meta is unique 31 * @type bool $addMeta Add meta status 32 * @type string $prevValue Previous value to check before updating 33 * @type bool $checkCurrentValue Check current value use when an import process running 34 * } 35 * 36 * @return null|int|bool 37 */ 38 public function insertMeta( $args ) { 39 global $wpdb; 40 41 $args = wp_parse_args( $args, [ 42 'metaType' => '', 43 'objectID' => 0, 44 'metaKey' => '', 45 'metaValue' => '', 46 'unique' => true, 47 'addMeta' => false, 48 'prevValue' => '', 49 'checkCurrentValue' => true 50 ] ); 51 52 extract( $args ); 53 54 if ( ! $objectID || empty( $metaType ) || empty( $metaKey ) ) 55 return null; 56 57 $tableName = $this->getMetaTableName( $metaType ); 58 if ( ! $tableName ) 59 return null; 60 61 $column = sanitize_key( $metaType . '_id' ); 62 63 // WP check for an existing meta key for an object id 64 // Checked because update_metadata function checked again and call add_metadata function 65 if ( ! $addMeta ) { 66 $_metaKey = $this->translateColumnName( $metaType, $metaKey ); 67 $wpMetaTable = $this->getWPMetaTableName( $metaType ); 68 $idColumn = 'user' === $metaType ? 'umeta_id' : 'meta_id'; 69 $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $idColumn FROM $wpMetaTable WHERE meta_key = %s AND $column = %d", $_metaKey, $objectID ) ); 70 71 if ( empty( $meta_ids ) ) 72 return null; 73 } 74 75 $addTableColumn = $this->addTableColumn( $tableName, $metaType, $metaKey, $metaValue ); 76 if ( ! $addTableColumn ) 77 return null; 78 79 $checkInserted = intval( $wpdb->get_var( 80 $wpdb->prepare( 81 "SELECT COUNT(*) FROM {$tableName} WHERE {$column} = %d", 82 $objectID 83 ) 84 ) ); 85 86 if ( is_bool( $metaValue ) ) 87 $metaValue = intval( $metaValue ); 88 89 if ( $checkInserted ) { 90 if ( $checkCurrentValue ) { 91 $currentValue = $wpdb->get_var( 92 $wpdb->prepare( 93 "SELECT {$metaKey} FROM {$tableName} WHERE {$column} = %d", 94 $objectID 95 ) 96 ); 97 98 $currentValue = maybe_unserialize( $currentValue ); 99 100 if ( $unique && $currentValue !== null ) 101 return null; 102 103 elseif ( ! $unique && empty( $prevValue ) && $addMeta ) { 104 if ( is_array( $currentValue ) ) 105 $metaValue = array_merge( $currentValue, [ $metaValue ] ); 106 elseif ( ! is_null( $currentValue ) ) 107 $metaValue = [ $currentValue, $metaValue ]; 108 // 109 } elseif ( ! $unique && ! empty( $prevValue ) && $currentValue !== null ) { 110 if ( is_array( $currentValue ) ) { 111 $indexValue = array_search( $prevValue, $currentValue, false ); 112 113 if ( $indexValue === false ) 114 return null; 115 else { 116 $currentValue[ $indexValue ] = $metaValue; 117 $metaValue = $currentValue; 118 } 119 } elseif ( $prevValue !== $currentValue ) 120 return null; 121 } 122 123 $addTableColumn = $this->addTableColumn( $tableName, $metaType, $metaKey, $metaValue ); 124 if ( ! $addTableColumn ) 125 return null; 126 } 127 128 $metaValue = maybe_serialize( $metaValue ); 129 130 if ( $metaValue === '' ) 131 $metaValue = null; 132 133 $result = $wpdb->update( 134 $tableName, 135 [ $metaKey => $metaValue, 'updated_at' => $this->now ], 136 [ $column => $objectID ] 137 ); 138 139 wp_cache_delete( $objectID . '_' . $metaKey, WPMETAOPTIMIZER_PLUGIN_KEY . '_post_meta' ); 140 141 return $result; 142 } else { 143 $metaValue = maybe_serialize( $metaValue ); 144 145 $result = $wpdb->insert( 146 $tableName, 147 [ 148 $column => $objectID, 149 'created_at' => $this->now, 150 'updated_at' => $this->now, 151 $metaKey => $metaValue 152 ] 153 ); 154 if ( ! $result ) 155 return false; 156 157 return $wpdb->insert_id; 158 } 159 } 160 161 /** 162 * Delete meta row from plugin tables 163 * 164 * @param int $objectID Object ID 165 * @param string $type Meta type 166 * 167 * @return bool|int 168 */ 169 public function deleteMetaRow( $objectID, $type ) { 170 global $wpdb; 171 $table = $this->getMetaTableName( $type ); 172 if ( $table ) 173 return $wpdb->query( "DELETE FROM {$table} WHERE {$type}_id = {$objectID}" ); 174 175 return false; 176 } 177 178 /** 179 * Add column to plugin tables 180 * 181 * @param string $table Table name 182 * @param string $type Meta type 183 * @param string $field Meta field 184 * @param string $metaValue Meta value 185 * 186 * @return bool|int|null 187 */ 188 public function addTableColumn( $table, $type, $field, $metaValue ) { 189 global $wpdb; 190 $addTableColumn = true; 191 $collate = ''; 192 193 $value = maybe_serialize( $metaValue ); 194 $columnType = $this->getFieldType( $value ); 195 $valueLength = mb_strlen( $value ); 196 197 if ( in_array( $columnType, $this->charTypes ) ) 198 $collate = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci'; 199 200 if ( $this->checkColumnExists( $table, $type, $field, false ) ) { 201 $currentColumnType = $this->getTableColumnType( $table, $field ); 202 $newColumnType = $this->getNewColumnType( $currentColumnType, $columnType ); 203 204 if ( $newColumnType == 'VARCHAR' ) { 205 $currentFieldMaxLengthValue = intval( $wpdb->get_var( "SELECT MAX(LENGTH({$field})) as length FROM {$table}" ) ); 206 207 if ( $currentFieldMaxLengthValue >= $valueLength && $currentColumnType === 'VARCHAR' ) 208 return $addTableColumn; 209 else 210 $newColumnType = 'VARCHAR(' . ( $valueLength > $currentFieldMaxLengthValue ? $valueLength : $currentFieldMaxLengthValue ) . ')'; 211 } elseif ( $newColumnType == $currentColumnType ) 212 return $addTableColumn; 213 214 $sql = "ALTER TABLE `$table` CHANGE `{$field}` `{$field}` {$newColumnType} {$collate} NULL DEFAULT NULL"; 215 } else { 216 if ( $columnType == 'VARCHAR' ) 217 $columnType = 'VARCHAR(' . $valueLength . ')'; 218 219 $sql = "ALTER TABLE `{$table}` ADD COLUMN `{$field}` {$columnType} {$collate} NULL AFTER `{$type}_id`"; 220 } 221 222 $addTableColumn = $wpdb->query( $sql ); 223 224 return $addTableColumn; 225 } 226 227 /** 228 * Check column exists in plugin tables 229 * 230 * @param string $table Table name 231 * @param string $type Meta type 232 * @param string $field Meta field 233 * @param bool $useCache Use cache 234 * 235 * @return bool 236 */ 237 public function checkColumnExists( $table, $type, $field, $useCache = true ) { 238 $tableColumns = $this->getTableColumns( $table, $type, $useCache ); 239 240 return in_array( $field, $tableColumns ); 241 } 242 243 /** 244 * Get list of plugin table columns 245 * 246 * @param string $table Table name 247 * @param string $type Meta type 248 * @param bool $useCache Use cache 249 * 250 * @return array 251 */ 252 public function getTableColumns( $table, $type, $useCache = false ) { 253 global $wpdb; 254 $tableColumns = false; 255 256 if ( $useCache ) 257 $tableColumns = wp_cache_get( 'table_columns_' . $table . '_' . $type, WPMETAOPTIMIZER_PLUGIN_KEY ); 258 259 if ( $tableColumns === false ) { 260 $columns = $wpdb->get_results( "SHOW COLUMNS FROM $table", ARRAY_A ); 261 $columns = array_map( function ( $column ) { 262 return $column['Field']; 263 }, $columns ); 264 $tableColumns = array_diff( $columns, array_merge( $this->ignoreTableColumns, [ $type . '_id' ] ) ); 265 266 wp_cache_set( 'table_columns_' . $table . '_' . $type, $tableColumns, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE ); 267 } 268 269 return $tableColumns; 270 } 271 272 /** 273 * Changed column name if in reserved column keys 274 * 275 * @param string $type Meta type 276 * @param string $columnName Column name 277 * 278 * @return string Changed column name 279 */ 280 public function translateColumnName( $type, $columnName ) { 281 $suffix = $this->reservedKeysSuffix; 282 $reservedKeys = array_merge( $this->ignoreTableColumns, [ $type . '_id' ] ); 283 284 if ( substr( $columnName, - ( strlen( $suffix ) ) ) === $suffix ) 285 $columnName = str_replace( $suffix, '', $columnName ); 286 elseif ( in_array( $columnName, $reservedKeys ) ) 287 $columnName = $columnName . $suffix; 288 289 return $columnName; 290 } 291 292 /** 293 * Get new column type for exists columns 294 * 295 * @param string $currentColumnType Current column type 296 * @param string $valueType New value type 297 * 298 * @return string 299 */ 300 public function getNewColumnType( $currentColumnType, $valueType ) { 301 if ( $currentColumnType === $valueType ) 302 return $currentColumnType; 303 elseif ( in_array( $currentColumnType, $this->intTypes ) && in_array( $valueType, $this->floatTypes ) ) 304 return $valueType; 305 elseif ( in_array( $currentColumnType, $this->intTypes ) && in_array( $valueType, $this->charTypes ) ) 306 return $valueType; 307 elseif ( in_array( $currentColumnType, $this->dateTypes ) && in_array( $valueType, $this->charTypes ) ) 308 return $valueType; 309 elseif ( in_array( $currentColumnType, $this->intTypes ) && array_search( $currentColumnType, $this->intTypes ) < array_search( $valueType, $this->intTypes ) ) 310 return $valueType; 311 elseif ( in_array( $currentColumnType, $this->floatTypes ) && array_search( $currentColumnType, $this->floatTypes ) < array_search( $valueType, $this->floatTypes ) ) 312 return $valueType; 313 314 return $currentColumnType; 315 } 316 317 /** 318 * Get table column type 319 * 320 * @param string $table Table name 321 * @param string $field Column name 322 * 323 * @return string Column type 324 */ 325 public function getTableColumnType( $table, $field ) { 326 global $wpdb; 327 328 $wpdb->get_results( "SELECT `$field` FROM {$table} LIMIT 1" ); 329 $columnType = $wpdb->get_col_info( 'type', 0 ); 330 331 if ( $columnType === 252 ) 332 return 'TEXT'; 333 else if ( $columnType === 253 ) 334 return 'VARCHAR'; 335 else if ( $columnType === 1 ) 336 return 'TINYINT'; 337 else if ( $columnType === 2 ) 338 return 'SMALLINT'; 339 else if ( $columnType === 9 ) 340 return 'MEDIUMINT'; 341 else if ( $columnType === 3 ) 342 return 'INT'; 343 else if ( $columnType === 8 ) 344 return 'BIGINT'; 345 else if ( $columnType === 4 ) 346 return 'FLOAT'; 347 else if ( $columnType === 5 ) 348 return 'DOUBLE'; 349 else if ( $columnType === 10 ) 350 return 'DATE'; 351 else if ( $columnType === 12 ) 352 return 'DATETIME'; 353 else 354 return false; 355 } 356 357 /** 358 * Return table column type base on value 359 * 360 * @param string $value Meta value 361 * 362 * @return string Column type 363 */ 364 public function getFieldType( $value ) { 365 $valueLength = mb_strlen( $value ); 366 367 if ( $this->isDate( $value ) ) 368 return 'DATE'; 369 elseif ( $this->isDateTime( $value ) ) 370 return 'DATETIME'; 371 // elseif ($this->isJson($value)) 372 // return 'LONGTEXT'; 373 elseif ( is_string( $value ) && $valueLength <= 65535 || is_null( $value ) ) 374 return 'TEXT'; // 'VARCHAR'; 375 elseif ( is_bool( $value ) ) 376 return 'TINYINT'; 377 elseif ( is_float( $value ) ) 378 return 'FLOAT'; 379 elseif ( is_double( $value ) ) 380 return 'DOUBLE'; 381 elseif ( is_numeric( $value ) && intval( $value ) != 0 || is_int( $value ) || $value == 0 ) { 382 $value = intval( $value ); 383 if ( $value >= - 128 && $value <= 127 ) 384 return 'TINYINT'; 385 if ( $value >= - 32768 && $value <= 32767 ) 386 return 'SMALLINT'; 387 if ( $value >= - 8388608 && $value <= 8388607 ) 388 return 'MEDIUMINT'; 389 if ( $value >= - 2147483648 && $value <= 2147483647 ) 390 return 'INT'; 391 else 392 return 'BIGINT'; 393 } else 394 return 'TEXT'; 395 } 396 397 /** 398 * Get table rows count 399 * 400 * @param string $table Table name 401 * 402 * @return int 403 */ 404 public function getTableRowsCount( $table ) { 405 global $wpdb; 406 407 return $wpdb->get_var( "SELECT COUNT(*) FROM $table" ); 408 } 409 410 /** 411 * Get meta table name base on type 412 * 413 * @param string $type Meta type 414 * 415 * @return bool|string 416 */ 417 public function getMetaTableName( $type ) { 418 if ( isset( $this->tables[ $type ] ) ) 419 return $this->tables[ $type ]['table']; 420 else 421 return false; 422 } 423 424 /** 425 * Get WP table name base on type 426 * 427 * @param string $type Meta type 428 * 429 * @return bool|string 430 */ 431 public function getWPPrimaryTableName( $type ) { 432 return $this->wpPrimaryTables[ $type ] ?? false; 433 } 434 435 /** 436 * Get WordPress meta table name base on type 437 * 438 * @param string $type Meta type 439 * 440 * @return bool|string 441 */ 442 public function getWPMetaTableName( $type ) { 443 return $this->wpMetaTables[ $type ] ?? false; 444 } 445 446 /** 447 * Check if user set dont save in default WordPress meta tables 448 * 449 * @param string $type Meta type 450 * 451 * @return bool 452 */ 453 public function checkDontSaveInDefaultTable( $type ) { 454 $defaultMetaSave = $this->Options->getOption( 'dont_save_wpmeta', [] ); 455 456 return isset( $defaultMetaSave[ $type ] ); 457 } 458 459 /** 460 * Check meta can get/add/update 461 * 462 * @param string $type Meta type 463 * 464 * @return bool 465 */ 466 public function checkMetaType( $type ) { 467 $metaSaveTypes = $this->Options->getOption( 'meta_save_types', [] ); 468 469 return isset( $metaSaveTypes[ $type ] ); 470 } 471 472 /** 473 * Check a supported post type 474 * 475 * @param int $postID Post ID 476 * 477 * @return bool 478 */ 479 public function checkPostType( $postID ) { 480 $postType = wp_cache_get( 'post_type_value_' . $postID, WPMETAOPTIMIZER_PLUGIN_KEY ); 481 if ( $postType === false ) { 482 $postType = get_post_type( $postID ); 483 wp_cache_set( 'post_type_value_' . $postID, $postType, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE ); 484 } 485 486 $allowedPostTypes = $this->Options->getOption( 'post_types', [] ); 487 488 return isset( $allowedPostTypes[ $postType ] ); 489 } 490 491 /** 492 * Get allowed post-types 493 * 494 * @return array 495 */ 496 public function getSupportPostTypes() { 497 $allowedPostTypes = $this->Options->getOption( 'post_types', [] ); 498 $allowedPostTypes = array_keys( $allowedPostTypes ); 499 if ( ( $key = array_search( 'hidden', $allowedPostTypes ) ) !== false ) 500 unset( $allowedPostTypes[ $key ] ); 501 502 return array_values( $allowedPostTypes ); 503 } 504 505 /** 506 * Check a meta key exists in black/white list 507 * 508 * @param string $type Meta type 509 * @param string $metaKey Meta key 510 * @param string $listName List name 511 * 512 * @return bool 513 */ 514 public function checkInBlackWhiteList( $type, $metaKey, $listName = 'black_list' ) { 515 if ( $listName === 'black_list' && isset( $this->ignoreWPMetaKeys[ $type ] ) && in_array( $metaKey, $this->ignoreWPMetaKeys[ $type ] ) ) 516 return true; 517 518 $list = $this->Options->getOption( $type . '_' . $listName, '' ); 519 if ( empty( $list ) ) 520 return ''; 521 522 $list = explode( "\n", $list ); 523 $list = str_replace( [ "\n", "\r" ], '', $list ); 524 $list = array_map( 'trim', $list ); 525 526 return in_array( $metaKey, $list ); 527 } 528 529 /** 530 * Check can change WordPress meta keys 531 * 532 * @param string $type Meta type 533 * @param string $metaKey Meta key 534 * 535 * @return bool 536 */ 537 public function checkCanChangeWPMetaKey( $type, $metaKey ) { 538 return ! ( isset( $this->cantChangeWPMetaKeys[ $type ] ) && in_array( $metaKey, $this->cantChangeWPMetaKeys[ $type ] ) ); 539 } 540 541 /** 542 * Get object left items count for import process 543 * 544 * @param string $type Meta type 545 * 546 * @return int 547 */ 548 public function getObjectLeftItemsCount( $type ) { 549 $latestObjectID = $this->Options->getOption( 'import_' . $type . '_latest_id', null ); 550 551 if ( $latestObjectID === 'finished' ) 552 return 0; 553 554 return $this->getLatestObjectID( $type, $latestObjectID, true ); 555 } 556 557 /** 558 * Get latest object ID 559 * 560 * @param string $type Meta type 561 * @param int $latestObjectID Latest changed object ID 562 * @param boolean $findItemsLeft Find items left for an import process 563 * 564 * @return int|null 565 */ 566 public function getLatestObjectID( $type, $latestObjectID = null, $findItemsLeft = false ) { 567 global $wpdb; 568 $primaryColumn = 'ID'; 569 $where = []; 570 $wheres = ""; 571 572 $table = $wpdb->prefix . $type . 's'; 573 574 if ( in_array( $type, [ 'term', 'comment' ] ) ) 575 $primaryColumn = $type . '_ID'; 576 577 if ( $latestObjectID = intval( $latestObjectID ) ) 578 $where[] = "$primaryColumn < $latestObjectID"; 579 580 if ( $type === 'post' ) { 581 $allowedPostTypes = $this->getSupportPostTypes(); 582 583 $postWhere = "(post_status IN ('publish','future','draft','pending','private')"; 584 if ( count( $allowedPostTypes ) ) 585 $postWhere .= " AND post_type IN ('" . implode( "','", $allowedPostTypes ) . "')"; 586 587 if ( in_array( 'attachment', $allowedPostTypes ) ) 588 $postWhere .= " OR post_type = 'attachment'"; 589 $postWhere .= ")"; 590 591 $where[] = $postWhere; 592 } 593 594 if ( count( $where ) ) 595 $wheres = "WHERE " . implode( ' AND ', $where ); 596 597 if ( $findItemsLeft ) 598 $query = "SELECT COUNT(*) FROM $table $wheres"; 599 else 600 $query = "SELECT $primaryColumn FROM $table $wheres ORDER BY $primaryColumn DESC LIMIT 1"; 601 602 return $wpdb->get_var( $query ); 603 } 604 605 /** 606 * Check active an automatic support WP query 607 * 608 * @return void 609 */ 610 public function activeAutomaticallySupportWPQuery() { 611 if ( $this->checkImportFinished() ) { 612 $supportWPQuery = $this->Options->getOption( 'support_wp_query', 0 ) == 1; 613 $activeAutomatically = $this->Options->getOption( 'support_wp_query_active_automatically', false ) == 1; 614 615 if ( ! $supportWPQuery && $activeAutomatically ) 616 $this->Options->setOption( 'support_wp_query', 1 ); 617 } 618 } 619 620 /** 621 * Check support WP Query 622 * 623 * @return boolean 624 */ 625 public function checkSupportWPQuery() { 626 $supportWPQuery = $this->Options->getOption( 'support_wp_query', false ) == 1; 627 $deactivateWhileImport = $this->Options->getOption( 'support_wp_query_deactive_while_import', false ) == 1; 628 629 return $supportWPQuery && ( ! $deactivateWhileImport || $this->checkImportFinished() ); 630 } 631 632 /** 633 * Check import finished 634 * 635 * @param boolean $type Meta type 636 * 637 * @return boolean 638 */ 639 public function checkImportFinished( $type = false ) { 640 // $types = array_keys($this->tables); 641 $types = $this->Options->getOption( 'meta_save_types', [] ); 642 if ( isset( $types['hidden'] ) ) 643 unset( $types['hidden'] ); 644 645 $types = array_keys( $types ); 646 647 if ( $type && in_array( $type, $types ) ) 648 $types = [ $type ]; 649 650 if ( count( $types ) == 0 ) 651 return false; 652 653 foreach ( $types as $type ) { 654 $latestObjectID = $this->Options->getOption( 'import_' . $type . '_latest_id', null ); 655 if ( $latestObjectID !== 'finished' ) 656 return false; 657 } 658 659 return true; 660 } 661 662 /** 663 * Check is JSON 664 * 665 * @param string $string Input string 666 * 667 * @return boolean 668 */ 669 private function isJson( $string ) { 670 if ( ! is_string( $string ) ) 671 return false; 672 json_decode( $string ); 673 674 return json_last_error() === JSON_ERROR_NONE; 675 } 676 677 /** 678 * Check is Date 679 * 680 * @param string $string Input string 681 * 682 * @return boolean 683 */ 684 private function isDate( $string ) { 685 $time = strtotime( $string ); 686 687 if ( $time ) 688 $time = DateTime::createFromFormat( 'Y-m-d', $string ) !== false; 689 690 return $time; 691 } 692 693 /** 694 * Check is DateTime 695 * 696 * @param string $string Input string 697 * 698 * @return boolean 699 */ 700 private function isDateTime( $string ) { 701 return DateTime::createFromFormat( 'Y-m-d H:i:s', $string ) !== false; 702 } 703 704 /** 705 * Returns an instance of class 706 * 707 * @return Helpers 708 */ 709 static function getInstance() { 710 if ( self::$instance == null ) 711 self::$instance = new Helpers(); 712 713 return self::$instance; 714 } 700 715 } -
meta-optimizer/trunk/inc/Install.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 class Install 6 { 7 /** 8 * Install plugin needed 9 * Create plugin tables 10 * Add default options 11 * 12 * @return void 13 */ 14 public static function install() 15 { 16 global $wpdb; 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 17 7 18 if (!function_exists('dbDelta')) 19 require_once(ABSPATH . str_replace('/', DIRECTORY_SEPARATOR, '/wp-admin/includes/upgrade.php')); 8 class Install { 9 /** 10 * Install plugin needed 11 * To Create plugin tables 12 * Add default options 13 * 14 * @return void 15 */ 16 public static function install() { 17 global $wpdb; 20 18 21 $tables = array( 22 'post' => $wpdb->postmeta . '_wpmo', 23 'comment' => $wpdb->commentmeta . '_wpmo', 24 'user' => $wpdb->usermeta . '_wpmo', 25 'term' => $wpdb->termmeta . '_wpmo' 26 ); 19 if ( ! function_exists( 'dbDelta' ) ) 20 require_once( ABSPATH . str_replace( '/', DIRECTORY_SEPARATOR, '/wp-admin/includes/upgrade.php' ) ); 27 21 28 foreach ($tables as $type => $table) { 29 if ($wpdb->get_var("show tables like '$table'") != $table) { 30 $sql = "CREATE TABLE `{$table}` ( 22 $tables = array( 23 'post' => $wpdb->postmeta . '_wpmo', 24 'comment' => $wpdb->commentmeta . '_wpmo', 25 'user' => $wpdb->usermeta . '_wpmo', 26 'term' => $wpdb->termmeta . '_wpmo' 27 ); 28 29 foreach ( $tables as $type => $table ) { 30 if ( $wpdb->get_var( "show tables like '$table'" ) != $table ) { 31 $sql = "CREATE TABLE `{$table}` ( 31 32 `meta_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 32 33 `{$type}_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0', … … 37 38 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; 38 39 39 dbDelta($sql);40 dbDelta("ALTER TABLE `{$table}` ROW_FORMAT=DYNAMIC;");41 }42 }40 dbDelta( $sql ); 41 dbDelta( "ALTER TABLE `{$table}` ROW_FORMAT=DYNAMIC;" ); 42 } 43 } 43 44 44 $currentPluginOptions = get_option('wp_meta_optimizer', false);45 if (!is_array($currentPluginOptions)) {46 $defaultPluginOptions = array(47 'support_wp_query'=> 0,48 'support_wp_query_active_automatically'=> 1,49 'support_wp_query_deactive_while_import' => 1,50 'meta_save_types'=> [51 'post' => '1',52 'comment' => '1',53 'user' => '1',54 'term' => '1',55 ],56 'import'=> [57 'post' => '1',58 'comment' => '1',59 'user' => '1',60 'term' => '1',61 ],62 'post_types'=> [63 'post' => '1',64 'page' => '1' 65 ],66 'import_items_number'=> 167 );45 $currentPluginOptions = get_option( 'wp_meta_optimizer', false ); 46 if ( ! is_array( $currentPluginOptions ) ) { 47 $defaultPluginOptions = array( 48 'support_wp_query' => 0, 49 'support_wp_query_active_automatically' => 1, 50 'support_wp_query_deactive_while_import' => 1, 51 'meta_save_types' => [ 52 'post' => 1, 53 'comment' => 1, 54 'user' => 1, 55 'term' => 1, 56 ], 57 'import' => [ 58 'post' => 1, 59 'comment' => 1, 60 'user' => 1, 61 'term' => 1, 62 ], 63 'post_types' => [ 64 'post' => 1, 65 'page' => 1 66 ], 67 'import_items_number' => 1 68 ); 68 69 69 update_option('wp_meta_optimizer', $defaultPluginOptions); 70 } 70 update_option( 'wp_meta_optimizer', $defaultPluginOptions ); 71 } else { 72 $oldVersion = get_option( 'wp_meta_optimizer_version', '1.0' ); 71 73 72 if (!function_exists('get_plugin_data')) 73 require_once(ABSPATH . 'wp-admin/includes/plugin.php'); 74 $pluginData = get_plugin_data(WPMETAOPTIMIZER_PLUGIN_FILE_PATH); 75 update_option('wp_meta_optimizer_version', $pluginData['Version']); 76 } 74 if ( version_compare( $oldVersion, '1.1', '<' ) ) { 75 $currentPluginOptions['import']['post'] = 1; 76 $currentPluginOptions['import_post_latest_id'] = null; 77 78 update_option( 'wp_meta_optimizer', $currentPluginOptions ); 79 } 80 } 81 82 if ( ! function_exists( 'get_plugin_data' ) ) 83 require_once( ABSPATH . 'wp-admin/includes/plugin.php' ); 84 $pluginData = get_plugin_data( WPMETAOPTIMIZER_PLUGIN_FILE_PATH ); 85 update_option( 'wp_meta_optimizer_version', $pluginData['Version'] ); 86 } 77 87 } -
meta-optimizer/trunk/inc/Integration.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 class Integration extends Base 6 { 7 public static $instance = null; 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 8 7 9 function __construct() 10 { 11 add_filter('acf/pre_load_metadata', [$this, 'acfGetMeta'], 10, 4); 12 } 8 class Integration extends Base { 9 public static $instance = null; 13 10 14 /** 15 * Advanced custom field 16 * 17 * @param null $check Default check value (null) 18 * @param int $post_id Post ID 19 * @param string $name Meta name 20 * @param boolean $hidden Meta key is hidden 21 * @return null|string 22 */ 23 function acfGetMeta($check, $post_id, $name, $hidden) 24 { 25 if (!function_exists('acf_decode_post_id')) 26 return $check; // null 11 function __construct() { 12 add_filter( 'acf/pre_load_metadata', [ $this, 'acfGetMeta' ], 10, 4 ); 13 } 27 14 28 // Decode $post_id for $type and $id. 29 $decoded = acf_decode_post_id($post_id); 30 $id = $decoded['id']; 31 $type = $decoded['type']; 15 /** 16 * Advanced custom field 17 * 18 * @param null $check Default check value (null) 19 * @param int $post_id Post ID 20 * @param string $name Meta name 21 * @param boolean $hidden Meta key is hidden 22 * 23 * @return null|string|array 24 */ 25 function acfGetMeta( $check, $post_id, $name, $hidden ) { 26 if ( ! function_exists( 'acf_decode_post_id' ) ) 27 return $check; // null 32 28 33 // Hidden meta uses an underscore prefix. 34 $prefix = $hidden ? '_' : ''; 29 // Decode $post_id for $type and $id. 30 $decoded = acf_decode_post_id( $post_id ); 31 $id = $decoded['id']; 32 $type = $decoded['type']; 35 33 36 // Bail early if no $id (possible during new acf_form). 37 if (!$id) 38 return $check; // null 34 // Hidden meta uses an underscore prefix. 35 $prefix = $hidden ? '_' : ''; 39 36 40 if ($type !== 'option') { 41 $metaValue = get_metadata($type, $id, "{$prefix}{$name}", true); 42 return is_array($metaValue) && isset($metaValue[0]) ? $metaValue[0] : $metaValue; 43 } 37 // Bail early if no $id (possible during new acf_form). 38 if ( ! $id ) 39 return $check; // null 44 40 45 return $check; // null 46 } 41 if ( $type !== 'option' ) { 42 $metaValue = get_metadata( $type, $id, "{$prefix}{$name}", true ); 47 43 48 /** 49 * Returns an instance of class 50 * @return Integration 51 */ 52 static function getInstance() 53 { 54 if (self::$instance == null) 55 self::$instance = new Integration(); 44 return is_array( $metaValue ) && isset( $metaValue[0] ) ? $metaValue[0] : $metaValue; 45 } 56 46 57 return self::$instance; 58 } 47 return $check; // null 48 } 49 50 /** 51 * Returns an instance of class 52 * 53 * @return Integration 54 */ 55 static function getInstance() { 56 if ( self::$instance == null ) 57 self::$instance = new Integration(); 58 59 return self::$instance; 60 } 59 61 } -
meta-optimizer/trunk/inc/MetaQuery.php
r2802191 r2960316 2 2 3 3 namespace WPMetaOptimizer; 4 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 4 7 5 8 /** 6 9 * Meta API: MetaQuery class 7 * @copyright This class base on code from WordPress Meta Query class (WP_Meta_Query).8 10 * 9 * @package WPMetaOptimizer 11 * @copyright This class base on code from WordPress Meta Query class (WP_Meta_Query). 12 * 13 * @package WPMetaOptimizer 10 14 * @subpackage Meta 11 * @since 4.4.015 * @since 4.4.0 12 16 */ 13 17 … … 24 28 * @since 1.0 25 29 */ 26 class MetaQuery 27 { 30 class MetaQuery { 28 31 /** 29 32 * Helpers class. … … 111 114 * Constructor. 112 115 * 113 * @since 3.2.0 114 * @since 4.2.0 Introduced support for naming query clauses by associative array keys. 115 * @since 5.1.0 Introduced `$compare_key` clause parameter, which enables LIKE key matches. 116 * @since 5.3.0 Increased the number of operators available to `$compare_key`. Introduced `$type_key`, 117 * which enables the `$key` to be cast to a new data type for comparisons. 118 * 119 * @param array $meta_query { 120 * Array of meta query clauses. When first-order clauses or sub-clauses use strings as 121 * their array keys, they may be referenced in the 'orderby' parameter of the parent query. 122 * 123 * @type string $relation Optional. The MySQL keyword used to join the clauses of the query. 124 * Accepts 'AND' or 'OR'. Default 'AND'. 125 * @type array ...$0 { 126 * Optional. An array of first-order clause parameters, or another fully-formed meta query. 127 * 128 * @type string|string[] $key Meta key or keys to filter by. 129 * @type string $compare_key MySQL operator used for comparing the $key. Accepts: 116 * @param array $meta_query { 117 * Array of meta query clauses. When first-order clauses or sub-clauses 118 * use strings as their array keys, they may be referenced in the 119 * 'orderby' parameter of the parent query. 120 * 121 * @type string $relation Optional. The MySQL keyword used to join the clauses of the query. 122 * Accepts 'AND' or 'OR'. Default 'AND'. 123 * @type array ...$0 { 124 * Optional. An array of first-order clause parameters, or another 125 * fully-formed meta query. 126 * 127 * @type string|string[] $key Meta key or keys to filter by. 128 * @type string $compare_key MySQL operator used for comparing the $key. Accepts: 130 129 * - '=' 131 130 * - '!=' … … 140 139 * - 'NOT EXISTS' (alias of '!=') 141 140 * Default is 'IN' when `$key` is an array, '=' otherwise. 142 * @type string $type_keyMySQL data type that the meta_key column will be CAST to for141 * @type string $type_key MySQL data type that the meta_key column will be CAST to for 143 142 * comparisons. Accepts 'BINARY' for case-sensitive regular expression 144 143 * comparisons. Default is ''. 145 * @type string|string[] $valueMeta value or values to filter by.146 * @type string $compareMySQL operator used for comparing the $value. Accepts:144 * @type string|string[] $value Meta value or values to filter by. 145 * @type string $compare MySQL operator used for comparing the $value. Accepts: 147 146 * - '=', 148 147 * - '!=' … … 163 162 * - 'NOT EXISTS' 164 163 * Default is 'IN' when `$value` is an array, '=' otherwise. 165 * @type string $typeMySQL data type that the meta_value column will be CAST to for164 * @type string $type MySQL data type that the meta_value column will be CAST to for 166 165 * comparisons. Accepts: 167 166 * - 'NUMERIC' … … 175 174 * - 'UNSIGNED' 176 175 * Default is 'CHAR'. 177 * } 178 * } 179 */ 180 public function __construct($meta_query = false, $helpers = false) 181 { 182 if ($helpers) 176 * } 177 * } 178 * @since 4.2.0 Introduced support for naming query clauses by associative array keys. 179 * @since 5.1.0 Introduced `$compare_key` clause parameter, which enables LIKE key matches. 180 * @since 5.3.0 Increased the number of operators available to `$compare_key`. Introduced `$type_key`, 181 * which enables the `$key` to be cast to a new data type for 182 * comparisons. 183 * 184 * @since 3.2.0 185 */ 186 public function __construct( $meta_query = false, $helpers = false ) { 187 if ( $helpers ) 183 188 $this->Helpers = $helpers; 184 189 else 185 190 $this->Helpers = Helpers::getInstance(); 186 191 187 if ( !$meta_query)192 if ( ! $meta_query ) 188 193 return; 189 194 190 if ( isset($meta_query['relation']) && 'OR' === strtoupper($meta_query['relation'])) {195 if ( isset( $meta_query['relation'] ) && 'OR' === strtoupper( $meta_query['relation'] ) ) { 191 196 $this->relation = 'OR'; 192 197 } else { … … 194 199 } 195 200 196 $this->queries = $this->sanitize_query( $meta_query);201 $this->queries = $this->sanitize_query( $meta_query ); 197 202 } 198 203 … … 202 207 * Eliminates empty items and ensures that a 'relation' is set. 203 208 * 204 * @since 4.1.0205 *206 209 * @param array $queries Array of query clauses. 210 * 207 211 * @return array Sanitized array of query clauses. 208 */ 209 public function sanitize_query($queries) 210 { 212 * @since 4.1.0 213 * 214 */ 215 public function sanitize_query( $queries ) { 211 216 $clean_queries = array(); 212 217 213 if ( !is_array($queries)) {218 if ( ! is_array( $queries ) ) { 214 219 return $clean_queries; 215 220 } 216 221 217 foreach ( $queries as $key => $query) {218 if ( 'relation' === $key) {222 foreach ( $queries as $key => $query ) { 223 if ( 'relation' === $key ) { 219 224 $relation = $query; 220 } elseif ( !is_array($query)) {225 } elseif ( ! is_array( $query ) ) { 221 226 continue; 222 227 223 228 // First-order clause. 224 } elseif ( $this->is_first_order_clause($query)) {225 if ( isset($query['value']) && array() === $query['value']) {226 unset( $query['value']);229 } elseif ( $this->is_first_order_clause( $query ) ) { 230 if ( isset( $query['value'] ) && array() === $query['value'] ) { 231 unset( $query['value'] ); 227 232 } 228 233 229 $clean_queries[ $key] = $query;234 $clean_queries[ $key ] = $query; 230 235 231 236 // Otherwise, it's a nested query, so we recurse. 232 237 } else { 233 $cleaned_query = $this->sanitize_query( $query);234 235 if ( !empty($cleaned_query)) {236 $clean_queries[ $key] = $cleaned_query;238 $cleaned_query = $this->sanitize_query( $query ); 239 240 if ( ! empty( $cleaned_query ) ) { 241 $clean_queries[ $key ] = $cleaned_query; 237 242 } 238 243 } 239 244 } 240 245 241 if ( empty($clean_queries)) {246 if ( empty( $clean_queries ) ) { 242 247 return $clean_queries; 243 248 } 244 249 245 250 // Sanitize the 'relation' key provided in the query. 246 if ( isset($relation) && 'OR' === strtoupper($relation)) {251 if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) { 247 252 $clean_queries['relation'] = 'OR'; 248 253 $this->has_or_relation = true; … … 253 258 * simplifies the logic around combining key-only queries. 254 259 */ 255 } elseif ( 1 === count($clean_queries)) {260 } elseif ( 1 === count( $clean_queries ) ) { 256 261 $clean_queries['relation'] = 'OR'; 257 262 … … 270 275 * a 'value' array key. 271 276 * 272 * @since 4.1.0273 *274 277 * @param array $query Meta query arguments. 278 * 275 279 * @return bool Whether the query clause is a first-order clause. 276 */ 277 protected function is_first_order_clause($query) 278 { 279 return isset($query['key']) || isset($query['value']); 280 * @since 4.1.0 281 * 282 */ 283 protected function is_first_order_clause( $query ) { 284 return isset( $query['key'] ) || isset( $query['value'] ); 280 285 } 281 286 … … 283 288 * Constructs a meta query based on 'meta_*' query vars 284 289 * 290 * @param array $qv The query variables 291 * 285 292 * @since 3.2.0 286 293 * 287 * @param array $qv The query variables 288 */ 289 public function parse_query_vars($qv) 290 { 294 */ 295 public function parse_query_vars( $qv ) { 291 296 $meta_query = array(); 292 297 … … 298 303 */ 299 304 $primary_meta_query = array(); 300 foreach ( array('key', 'compare', 'type', 'compare_key', 'type_key') as $key) {301 if ( !empty($qv["meta_$key"])) {302 $primary_meta_query[ $key] = $qv["meta_$key"];305 foreach ( array( 'key', 'compare', 'type', 'compare_key', 'type_key' ) as $key ) { 306 if ( ! empty( $qv["meta_$key"] ) ) { 307 $primary_meta_query[ $key ] = $qv["meta_$key"]; 303 308 } 304 309 } 305 310 306 311 // WP_Query sets 'meta_value' = '' by default. 307 if ( isset($qv['meta_value']) && '' !== $qv['meta_value'] && (!is_array($qv['meta_value']) || $qv['meta_value'])) {312 if ( isset( $qv['meta_value'] ) && '' !== $qv['meta_value'] && ( ! is_array( $qv['meta_value'] ) || $qv['meta_value'] ) ) { 308 313 $primary_meta_query['value'] = $qv['meta_value']; 309 314 } 310 315 311 $existing_meta_query = isset( $qv['meta_query']) && is_array($qv['meta_query']) ? $qv['meta_query'] : array();312 313 if ( !empty($primary_meta_query) && !empty($existing_meta_query)) {316 $existing_meta_query = isset( $qv['meta_query'] ) && is_array( $qv['meta_query'] ) ? $qv['meta_query'] : array(); 317 318 if ( ! empty( $primary_meta_query ) && ! empty( $existing_meta_query ) ) { 314 319 $meta_query = array( 315 320 'relation' => 'AND', … … 317 322 $existing_meta_query, 318 323 ); 319 } elseif ( !empty($primary_meta_query)) {324 } elseif ( ! empty( $primary_meta_query ) ) { 320 325 $meta_query = array( 321 326 $primary_meta_query, 322 327 ); 323 } elseif ( !empty($existing_meta_query)) {328 } elseif ( ! empty( $existing_meta_query ) ) { 324 329 $meta_query = $existing_meta_query; 325 330 } 326 331 327 $this->__construct( $meta_query);332 $this->__construct( $meta_query ); 328 333 } 329 334 … … 331 336 * Return the appropriate alias for the given meta type if applicable. 332 337 * 338 * @param string $type MySQL type to cast meta_value. 339 * 340 * @return string MySQL type. 333 341 * @since 3.7.0 334 342 * 335 * @param string $type MySQL type to cast meta_value. 336 * @return string MySQL type. 337 */ 338 public function get_cast_for_type($type = '') 339 { 340 if (empty($type)) { 343 */ 344 public function get_cast_for_type( $type = '' ) { 345 if ( empty( $type ) ) { 341 346 return 'CHAR'; 342 347 } 343 348 344 $meta_type = strtoupper( $type);345 346 if ( !preg_match('/^(?:BINARY|CHAR|DATE|DATETIME|SIGNED|UNSIGNED|TIME|NUMERIC(?:\(\d+(?:,\s?\d+)?\))?|DECIMAL(?:\(\d+(?:,\s?\d+)?\))?)$/', $meta_type)) {349 $meta_type = strtoupper( $type ); 350 351 if ( ! preg_match( '/^(?:BINARY|CHAR|DATE|DATETIME|SIGNED|UNSIGNED|TIME|NUMERIC(?:\(\d+(?:,\s?\d+)?\))?|DECIMAL(?:\(\d+(?:,\s?\d+)?\))?)$/', $meta_type ) ) { 347 352 return 'CHAR'; 348 353 } 349 354 350 if ( 'NUMERIC' === $meta_type) {355 if ( 'NUMERIC' === $meta_type ) { 351 356 $meta_type = 'SIGNED'; 352 357 } … … 357 362 /** 358 363 * Generates SQL clauses to be appended to a main query. 359 *360 * @since 3.2.0361 364 * 362 365 * @param string $type Type of meta. Possible values include but are not limited … … 366 369 * @param object $context Optional. The main query object that corresponds to the type, for 367 370 * example a `WP_Query`, `WP_User_Query`, or `WP_Site_Query`. 371 * 368 372 * @return string[]|false { 369 373 * Array containing JOIN and WHERE SQL clauses to append to the main query, 370 374 * or false if no table exists for the requested meta type. 371 375 * 372 * @type string $join SQL fragment to append to the main JOIN clause. 373 * @type string $where SQL fragment to append to the main WHERE clause. 374 * } 375 */ 376 public function get_sql($type, $primary_table, $primary_id_column, $context = null) 377 { 378 $meta_table = $this->Helpers->getMetaTableName($type); 379 if (!$meta_table) 376 * @type string $join SQL fragment to append to the main JOIN clause. 377 * @type string $where SQL fragment to append to the main WHERE clause. 378 * } 379 * @since 3.2.0 380 * 381 */ 382 public function get_sql( $type, $primary_table, $primary_id_column, $context = null ) { 383 $meta_table = $this->Helpers->getMetaTableName( $type ); 384 if ( ! $meta_table ) 380 385 return false; 381 386 … … 383 388 384 389 $this->meta_table = $meta_table; 385 $this->meta_id_column = sanitize_key( $type . '_id');390 $this->meta_id_column = sanitize_key( $type . '_id' ); 386 391 387 392 $this->primary_table = $primary_table; … … 394 399 * be LEFT. Otherwise posts with no metadata will be excluded from results. 395 400 */ 396 if ( false !== strpos($sql['join'], 'LEFT JOIN')) {397 $sql['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $sql['join']);401 if ( false !== strpos( $sql['join'], 'LEFT JOIN' ) ) { 402 $sql['join'] = str_replace( 'INNER JOIN', 'LEFT JOIN', $sql['join'] ); 398 403 } 399 404 400 405 /** 401 406 * Filters the meta query's generated SQL. 402 *403 * @since 3.1.0404 407 * 405 408 * @param string[] $sql Array containing the query's JOIN and WHERE clauses. … … 411 414 * @param object $context The main query object that corresponds to the type, for 412 415 * example a `WP_Query`, `WP_User_Query`, or `WP_Site_Query`. 416 * 417 * @since 3.1.0 418 * 413 419 */ 414 return apply_filters_ref_array('get_meta_sql_wpmo', array($sql, $this->queries, $type, $primary_table, $primary_id_column, $context)); 420 return apply_filters_ref_array( 'get_meta_sql_wpmo', array( 421 $sql, 422 $this->queries, 423 $type, 424 $primary_table, 425 $primary_id_column, 426 $context 427 ) ); 415 428 } 416 429 … … 421 434 * out to maintain parity with the other Query classes. 422 435 * 423 * @since 4.1.0424 *425 436 * @return string[] { 426 437 * Array containing JOIN and WHERE SQL clauses to append to the main query. 427 438 * 428 * @type string $join SQL fragment to append to the main JOIN clause. 429 * @type string $where SQL fragment to append to the main WHERE clause. 430 * } 431 */ 432 protected function get_sql_clauses() 433 { 439 * @type string $join SQL fragment to append to the main JOIN clause. 440 * @type string $where SQL fragment to append to the main WHERE clause. 441 * } 442 * @since 4.1.0 443 * 444 */ 445 protected function get_sql_clauses() { 434 446 /* 435 447 * $queries are passed by reference to get_sql_for_query() for recursion. … … 437 449 */ 438 450 $queries = $this->queries; 439 $sql = $this->get_sql_for_query( $queries);440 441 if ( !empty($sql['where'])) {451 $sql = $this->get_sql_for_query( $queries ); 452 453 if ( ! empty( $sql['where'] ) ) { 442 454 $sql['where'] = ' AND ' . $sql['where']; 443 455 } … … 451 463 * If nested subqueries are found, this method recurses the tree to 452 464 * produce the properly nested SQL. 453 *454 * @since 4.1.0455 465 * 456 466 * @param array $query Query to parse (passed by reference). 457 467 * @param int $depth Optional. Number of tree levels deep we currently are. 458 468 * Used to calculate indentation. Default 0. 469 * 459 470 * @return string[] { 460 471 * Array containing JOIN and WHERE SQL clauses to append to a single query array. 461 472 * 462 * @type string $join SQL fragment to append to the main JOIN clause. 463 * @type string $where SQL fragment to append to the main WHERE clause. 464 * } 465 */ 466 protected function get_sql_for_query(&$query, $depth = 0) 467 { 473 * @type string $join SQL fragment to append to the main JOIN clause. 474 * @type string $where SQL fragment to append to the main WHERE clause. 475 * } 476 * @since 4.1.0 477 * 478 */ 479 protected function get_sql_for_query( &$query, $depth = 0 ) { 468 480 $sql_chunks = array( 469 481 'join' => array(), … … 477 489 478 490 $indent = ''; 479 for ( $i = 0; $i < $depth; $i++) {491 for ( $i = 0; $i < $depth; $i ++ ) { 480 492 $indent .= ' '; 481 493 } 482 494 483 foreach ( $query as $key => &$clause) {484 if ( 'relation' === $key) {495 foreach ( $query as $key => &$clause ) { 496 if ( 'relation' === $key ) { 485 497 $relation = $query['relation']; 486 } elseif ( is_array($clause)) {498 } elseif ( is_array( $clause ) ) { 487 499 488 500 // This is a first-order clause. 489 if ( $this->is_first_order_clause($clause)) {490 $clause_sql = $this->get_sql_for_clause( $clause, $query, $key);491 492 $where_count = count( $clause_sql['where']);493 if ( !$where_count) {501 if ( $this->is_first_order_clause( $clause ) ) { 502 $clause_sql = $this->get_sql_for_clause( $clause, $query, $key ); 503 504 $where_count = count( $clause_sql['where'] ); 505 if ( ! $where_count ) { 494 506 $sql_chunks['where'][] = ''; 495 } elseif ( 1 === $where_count) {507 } elseif ( 1 === $where_count ) { 496 508 $sql_chunks['where'][] = $clause_sql['where'][0]; 497 509 } else { 498 $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where']) . ' )';510 $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )'; 499 511 } 500 512 501 $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join']);513 $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] ); 502 514 // This is a subquery, so we recurse. 503 515 } else { 504 $clause_sql = $this->get_sql_for_query( $clause, $depth + 1);516 $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 ); 505 517 506 518 $sql_chunks['where'][] = $clause_sql['where']; … … 511 523 512 524 // Filter to remove empties. 513 $sql_chunks['join'] = array_filter( $sql_chunks['join']);514 $sql_chunks['where'] = array_filter( $sql_chunks['where']);515 516 if ( empty($relation)) {525 $sql_chunks['join'] = array_filter( $sql_chunks['join'] ); 526 $sql_chunks['where'] = array_filter( $sql_chunks['where'] ); 527 528 if ( empty( $relation ) ) { 517 529 $relation = 'AND'; 518 530 } 519 531 520 532 // Filter duplicate JOIN clauses and combine into a single string. 521 if ( !empty($sql_chunks['join'])) {522 $sql['join'] = implode( ' ', array_unique($sql_chunks['join']));533 if ( ! empty( $sql_chunks['join'] ) ) { 534 $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) ); 523 535 } 524 536 525 537 // Generate a single WHERE clause with proper brackets and indentation. 526 if ( !empty($sql_chunks['where'])) {527 $sql['where'] = '( ' . "\n " . $indent . implode( ' ' . "\n " . $indent . $relation . ' ' . "\n " . $indent, $sql_chunks['where']) . "\n" . $indent . ')';538 if ( ! empty( $sql_chunks['where'] ) ) { 539 $sql['where'] = '( ' . "\n " . $indent . implode( ' ' . "\n " . $indent . $relation . ' ' . "\n " . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')'; 528 540 } 529 541 … … 535 547 * 536 548 * "First-order" means that it's an array with a 'key' or 'value'. 537 *538 * @since 4.1.0539 *540 * @global wpdb $wpdb WordPress database abstraction object.541 549 * 542 550 * @param array $clause Query clause (passed by reference). … … 544 552 * @param string $clause_key Optional. The array key used to name the clause in the original `$meta_query` 545 553 * parameters. If not provided, a key will be generated automatically. 554 * 546 555 * @return array { 547 556 * Array containing JOIN and WHERE SQL clauses to append to a first-order query. 548 557 * 549 * @type string $join SQL fragment to append to the main JOIN clause. 550 * @type string $where SQL fragment to append to the main WHERE clause. 551 * } 552 */ 553 public function get_sql_for_clause(&$clause, $parent_query, $clause_key = '') 554 { 558 * @type string $join SQL fragment to append to the main JOIN clause. 559 * @type string $where SQL fragment to append to the main WHERE clause. 560 * } 561 * @since 4.1.0 562 * 563 * @global \wpdb $wpdb WordPress database abstraction object. 564 * 565 */ 566 public function get_sql_for_clause( &$clause, $parent_query, $clause_key = '' ) { 555 567 global $wpdb; 556 568 … … 560 572 ); 561 573 562 if ( isset($clause['compare'])) {563 $clause['compare'] = strtoupper( $clause['compare']);574 if ( isset( $clause['compare'] ) ) { 575 $clause['compare'] = strtoupper( $clause['compare'] ); 564 576 } else { 565 $clause['compare'] = isset( $clause['value']) && is_array($clause['value']) ? 'IN' : '=';577 $clause['compare'] = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '='; 566 578 } 567 579 … … 589 601 ); 590 602 591 if ( !in_array($clause['compare'], $non_numeric_operators, true) && !in_array($clause['compare'], $numeric_operators, true)) {603 if ( ! in_array( $clause['compare'], $non_numeric_operators, true ) && ! in_array( $clause['compare'], $numeric_operators, true ) ) { 592 604 $clause['compare'] = '='; 593 605 } 594 606 595 if ( isset($clause['compare_key'])) {596 $clause['compare_key'] = strtoupper( $clause['compare_key']);607 if ( isset( $clause['compare_key'] ) ) { 608 $clause['compare_key'] = strtoupper( $clause['compare_key'] ); 597 609 } else { 598 $clause['compare_key'] = isset( $clause['key']) && is_array($clause['key']) ? 'IN' : '=';599 } 600 601 if ( !in_array($clause['compare_key'], $non_numeric_operators, true)) {610 $clause['compare_key'] = isset( $clause['key'] ) && is_array( $clause['key'] ) ? 'IN' : '='; 611 } 612 613 if ( ! in_array( $clause['compare_key'], $non_numeric_operators, true ) ) { 602 614 $clause['compare_key'] = '='; 603 615 } … … 610 622 611 623 // We prefer to avoid joins if possible. Look for an existing join compatible with this clause. 612 $alias = $this->find_compatible_table_alias( $clause, $parent_query);613 $i = count( $this->table_aliases);624 $alias = $this->find_compatible_table_alias( $clause, $parent_query ); 625 $i = count( $this->table_aliases ); 614 626 615 627 // TODO: Disabled, since duplicate joins are not needed 616 if ( !$i && false === $alias) {628 if ( ! $i && false === $alias ) { 617 629 $alias = $i ? 'mt' . $i : $this->meta_table; 618 630 619 631 // JOIN clauses for NOT EXISTS have their own syntax. 620 if ( 'NOT EXISTS' === $meta_compare) {632 if ( 'NOT EXISTS' === $meta_compare ) { 621 633 $join .= " INNER JOIN $this->meta_table"; 622 634 $join .= $i ? " AS $alias" : ''; … … 639 651 $this->table_aliases[] = $alias; 640 652 $sql_chunks['join'][] = $join; 641 } else{653 } else { 642 654 $alias = $this->meta_table; 643 655 } … … 647 659 648 660 // Determine the data type. 649 $_meta_type = isset( $clause['type']) ? $clause['type'] : '';650 $meta_type = $this->get_cast_for_type( $_meta_type);661 $_meta_type = isset( $clause['type'] ) ? $clause['type'] : ''; 662 $meta_type = $this->get_cast_for_type( $_meta_type ); 651 663 $clause['cast'] = $meta_type; 652 664 653 665 // Fallback for clause keys is the table alias. Key must be a string. 654 if ( is_int($clause_key) || !$clause_key) {666 if ( is_int( $clause_key ) || ! $clause_key ) { 655 667 $clause_key = $clause['alias']; 656 668 } … … 659 671 $iterator = 1; 660 672 $clause_key_base = $clause_key; 661 while ( isset($this->clauses[$clause_key])) {673 while ( isset( $this->clauses[ $clause_key ] ) ) { 662 674 $clause_key = $clause_key_base . '-' . $iterator; 663 $iterator ++;675 $iterator ++; 664 676 } 665 677 666 678 // Store the clause in our flat array. 667 $this->clauses[ $clause_key] = &$clause;679 $this->clauses[ $clause_key ] = &$clause; 668 680 669 681 // Next, build the WHERE clause. 670 682 671 683 // meta_key. 672 if ( array_key_exists('key', $clause)) {673 if ( 'NOT EXISTS' === $meta_compare) {684 if ( array_key_exists( 'key', $clause ) ) { 685 if ( 'NOT EXISTS' === $meta_compare ) { 674 686 $sql_chunks['where'][] = $alias . '.' . $clause['key'] . ' IS NULL'; 675 687 } else { // TODO: Column name query … … 680 692 * nested clause. 681 693 */ 682 if (in_array($meta_compare_key, array('!=', 'NOT IN', 'NOT LIKE', 'NOT EXISTS', 'NOT REGEXP'), true)) { 694 if ( in_array( $meta_compare_key, array( 695 '!=', 696 'NOT IN', 697 'NOT LIKE', 698 'NOT EXISTS', 699 'NOT REGEXP' 700 ), true ) ) { 683 701 // Negative clauses may be reused. 684 $i = count( $this->table_aliases);702 $i = count( $this->table_aliases ); 685 703 $subquery_alias = $i ? 'mt' . $i : $this->meta_table; 686 704 $this->table_aliases[] = $subquery_alias; 687 705 688 $meta_compare_string_start = 'NOT EXISTS (';706 $meta_compare_string_start = 'NOT EXISTS ('; 689 707 $meta_compare_string_start .= "SELECT 1 FROM $wpdb->postmeta $subquery_alias "; 690 708 $meta_compare_string_start .= "WHERE $subquery_alias.post_ID = $alias.post_ID "; 691 $meta_compare_string_end = 'LIMIT 1';709 $meta_compare_string_end = 'LIMIT 1'; 692 710 $meta_compare_string_end .= ')'; 693 711 } 694 712 695 switch ( $meta_compare_key) {713 switch ( $meta_compare_key ) { 696 714 case '=': 697 715 case 'EXISTS': … … 699 717 break; 700 718 case 'LIKE': 701 $meta_compare_value = '%' . $wpdb->esc_like( trim($clause['key'])) . '%';702 $where = $wpdb->prepare( "$alias.meta_key LIKE %s", $meta_compare_value); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared719 $meta_compare_value = '%' . $wpdb->esc_like( trim( $clause['key'] ) ) . '%'; 720 $where = $wpdb->prepare( "$alias.meta_key LIKE %s", $meta_compare_value ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 703 721 break; 704 722 case 'IN': 705 $meta_compare_string = "$alias.meta_key IN (" . substr( str_repeat(',%s', count($clause['key'])), 1) . ')';706 $where = $wpdb->prepare( $meta_compare_string, $clause['key']); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared723 $meta_compare_string = "$alias.meta_key IN (" . substr( str_repeat( ',%s', count( $clause['key'] ) ), 1 ) . ')'; 724 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 707 725 break; 708 726 case 'RLIKE': 709 727 case 'REGEXP': 710 728 $operator = $meta_compare_key; 711 if ( isset($clause['type_key']) && 'BINARY' === strtoupper($clause['type_key'])) {729 if ( isset( $clause['type_key'] ) && 'BINARY' === strtoupper( $clause['type_key'] ) ) { 712 730 $cast = 'BINARY'; 713 731 } else { 714 732 $cast = ''; 715 733 } 716 $where = $wpdb->prepare( "$alias.meta_key $operator $cast %s", trim($clause['key'])); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared734 $where = $wpdb->prepare( "$alias.meta_key $operator $cast %s", trim( $clause['key'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared 717 735 break; 718 736 … … 720 738 case 'NOT EXISTS': 721 739 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key = %s " . $meta_compare_string_end; 722 $where = $wpdb->prepare( $meta_compare_string, $clause['key']); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared740 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 723 741 break; 724 742 case 'NOT LIKE': 725 743 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key LIKE %s " . $meta_compare_string_end; 726 744 727 $meta_compare_value = '%' . $wpdb->esc_like( trim($clause['key'])) . '%';728 $where = $wpdb->prepare( $meta_compare_string, $meta_compare_value); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared745 $meta_compare_value = '%' . $wpdb->esc_like( trim( $clause['key'] ) ) . '%'; 746 $where = $wpdb->prepare( $meta_compare_string, $meta_compare_value ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 729 747 break; 730 748 case 'NOT IN': 731 $array_subclause = '(' . substr( str_repeat(',%s', count($clause['key'])), 1) . ') ';749 $array_subclause = '(' . substr( str_repeat( ',%s', count( $clause['key'] ) ), 1 ) . ') '; 732 750 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key IN " . $array_subclause . $meta_compare_string_end; 733 $where = $wpdb->prepare( $meta_compare_string, $clause['key']); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared751 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 734 752 break; 735 753 case 'NOT REGEXP': 736 754 $operator = $meta_compare_key; 737 if ( isset($clause['type_key']) && 'BINARY' === strtoupper($clause['type_key'])) {755 if ( isset( $clause['type_key'] ) && 'BINARY' === strtoupper( $clause['type_key'] ) ) { 738 756 $cast = 'BINARY'; 739 757 } else { … … 742 760 743 761 $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key REGEXP $cast %s " . $meta_compare_string_end; 744 $where = $wpdb->prepare( $meta_compare_string, $clause['key']); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared762 $where = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 745 763 break; 746 764 } … … 751 769 752 770 // meta_value. 753 if ( array_key_exists('value', $clause)) {771 if ( array_key_exists( 'value', $clause ) ) { 754 772 $meta_value = $clause['value']; 755 773 756 if ( in_array($meta_compare, array('IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'), true)) {757 if ( !is_array($meta_value)) {758 $meta_value = preg_split( '/[,\s]+/', $meta_value);774 if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ), true ) ) { 775 if ( ! is_array( $meta_value ) ) { 776 $meta_value = preg_split( '/[,\s]+/', $meta_value ); 759 777 } 760 } elseif ( is_string($meta_value)) {761 $meta_value = trim( $meta_value);762 } 763 764 switch ( $meta_compare) {778 } elseif ( is_string( $meta_value ) ) { 779 $meta_value = trim( $meta_value ); 780 } 781 782 switch ( $meta_compare ) { 765 783 case 'IN': 766 784 case 'NOT IN': 767 $meta_compare_string = '(' . substr( str_repeat(',%s', count($meta_value)), 1) . ')';768 $where = $wpdb->prepare( $meta_compare_string, $meta_value);785 $meta_compare_string = '(' . substr( str_repeat( ',%s', count( $meta_value ) ), 1 ) . ')'; 786 $where = $wpdb->prepare( $meta_compare_string, $meta_value ); 769 787 break; 770 788 771 789 case 'BETWEEN': 772 790 case 'NOT BETWEEN': 773 $where = $wpdb->prepare( '%s AND %s', $meta_value[0], $meta_value[1]);791 $where = $wpdb->prepare( '%s AND %s', $meta_value[0], $meta_value[1] ); 774 792 break; 775 793 776 794 case 'LIKE': 777 795 case 'NOT LIKE': 778 $meta_value = '%' . $wpdb->esc_like( $meta_value) . '%';779 $where = $wpdb->prepare( '%s', $meta_value);796 $meta_value = '%' . $wpdb->esc_like( $meta_value ) . '%'; 797 $where = $wpdb->prepare( '%s', $meta_value ); 780 798 break; 781 799 782 // EXISTS with a value is interpreted as '='.800 // EXISTS with a value is interpreted as '='. 783 801 case 'EXISTS': 784 802 $meta_compare = '='; 785 $where = $wpdb->prepare( '%s', $meta_value);803 $where = $wpdb->prepare( '%s', $meta_value ); 786 804 break; 787 805 788 // 'value' is ignored for NOT EXISTS.806 // 'value' is ignored for NOT EXISTS. 789 807 case 'NOT EXISTS': 790 808 $where = ''; … … 792 810 793 811 default: 794 $where = $wpdb->prepare( '%s', $meta_value);812 $where = $wpdb->prepare( '%s', $meta_value ); 795 813 break; 796 814 } 797 815 798 if ( $where) {799 if ( 'CHAR' === $meta_type) {816 if ( $where ) { 817 if ( 'CHAR' === $meta_type ) { 800 818 $sql_chunks['where'][] = "$alias.{$clause['key']} {$meta_compare} {$where}"; 801 819 } else { … … 809 827 * be joined in parentheses. 810 828 */ 811 if ( 1 < count($sql_chunks['where'])) {812 $sql_chunks['where'] = array( '( ' . implode(' AND ', $sql_chunks['where']) . ' )');829 if ( 1 < count( $sql_chunks['where'] ) ) { 830 $sql_chunks['where'] = array( '( ' . implode( ' AND ', $sql_chunks['where'] ) . ' )' ); 813 831 } 814 832 … … 822 840 * a value of 'orderby' corresponding to a meta clause. 823 841 * 842 * @return array Meta clauses. 824 843 * @since 4.2.0 825 844 * 826 * @return array Meta clauses. 827 */ 828 public function get_clauses() 829 { 845 */ 846 public function get_clauses() { 830 847 return $this->clauses; 831 848 } … … 845 862 * connected by the relation 'OR'. 846 863 * 847 * @since 4.1.0848 *849 864 * @param array $clause Query clause. 850 865 * @param array $parent_query Parent query of $clause. 866 * 851 867 * @return string|false Table alias if found, otherwise false. 852 */ 853 protected function find_compatible_table_alias($clause, $parent_query) 854 { 868 * @since 4.1.0 869 * 870 */ 871 protected function find_compatible_table_alias( $clause, $parent_query ) { 855 872 $alias = false; 856 873 857 foreach ( $parent_query as $sibling) {874 foreach ( $parent_query as $sibling ) { 858 875 // If the sibling has no alias yet, there's nothing to check. 859 if ( empty($sibling['alias'])) {876 if ( empty( $sibling['alias'] ) ) { 860 877 continue; 861 878 } 862 879 863 880 // We're only interested in siblings that are first-order clauses. 864 if ( !is_array($sibling) || !$this->is_first_order_clause($sibling)) {881 if ( ! is_array( $sibling ) || ! $this->is_first_order_clause( $sibling ) ) { 865 882 continue; 866 883 } … … 869 886 870 887 // Clauses connected by OR can share joins as long as they have "positive" operators. 871 if ( 'OR' === $parent_query['relation']) {872 $compatible_compares = array( '=', 'IN', 'BETWEEN', 'LIKE', 'REGEXP', 'RLIKE', '>', '>=', '<', '<=');888 if ( 'OR' === $parent_query['relation'] ) { 889 $compatible_compares = array( '=', 'IN', 'BETWEEN', 'LIKE', 'REGEXP', 'RLIKE', '>', '>=', '<', '<=' ); 873 890 874 891 // Clauses joined by AND with "negative" operators share a join only if they also share a key. 875 } elseif ( isset($sibling['key']) && isset($clause['key']) && $sibling['key'] === $clause['key']) {876 $compatible_compares = array( '!=', 'NOT IN', 'NOT LIKE');877 } 878 879 $clause_compare = strtoupper( $clause['compare']);880 $sibling_compare = strtoupper( $sibling['compare']);881 if ( in_array($clause_compare, $compatible_compares, true) && in_array($sibling_compare, $compatible_compares, true)) {882 $alias = preg_replace( '/\W/', '_', $sibling['alias']);892 } elseif ( isset( $sibling['key'] ) && isset( $clause['key'] ) && $sibling['key'] === $clause['key'] ) { 893 $compatible_compares = array( '!=', 'NOT IN', 'NOT LIKE' ); 894 } 895 896 $clause_compare = strtoupper( $clause['compare'] ); 897 $sibling_compare = strtoupper( $sibling['compare'] ); 898 if ( in_array( $clause_compare, $compatible_compares, true ) && in_array( $sibling_compare, $compatible_compares, true ) ) { 899 $alias = preg_replace( '/\W/', '_', $sibling['alias'] ); 883 900 break; 884 901 } … … 888 905 * Filters the table alias identified as compatible with the current clause. 889 906 * 907 * @param string|false $alias Table alias, or false if none was found. 908 * @param array $clause First-order query clause. 909 * @param array $parent_query Parent of $clause. 910 * @param MetaQuery $query MetaQuery object. 911 * 890 912 * @since 4.1.0 891 913 * 892 * @param string|false $alias Table alias, or false if none was found.893 * @param array $clause First-order query clause.894 * @param array $parent_query Parent of $clause.895 * @param MetaQuery $query MetaQuery object.896 914 */ 897 return apply_filters( 'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this);915 return apply_filters( 'meta_query_find_compatible_table_alias', $alias, $clause, $parent_query, $this ); 898 916 } 899 917 … … 905 923 * method can be used in these cases to determine whether such a clause is necessary. 906 924 * 925 * @return bool True if the query contains any `OR` relations, otherwise false. 907 926 * @since 4.3.0 908 927 * 909 * @return bool True if the query contains any `OR` relations, otherwise false. 910 */ 911 public function has_or_relation() 912 { 928 */ 929 public function has_or_relation() { 913 930 return $this->has_or_relation; 914 931 } -
meta-optimizer/trunk/inc/Options.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 class Options extends Base 6 { 7 public static $instance = null; 8 9 function __construct() 10 { 11 parent::__construct(); 12 13 add_action('admin_menu', array($this, 'menu')); 14 add_action('init', array($this, 'defineWords')); 15 } 16 17 function defineWords() 18 { 19 $tableInfo = array( 20 'post' => [ 21 'name' => __('Post'), 22 'title' => __('Post Meta', 'meta-optimizer') 23 ], 24 'comment' => [ 25 'name' => __('Comment'), 26 'title' => __('Comment Meta', 'meta-optimizer') 27 ], 28 'user' => [ 29 'name' => __('User'), 30 'title' => __('User Meta', 'meta-optimizer') 31 ], 32 'term' => [ 33 'name' => __('Term', 'meta-optimizer'), 34 'title' => __('Term Meta', 'meta-optimizer') 35 ] 36 ); 37 38 foreach ($this->tables as $type => $info) { 39 $this->tables[$type]['name'] = $tableInfo[$type]['name']; 40 $this->tables[$type]['title'] = $tableInfo[$type]['title']; 41 } 42 } 43 44 /** 45 * Add admin menu 46 * 47 * @return void 48 */ 49 public function menu() 50 { 51 add_options_page(__('Meta Optimizer', 'meta-optimizer'), __('Meta Optimizer', 'meta-optimizer'), 'manage_options', WPMETAOPTIMIZER_PLUGIN_KEY, array($this, 'settingsPage')); 52 } 53 54 /** 55 * Add settings page 56 * 57 * @return void 58 */ 59 public function settingsPage() 60 { 61 $Helpers = Helpers::getInstance(); 62 $updateMessage = ''; 63 $currentTab = 'tables'; 64 if (isset($_POST[WPMETAOPTIMIZER_PLUGIN_KEY])) { 65 if (wp_verify_nonce($_POST[WPMETAOPTIMIZER_PLUGIN_KEY], 'settings_submit')) { 66 $currentTab = sanitize_text_field($_POST['current_tab']); 67 $checkBoxList = []; 68 unset($_POST[WPMETAOPTIMIZER_PLUGIN_KEY]); 69 unset($_POST['current_tab']); 70 71 $options = $this->getOption(null, [], false); 72 73 foreach ($_POST as $key => $value) { 74 if (strpos($key, '_white_list') !== false || strpos($key, '_black_list') !== false) 75 $value = sanitize_textarea_field($value); 76 else 77 $value = is_array($value) ? array_map('sanitize_text_field', $value) : sanitize_text_field($value); 78 79 $options[sanitize_key($key)] = $value; 80 } 81 82 if ($currentTab == 'settings') 83 $checkBoxList = ['support_wp_query', 'support_wp_query_active_automatically', 'support_wp_query_deactive_while_import', 'original_meta_actions']; 84 85 foreach ($checkBoxList as $checkbox) 86 $options[$checkbox] = isset($_POST[$checkbox]) ? sanitize_text_field($_POST[$checkbox]) : 0; 87 88 update_option(WPMETAOPTIMIZER_OPTION_KEY, $options); 89 $updateMessage = $this->getNoticeMessageHTML(__('Settings saved.')); 90 91 // Reset Import 92 foreach ($this->tables as $type => $table) { 93 if (isset($_POST['reset_import_' . $type])) 94 $this->setOption('import_' . $type . '_latest_id', null); 95 } 96 } 97 } 98 99 $options = $this->getOption(null, [], false); 100 101 $postTypes = get_post_types([ 102 'show_ui' => true 103 ], "objects"); 104 105 $metaSaveTypes = $this->getOption('meta_save_types', []); 106 ?> 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 7 8 class Options extends Base { 9 public static $instance = null; 10 11 function __construct() { 12 parent::__construct(); 13 14 add_action( 'admin_menu', array( $this, 'adminMenu' ) ); 15 add_action( 'init', array( $this, 'defineWords' ) ); 16 } 17 18 function defineWords() { 19 $tableInfo = array( 20 'post' => [ 21 'name' => __( 'Post' ), 22 'title' => __( 'Post Meta', 'meta-optimizer' ) 23 ], 24 'comment' => [ 25 'name' => __( 'Comment' ), 26 'title' => __( 'Comment Meta', 'meta-optimizer' ) 27 ], 28 'user' => [ 29 'name' => __( 'User' ), 30 'title' => __( 'User Meta', 'meta-optimizer' ) 31 ], 32 'term' => [ 33 'name' => __( 'Term', 'meta-optimizer' ), 34 'title' => __( 'Term Meta', 'meta-optimizer' ) 35 ] 36 ); 37 38 foreach ( $this->tables as $type => $info ) { 39 $this->tables[ $type ]['name'] = $tableInfo[ $type ]['name']; 40 $this->tables[ $type ]['title'] = $tableInfo[ $type ]['title']; 41 } 42 } 43 44 /** 45 * Add admin menu 46 * 47 * @return void 48 */ 49 public function adminMenu() { 50 add_submenu_page( 'tools.php', __( 'Meta Optimizer', 'meta-optimizer' ), __( 'Meta Optimizer', 'meta-optimizer' ), 'manage_options', WPMETAOPTIMIZER_PLUGIN_KEY, array( 51 $this, 52 'settingsPage' 53 ) ); 54 } 55 56 /** 57 * Add settings page 58 * 59 * @return void 60 */ 61 public function settingsPage() { 62 $Helpers = Helpers::getInstance(); 63 $updateMessage = ''; 64 $currentTab = 'tables'; 65 if ( isset( $_POST[ WPMETAOPTIMIZER_PLUGIN_KEY ] ) ) { 66 if ( wp_verify_nonce( $_POST[ WPMETAOPTIMIZER_PLUGIN_KEY ], 'settings_submit' ) ) { 67 $currentTab = sanitize_text_field( $_POST['current_tab'] ); 68 $checkBoxList = []; 69 unset( $_POST[ WPMETAOPTIMIZER_PLUGIN_KEY ] ); 70 unset( $_POST['current_tab'] ); 71 72 $options = $this->getOption( null, [], false ); 73 74 foreach ( $_POST as $key => $value ) { 75 if ( strpos( $key, '_white_list' ) !== false || strpos( $key, '_black_list' ) !== false ) 76 $value = sanitize_textarea_field( $value ); 77 else 78 $value = is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : sanitize_text_field( $value ); 79 80 $options[ sanitize_key( $key ) ] = $value; 81 } 82 83 if ( $currentTab == 'settings' ) 84 $checkBoxList = [ 85 'support_wp_query', 86 'support_wp_query_active_automatically', 87 'support_wp_query_deactive_while_import', 88 'original_meta_actions' 89 ]; 90 91 foreach ( $checkBoxList as $checkbox ) { 92 $options[ $checkbox ] = isset( $_POST[ $checkbox ] ) ? sanitize_text_field( $_POST[ $checkbox ] ) : 0; 93 } 94 95 update_option( WPMETAOPTIMIZER_OPTION_KEY, $options ); 96 $updateMessage = $this->getNoticeMessageHTML( __( 'Settings saved.' ) ); 97 98 // Reset Import 99 foreach ( $this->tables as $type => $table ) { 100 if ( isset( $_POST[ 'reset_import_' . $type ] ) ) 101 $this->setOption( 'import_' . $type . '_latest_id', null ); 102 } 103 } 104 } 105 106 $options = $this->getOption( null, [], false ); 107 108 $postTypes = get_post_types( [ 109 'show_ui' => true 110 ], "objects" ); 111 112 $metaSaveTypes = $this->getOption( 'meta_save_types', [] ); 113 ?> 107 114 <div class="wrap wpmo-wrap"> 108 <h1 class="wp-heading-inline"><span class="dashicons dashicons-editor-table"></span> <?php _e('Meta Optimizer', 'meta-optimizer') ?></h1> 109 <?php echo wp_kses($updateMessage, array('div' => ['class' => []], 'p' => [])); ?> 115 <h1 class="wp-heading-inline"><span 116 class="dashicons dashicons-editor-table"></span> <?php _e( 'Meta Optimizer', 'meta-optimizer' ) ?> 117 </h1> 118 <?php echo wp_kses( $updateMessage, array( 'div' => [ 'class' => [] ], 'p' => [] ) ); ?> 110 119 111 120 <div class="nav-tab-wrapper"> 112 <a id="tables-tab" class="wpmo-tab nav-tab <?php echo $currentTab == 'tables' ? 'nav-tab-active' : '' ?>"><?php _e('Tables', 'meta-optimizer') ?></a> 113 <a id="settings-tab" class="wpmo-tab nav-tab <?php echo $currentTab == 'settings' ? 'nav-tab-active' : '' ?>"><?php _e('Settings') ?></a> 114 <a id="import-tab" class="wpmo-tab nav-tab <?php echo $currentTab == 'import' ? 'nav-tab-active' : '' ?>"><?php _e('Import', 'meta-optimizer') ?></a> 121 <a id="tables-tab" 122 class="wpmo-tab nav-tab <?php echo $currentTab == 'tables' ? 'nav-tab-active' : '' ?>"><?php _e( 'Tables', 'meta-optimizer' ) ?></a> 123 <a id="settings-tab" 124 class="wpmo-tab nav-tab <?php echo $currentTab == 'settings' ? 'nav-tab-active' : '' ?>"><?php _e( 'Settings' ) ?></a> 125 <a id="import-tab" 126 class="wpmo-tab nav-tab <?php echo $currentTab == 'import' ? 'nav-tab-active' : '' ?>"><?php _e( 'Import', 'meta-optimizer' ) ?></a> 115 127 </div> 116 128 117 129 <div id="tables-tab-content" class="wpmo-tab-content <?php echo $currentTab != 'tables' ? 'hidden' : '' ?>"> 118 <?php119 foreach ($this->tables as $type => $table) {120 $columns = $Helpers->getTableColumns($table['table'], $type);121 sort($columns);122 ?>123 <h2><?php echo esc_html( $table['title']) ?></h2>130 <?php 131 foreach ( $this->tables as $type => $table ) { 132 $columns = $Helpers->getTableColumns( $table['table'], $type ); 133 sort( $columns ); 134 ?> 135 <h2><?php echo esc_html( $table['title'] ) ?></h2> 124 136 <p> 125 <?php126 _e('Number of Columns:', 'meta-optimizer');127 echo ' ' . (is_array($columns) ? count($columns) : 0) . ' - ';128 _e('Number of rows:', 'meta-optimizer');129 echo ' ' . $Helpers->getTableRowsCount($table['table']);130 ?>137 <?php 138 _e( 'Number of Columns:', 'meta-optimizer' ); 139 echo ' ' . ( is_array( $columns ) ? count( $columns ) : 0 ) . ' - '; 140 _e( 'Number of rows:', 'meta-optimizer' ); 141 echo ' ' . $Helpers->getTableRowsCount( $table['table'] ); 142 ?> 131 143 </p> 132 144 133 145 <table class="wp-list-table widefat fixed striped table-view-list table-sticky-head"> 134 146 <thead> 135 <tr> 136 <th style="width:30px">#</th> 137 <th><?php _e('Field Name', 'meta-optimizer') ?></th> 138 <th><?php _e('Change') ?></th> 139 <?php if ($this->getOption('original_meta_actions', false) == 1) { ?> 140 <th class="color-red"><span class="dashicons dashicons-info"></span> <abbr title="<?php echo sprintf(__("These actions directly affect the %s WordPress table and %s plugin table", 'meta-optimizer'), $Helpers->getWPMetaTableName($type), $Helpers->getMetaTableName($type)); ?>" class="tooltip-title"><?php _e('Change the original meta') ?></abbr></th> 141 <?php } ?> 142 </tr> 147 <tr> 148 <th style="width:30px">#</th> 149 <th><?php _e( 'Field Name', 'meta-optimizer' ) ?></th> 150 <th><?php _e( 'Change' ) ?></th> 151 <?php if ( $this->getOption( 'original_meta_actions', false ) == 1 ) { ?> 152 <th class="color-red"><span class="dashicons dashicons-info"></span> <abbr 153 title="<?php echo sprintf( __( "These actions directly affect the %s WordPress table and %s plugin table", 'meta-optimizer' ), $Helpers->getWPMetaTableName( $type ), $Helpers->getMetaTableName( $type ) ); ?>" 154 class="tooltip-title"><?php _e( 'Change the original meta' ) ?></abbr></th> 155 <?php } ?> 156 </tr> 143 157 </thead> 144 158 <tbody> 145 <?php146 $c = 1;147 if (is_array($columns) && count($columns))148 foreach ($columns as $column) {149 $_column = $column;150 $column = $Helpers->translateColumnName($type, $column);151 152 $checkInBlackList = Helpers::getInstance()->checkInBlackWhiteList($type, $column);153 if ($checkInBlackList) {154 $listActionTitle = __('Remove from black list', 'meta-optimizer');155 $listAction= 'remove';156 } else {157 $listActionTitle = __('Add to black list', 'meta-optimizer');158 $listAction= 'insert';159 }160 161 if ($_column === $column)162 $_column = '';163 164 echo "<tr class='" . ($checkInBlackList ? 'black-list-column' : '') . "'><td>{$c}</td><td class='column-name'><span>" . esc_html($column) . "</span>" . ($_column ? " <abbr class='translated-column-name tooltip-title' title='" . __('The meta key was renamed because it equals the name of a reserved column.', 'meta-optimizer') . "'>(" . esc_html($_column) . ")</abbr>" : '') . "</td>";165 166 echo "<td class='change-icons'>";167 echo "<span class='dashicons dashicons-edit rename-table-column tooltip-title' title='" . __('Rename', 'meta-optimizer') . "' data-type='" . esc_html($type) . "' data-meta-table='plugin' data-column='" . esc_html($column) . "'></span>";168 echo "<span class='dashicons dashicons-trash delete-table-column tooltip-title' title='" . __('Delete') . "' data-type='" . esc_html($type) . "' data-meta-table='plugin' data-column='" . esc_html($column) . "'></span>";169 echo "<span span class='dashicons dashicons-" . esc_html($listAction) . " add-remove-black-list tooltip-title' title='" . esc_html($listActionTitle) . "' data-action='" . esc_html($listAction) . "' data-type='" . esc_html($type) . "' data-meta-table='plugin' data-column='" . esc_html($column) . "'></span>";170 echo "</td>";171 172 if ($this->getOption('original_meta_actions', false) == 1) {173 echo "<td class='change-icons'>";174 if ($Helpers->checkCanChangeWPMetaKey($type, $column)) {175 echo "<span class='dashicons dashicons-edit rename-table-column tooltip-title' title='" . __('Rename', 'meta-optimizer') . "' data-type='" . esc_html($type) . "' data-meta-table='origin' data-column='" . esc_html($column) . "'></span>";176 echo "<span class='dashicons dashicons-trash delete-table-column tooltip-title' title='" . __('Delete') . "' data-type='" . esc_html($type) . "' data-meta-table='origin' data-column='" . esc_html($column) . "'></span>";177 } else {178 echo '---';179 }180 echo "</td>";181 }182 183 echo "</tr>";184 $c++;185 }186 else187 echo "<tr><td colspan='" . ($this->getOption('original_meta_actions', false) == 1 ? 4 : 3) . "'>" . __('Without custom field column', 'meta-optimizer') . "</td></tr>";188 ?>159 <?php 160 $c = 1; 161 if ( is_array( $columns ) && count( $columns ) ) 162 foreach ( $columns as $column ) { 163 $_column = $column; 164 $column = $Helpers->translateColumnName( $type, $column ); 165 166 $checkInBlackList = Helpers::getInstance()->checkInBlackWhiteList( $type, $column ); 167 if ( $checkInBlackList ) { 168 $listActionTitle = __( 'Remove from black list', 'meta-optimizer' ); 169 $listAction = 'remove'; 170 } else { 171 $listActionTitle = __( 'Add to black list', 'meta-optimizer' ); 172 $listAction = 'insert'; 173 } 174 175 if ( $_column === $column ) 176 $_column = ''; 177 178 echo "<tr class='" . ( $checkInBlackList ? 'black-list-column' : '' ) . "'><td>{$c}</td><td class='column-name'><span>" . esc_html( $column ) . "</span>" . ( $_column ? " <abbr class='translated-column-name tooltip-title' title='" . __( 'The meta key was renamed because it equals the name of a reserved column.', 'meta-optimizer' ) . "'>(" . esc_html( $_column ) . ")</abbr>" : '' ) . "</td>"; 179 180 echo "<td class='change-icons'>"; 181 echo "<span class='dashicons dashicons-edit rename-table-column tooltip-title' title='" . __( 'Rename', 'meta-optimizer' ) . "' data-type='" . esc_html( $type ) . "' data-meta-table='plugin' data-column='" . esc_html( $column ) . "'></span>"; 182 echo "<span class='dashicons dashicons-trash delete-table-column tooltip-title' title='" . __( 'Delete' ) . "' data-type='" . esc_html( $type ) . "' data-meta-table='plugin' data-column='" . esc_html( $column ) . "'></span>"; 183 echo "<span span class='dashicons dashicons-" . esc_html( $listAction ) . " add-remove-black-list tooltip-title' title='" . esc_html( $listActionTitle ) . "' data-action='" . esc_html( $listAction ) . "' data-type='" . esc_html( $type ) . "' data-meta-table='plugin' data-column='" . esc_html( $column ) . "'></span>"; 184 echo "</td>"; 185 186 if ( $this->getOption( 'original_meta_actions', false ) == 1 ) { 187 echo "<td class='change-icons'>"; 188 if ( $Helpers->checkCanChangeWPMetaKey( $type, $column ) ) { 189 echo "<span class='dashicons dashicons-edit rename-table-column tooltip-title' title='" . __( 'Rename', 'meta-optimizer' ) . "' data-type='" . esc_html( $type ) . "' data-meta-table='origin' data-column='" . esc_html( $column ) . "'></span>"; 190 echo "<span class='dashicons dashicons-trash delete-table-column tooltip-title' title='" . __( 'Delete' ) . "' data-type='" . esc_html( $type ) . "' data-meta-table='origin' data-column='" . esc_html( $column ) . "'></span>"; 191 } else { 192 echo '---'; 193 } 194 echo "</td>"; 195 } 196 197 echo "</tr>"; 198 $c ++; 199 } 200 else 201 echo "<tr><td colspan='" . ( $this->getOption( 'original_meta_actions', false ) == 1 ? 4 : 3 ) . "'>" . __( 'Without custom field column', 'meta-optimizer' ) . "</td></tr>"; 202 ?> 189 203 </tbody> 190 204 </table> 191 205 <br> 192 <?php193 }194 ?>206 <?php 207 } 208 ?> 195 209 </div> 196 210 197 <div id="settings-tab-content" class="wpmo-tab-content <?php echo $currentTab != 'settings' ? 'hidden' : '' ?>"> 211 <div id="settings-tab-content" 212 class="wpmo-tab-content <?php echo $currentTab != 'settings' ? 'hidden' : '' ?>"> 198 213 <form action="" method="post"> 199 214 <input type="hidden" name="current_tab" value="settings"> 200 <?php wp_nonce_field('settings_submit', WPMETAOPTIMIZER_PLUGIN_KEY, false); ?>215 <?php wp_nonce_field( 'settings_submit', WPMETAOPTIMIZER_PLUGIN_KEY, false ); ?> 201 216 <table> 202 217 <tbody> 218 <tr> 219 <th><?php _e( 'Support WordPress Query', 'meta-optimizer' ) ?></th> 220 <td> 221 <label><input type="checkbox" name="support_wp_query" id="support_wp_query" 222 value="1" <?php checked( $this->getOption( 'support_wp_query', false ) == 1 ); ?> <?php disabled( ! $Helpers->checkImportFinished() ) ?>><?php _e( 'Active', 'meta-optimizer' ) ?> 223 </label> 224 <label><input type="checkbox" name="support_wp_query_active_automatically" 225 id="support_wp_query_active_automatically" 226 value="1" <?php checked( $this->getOption( 'support_wp_query_active_automatically', false ) == 1 ) ?>><?php _e( 'Active automatically after import completed', 'meta-optimizer' ) ?> 227 </label> 228 <label><input type="checkbox" name="support_wp_query_deactive_while_import" 229 id="support_wp_query_deactive_while_import" 230 value="1" <?php checked( $this->getOption( 'support_wp_query_deactive_while_import', false ) == 1 ) ?>><?php _e( 'Deactive while import process is run', 'meta-optimizer' ) ?> 231 </label> 232 <p class="description"><span 233 class="description-notice"><?php _e( 'Apply a filter to the WordPress query. You can disable this option if you experience any problems with the results of your display posts.', 'meta-optimizer' ) ?></span> 234 </p> 235 </td> 236 </tr> 237 <tr> 238 <td><?php _e( 'Save meta for', 'meta-optimizer' ) ?></td> 239 <td> 240 <input type="hidden" name="meta_save_types[hidden]" value="1"> 241 <?php 242 foreach ( $this->tables as $type => $table ) { 243 ?> 244 <label><input type="checkbox" 245 name="meta_save_types[<?php echo esc_attr( $type ) ?>]" 246 value="1" <?php checked( isset( $metaSaveTypes[ $type ] ) ) ?>> <?php echo esc_html( $table['name'] ) ?> 247 </label> 248 <?php 249 } 250 ?> 251 </td> 252 </tr> 253 <tr> 254 <td><?php _e( 'Don\'t saving Meta in the default tables', 'meta-optimizer' ) ?></td> 255 <td> 256 <input type="hidden" name="dont_save_wpmeta[hidden]" value="1"> 257 <?php 258 $defaultMetaSave = $this->getOption( 'dont_save_wpmeta', [] ); 259 foreach ( $this->tables as $type => $table ) { 260 ?> 261 <label><input type="checkbox" 262 name="dont_save_wpmeta[<?php echo esc_attr( $type ) ?>]" 263 value="1" <?php checked( isset( $defaultMetaSave[ $type ] ) ) ?>> <?php echo esc_html( $table['name'] ) ?> 264 </label> 265 <?php 266 } 267 ?> 268 <p class="description"> 269 <?php _e( 'You can choose the Meta types if you do not want Meta saved in the default tables.', 'meta-optimizer' ) ?> 270 <a href="https://developer.wordpress.org/plugins/metadata/" target="_blank"> 271 <?php _e( 'More information', 'meta-optimizer' ) ?> 272 </a> 273 </p> 274 </td> 275 </tr> 276 <tr> 277 <td><?php _e( 'Post Types', 'meta-optimizer' ) ?></td> 278 <td> 279 <input type="hidden" name="post_types[hidden]" value="1"> 280 <?php 281 $postTypesOption = $this->getOption( 'post_types', [] ); 282 foreach ( $postTypes as $postType ) { 283 if ( ! in_array( $postType->name, $this->ignorePostTypes ) ) 284 echo '<label><input type="checkbox" name="post_types[' . esc_attr( $postType->name ) . ']" value="1" ' . 285 checked( $postTypesOption[ $postType->name ] ?? 0, 1, false ) . ( isset( $metaSaveTypes['post'] ) ? '' : ' disabled' ) . '/>' . esc_html( $postType->label ) . '</label> '; 286 } 287 ?> 288 <br> 289 <p class="description"><?php _e( 'You can save meta fields for specific post types.', 'meta-optimizer' ) ?></p> 290 </td> 291 </tr> 292 <tr> 293 <td> 294 <label for="original_meta_actions"><?php _e( 'Actions for original meta', 'meta-optimizer' ) ?></label> 295 </td> 296 <td> 297 <label><input type="checkbox" name="original_meta_actions" id="original_meta_actions" 298 value="1" <?php checked( $this->getOption( 'original_meta_actions', false ) == 1 ) ?>><?php _e( 'Active', 'meta-optimizer' ) ?> 299 </label> 300 <p class="description"><?php _e( 'In the plugin tables tab, display actions for original meta keys.', 'meta-optimizer' ) ?></p> 301 </td> 302 </tr> 303 </tbody> 304 </table> 305 306 <table> 307 <thead> 308 <tr> 309 <th> 310 <?php _e( 'Black/White list', 'meta-optimizer' ) ?> 311 </th> 312 <td colspan="2"> 313 <?php _e( 'Set White/Black list for custom meta fields', 'meta-optimizer' ) ?> 314 <p class="description"><?php _e( 'Write each item on a new line', 'meta-optimizer' ) ?></p> 315 </td> 316 </tr> 317 <tr> 318 <th><?php _e( 'Type' ) ?></th> 319 <th><?php _e( 'White List', 'meta-optimizer' ) ?></th> 320 <th><?php _e( 'Black List', 'meta-optimizer' ) ?></th> 321 </tr> 322 </tr> 323 </thead> 324 <tbody> 325 <?php 326 foreach ( $this->tables as $type => $table ) { 327 ?> 203 328 <tr> 204 <t h><?php _e('Support WordPress Query', 'meta-optimizer') ?></th>329 <td><?php echo esc_html( $table['title'] ) ?></td> 205 330 <td> 206 <label><input type="checkbox" name="support_wp_query" id="support_wp_query" value="1" <?php checked($this->getOption('support_wp_query', false) == 1); ?> <?php disabled(!$Helpers->checkImportFinished()) ?>><?php _e('Active', 'meta-optimizer') ?></label> 207 <label><input type="checkbox" name="support_wp_query_active_automatically" id="support_wp_query_active_automatically" value="1" <?php checked($this->getOption('support_wp_query_active_automatically', false) == 1) ?>><?php _e('Active automatically after import completed', 'meta-optimizer') ?></label> 208 <label><input type="checkbox" name="support_wp_query_deactive_while_import" id="support_wp_query_deactive_while_import" value="1" <?php checked($this->getOption('support_wp_query_deactive_while_import', false) == 1) ?>><?php _e('Deactive while import process is run', 'meta-optimizer') ?></label> 209 <p class="description"><span class="description-notice"><?php _e('Apply a filter to the WordPress query. You can disable this option if you experience any problems with the results of your display posts.', 'meta-optimizer') ?></span></p> 331 <textarea name="<?php echo esc_attr( $type ) ?>_white_list" 332 id="<?php echo esc_attr( $type ) ?>_white_list" cols="40" rows="7" 333 class="ltr" 334 placeholder="custom_field_name" <?php echo isset( $metaSaveTypes[ $type ] ) ? '' : ' disabled' ?>><?php echo esc_textarea( $this->getOption( $type . '_white_list', '' ) ) ?></textarea> 335 </td> 336 <td> 337 <textarea name="<?php echo esc_attr( $type ) ?>_black_list" 338 id="<?php echo esc_attr( $type ) ?>_black_list" cols="40" rows="7" 339 class="ltr" 340 placeholder="custom_field_name" <?php echo isset( $metaSaveTypes[ $type ] ) ? '' : ' disabled' ?>><?php echo esc_textarea( $this->getOption( $type . '_black_list', '' ) ) ?></textarea> 210 341 </td> 211 342 </tr> 212 <tr> 213 <td><?php _e('Save meta for', 'meta-optimizer') ?></td> 214 <td> 215 <input type="hidden" name="meta_save_types[hidden]" value="1"> 216 <?php 217 foreach ($this->tables as $type => $table) { 218 ?> 219 <label><input type="checkbox" name="meta_save_types[<?php echo esc_attr($type) ?>]" value="1" <?php checked(isset($metaSaveTypes[$type])) ?>> <?php echo esc_html($table['name']) ?></label> 220 <?php 221 } 222 ?> 223 </td> 224 </tr> 225 <tr> 226 <td><?php _e('Don\'t saving Meta in the default tables', 'meta-optimizer') ?></td> 227 <td> 228 <input type="hidden" name="dont_save_wpmeta[hidden]" value="1"> 229 <?php 230 $defaultMetaSave = $this->getOption('dont_save_wpmeta', []); 231 foreach ($this->tables as $type => $table) { 232 ?> 233 <label><input type="checkbox" name="dont_save_wpmeta[<?php echo esc_attr($type) ?>]" value="1" <?php checked(isset($defaultMetaSave[$type])) ?>> <?php echo esc_html($table['name']) ?></label> 234 <?php 235 } 236 ?> 237 <p class="description"> 238 <?php _e('You can choose the Meta types if you do not want Meta saved in the default tables.', 'meta-optimizer') ?> 239 <a href="https://developer.wordpress.org/plugins/metadata/" target="_blank"> 240 <?php _e('More information', 'meta-optimizer') ?> 241 </a> 242 </p> 243 </td> 244 </tr> 245 <tr> 246 <td><?php _e('Post Types', 'meta-optimizer') ?></td> 247 <td> 248 <input type="hidden" name="post_types[hidden]" value="1"> 249 <?php 250 $postTypesOption = $this->getOption('post_types', []); 251 foreach ($postTypes as $postType) { 252 if (!in_array($postType->name, $this->ignorePostTypes)) 253 echo '<label><input type="checkbox" name="post_types[' . esc_attr($postType->name) . ']" value="1" ' . 254 checked($postTypesOption[$postType->name] ?? 0, 1, false) . (isset($metaSaveTypes['post']) ? '' : ' disabled') . '/>' . esc_html($postType->label) . '</label> '; 255 } 256 ?> 257 <br> 258 <p class="description"><?php _e('You can save meta fields for specific post types.', 'meta-optimizer') ?></p> 259 </td> 260 </tr> 261 <tr> 262 <td><label for="original_meta_actions"><?php _e('Actions for original meta', 'meta-optimizer') ?></label></td> 263 <td> 264 <label><input type="checkbox" name="original_meta_actions" id="original_meta_actions" value="1" <?php checked($this->getOption('original_meta_actions', false) == 1) ?>><?php _e('Active', 'meta-optimizer') ?></label> 265 <p class="description"><?php _e('In the plugin tables tab, display actions for original meta keys.', 'meta-optimizer') ?></p> 266 </td> 267 </tr> 268 </tbody> 269 </table> 270 271 <table> 272 <thead> 273 <tr> 274 <th> 275 <?php _e('Black/White list', 'meta-optimizer') ?> 276 </th> 277 <td colspan="2"> 278 <?php _e('Set White/Black list for custom meta fields', 'meta-optimizer') ?> 279 <p class="description"><?php _e('Write each item on a new line', 'meta-optimizer') ?></p> 280 </td> 281 </tr> 282 <tr> 283 <th><?php _e('Type') ?></th> 284 <th><?php _e('White List', 'meta-optimizer') ?></th> 285 <th><?php _e('Black List', 'meta-optimizer') ?></th> 286 </tr> 287 </tr> 288 </thead> 289 <tbody> 290 <?php 291 foreach ($this->tables as $type => $table) { 292 ?> 293 <tr> 294 <td><?php echo esc_html($table['title']) ?></td> 295 <td> 296 <textarea name="<?php echo esc_attr($type) ?>_white_list" id="<?php echo esc_attr($type) ?>_white_list" cols="40" rows="7" class="ltr" placeholder="custom_field_name" <?php echo isset($metaSaveTypes[$type]) ? '' : ' disabled' ?>><?php echo esc_textarea($this->getOption($type . '_white_list', '')) ?></textarea> 297 </td> 298 <td> 299 <textarea name="<?php echo esc_attr($type) ?>_black_list" id="<?php echo esc_attr($type) ?>_black_list" cols="40" rows="7" class="ltr" placeholder="custom_field_name" <?php echo isset($metaSaveTypes[$type]) ? '' : ' disabled' ?>><?php echo esc_textarea($this->getOption($type . '_black_list', '')) ?></textarea> 300 </td> 301 </tr> 302 <?php 303 } 304 ?> 305 <tr> 306 <td colspan="3"><input type="submit" class="button button-primary" value="<?php _e('Save') ?>"></td> 307 </tr> 343 <?php 344 } 345 ?> 346 <tr> 347 <td colspan="3"><input type="submit" class="button button-primary" 348 value="<?php _e( 'Save' ) ?>"></td> 349 </tr> 308 350 </tbody> 309 351 </table> … … 314 356 <form action="" method="post"> 315 357 <input type="hidden" name="current_tab" value="import"> 316 <?php wp_nonce_field('settings_submit', WPMETAOPTIMIZER_PLUGIN_KEY, false); ?>358 <?php wp_nonce_field( 'settings_submit', WPMETAOPTIMIZER_PLUGIN_KEY, false ); ?> 317 359 <table> 318 360 <tbody> 319 <tr> 320 <th colspan="2"><?php _e('Import Post/Comment/User/Term Metas from meta tables', 'meta-optimizer') ?></th> 321 </tr> 322 <tr> 323 <td><label for="import_items_number"><?php _e('Import items per run', 'meta-optimizer') ?></label></td> 324 <td> 325 <input type="number" name="import_items_number" id="import_items_number" class="small-text" step="1" min="1" max="10" value="<?php echo esc_attr($this->getOption('import_items_number', 1)) ?>" placeholder="1"> 326 <p class="description"><?php _e('The import scheduler runs every minute, and you can set the number of items to import.', 'meta-optimizer') ?></p> 327 </td> 328 </tr> 329 <tr> 330 <th><?php _e('Meta Tables', 'meta-optimizer') ?></th> 331 <td> 332 <input type="hidden" name="import[hidden]" value="1"> 333 <?php 334 $importTables = $this->getOption('import', []); 335 foreach ($this->tables as $type => $table) { 336 $latestObjectID = $this->getOption('import_' . $type . '_latest_id', false); 337 $metaTypeCanSaved = isset($metaSaveTypes[$type]); 338 ?> 339 <label><input type="checkbox" name="import[<?php echo esc_attr($type) ?>]" value="1" <?php checked(isset($importTables[$type])); 340 echo esc_html($metaTypeCanSaved) ? '' : ' disabled' ?>> <?php echo esc_html($table['name']) . ' (' . $Helpers->getWPMetaTableName($type) . ')' ?></label> <br> 341 <?php 342 if ($metaTypeCanSaved && $latestObjectID) { 343 $checkedDate = $this->getOption('import_' . $type . '_checked_date', false); 344 $_checkedDate = ''; 345 if ($checkedDate) 346 $_checkedDate = ' (' . wp_date('Y-m-d H:i:s', strtotime($checkedDate)) . ') '; 347 348 echo '<p>'; 349 350 if ($latestObjectID === 'finished') { 351 _e('Finished', 'meta-optimizer') . esc_html($_checkedDate) . ', '; 352 } elseif (is_numeric($latestObjectID)) { 353 $objectTitle = $objectLink = false; 354 355 if ($type == 'post') { 356 $objectTitle = get_the_title($latestObjectID); 357 $objectLink = get_edit_post_link($latestObjectID); 358 } elseif ($type == 'comment') { 359 $comment = get_comment($latestObjectID); 360 $objectTitle = $comment->comment_author . ' - ' . $comment->comment_author_email; 361 $objectLink = get_edit_comment_link($latestObjectID); 362 } elseif ($type == 'user') { 363 $user = get_userdata($latestObjectID); 364 $objectTitle = $user->display_name; 365 $objectLink = get_edit_user_link($latestObjectID); 366 } elseif ($type == 'term') { 367 $term = get_term($latestObjectID); 368 if ($term) 369 $objectTitle = $term->name; 370 $objectLink = get_edit_term_link($latestObjectID); 371 } 372 373 if ($objectTitle && $objectLink) 374 _e('The last item checked:', 'meta-optimizer') . " <a href='{$objectLink}' target='_blank'>{$objectTitle}</a> {$_checkedDate}, "; 375 else 376 _e('Unknown item', 'meta-optimizer') . " {$_checkedDate}, "; 377 378 _e('Left Items: ', 'meta-optimizer') . $Helpers->getObjectLeftItemsCount($type) . ", "; 379 } 380 381 echo "<label><input type='checkbox' name='reset_import_" . esc_attr($type) . "' value='1'> " . __('Reset', 'meta-optimizer') . '</label>'; 382 echo '</p>'; 383 } 384 ?> 385 <?php 386 echo '<br>'; 387 } 388 ?> 389 </td> 390 </tr> 391 <tr> 392 <td colspan="2"> 393 <p class="description"><?php _e('Importing runs in the background without requiring a website to be open.', 'meta-optimizer') ?></p> 394 </td> 395 </tr> 396 <tr> 397 <td colspan="2"><input type="submit" class="button button-primary" value="<?php _e('Save') ?>"></td> 398 </tr> 361 <tr> 362 <th colspan="2"><?php _e( 'Import Post/Comment/User/Term Metas from meta tables', 'meta-optimizer' ) ?></th> 363 </tr> 364 <tr> 365 <td> 366 <label for="import_items_number"><?php _e( 'Import items per run', 'meta-optimizer' ) ?></label> 367 </td> 368 <td> 369 <input type="number" name="import_items_number" id="import_items_number" 370 class="small-text" step="1" min="1" max="10" 371 value="<?php echo esc_attr( $this->getOption( 'import_items_number', 1 ) ) ?>" 372 placeholder="1"> 373 <p class="description"><?php _e( 'The import scheduler runs every minute, and you can set the number of items to import.', 'meta-optimizer' ) ?></p> 374 </td> 375 </tr> 376 <tr> 377 <th><?php _e( 'Meta Tables', 'meta-optimizer' ) ?></th> 378 <td> 379 <input type="hidden" name="import[hidden]" value="1"> 380 <?php 381 $importTables = $this->getOption( 'import', [] ); 382 foreach ( $this->tables as $type => $table ) { 383 $latestObjectID = $this->getOption( 'import_' . $type . '_latest_id', false ); 384 $metaTypeCanSaved = isset( $metaSaveTypes[ $type ] ); 385 ?> 386 <label><input type="checkbox" name="import[<?php echo esc_attr( $type ) ?>]" 387 value="1" <?php checked( isset( $importTables[ $type ] ) ); 388 echo esc_html( $metaTypeCanSaved ) ? '' : ' disabled' ?>> <?php echo esc_html( $table['name'] ) . ' (' . $Helpers->getWPMetaTableName( $type ) . ')' ?> 389 </label> <br> 390 <?php 391 if ( $metaTypeCanSaved && $latestObjectID ) { 392 $checkedDate = $this->getOption( 'import_' . $type . '_checked_date', false ); 393 $_checkedDate = ''; 394 if ( $checkedDate ) 395 $_checkedDate = ' (' . wp_date( 'Y-m-d H:i:s', strtotime( $checkedDate ) ) . ') '; 396 397 echo '<p>'; 398 399 if ( $latestObjectID === 'finished' ) { 400 _e( 'Finished', 'meta-optimizer' ); 401 echo esc_html( $_checkedDate ) . ', '; 402 403 } elseif ( is_numeric( $latestObjectID ) ) { 404 $objectTitle = $objectLink = false; 405 406 if ( $type == 'post' ) { 407 $objectTitle = get_the_title( $latestObjectID ); 408 $objectLink = get_edit_post_link( $latestObjectID ); 409 410 } elseif ( $type == 'comment' ) { 411 $comment = get_comment( $latestObjectID ); 412 $objectTitle = $comment->comment_author . ' - ' . $comment->comment_author_email; 413 $objectLink = get_edit_comment_link( $latestObjectID ); 414 415 } elseif ( $type == 'user' ) { 416 $user = get_userdata( $latestObjectID ); 417 $objectTitle = $user->display_name; 418 $objectLink = get_edit_user_link( $latestObjectID ); 419 420 } elseif ( $type == 'term' ) { 421 $term = get_term( $latestObjectID ); 422 if ( $term ) 423 $objectTitle = $term->name; 424 $objectLink = get_edit_term_link( $latestObjectID ); 425 } 426 427 if ( $objectTitle && $objectLink ) { 428 _e( 'The last item checked:', 'meta-optimizer' ); 429 echo " <a href='$objectLink' target='_blank'>$objectTitle</a> $_checkedDate, "; 430 } else { 431 _e( 'Unknown item', 'meta-optimizer' ); 432 echo " $_checkedDate, "; 433 } 434 435 _e( 'Left Items: ', 'meta-optimizer' ); 436 echo ' ' . $Helpers->getObjectLeftItemsCount( $type ) . ", "; 437 } 438 439 echo "<label><input type='checkbox' name='reset_import_" . esc_attr( $type ) . "' value='1'> " . __( 'Reset', 'meta-optimizer' ) . '</label>'; 440 echo '</p>'; 441 } 442 443 echo '<br>'; 444 } 445 ?> 446 </td> 447 </tr> 448 <tr> 449 <td colspan="2"> 450 <p class="description"><?php _e( 'Importing runs in the background without requiring a website to be open.', 'meta-optimizer' ) ?></p> 451 </td> 452 </tr> 453 <tr> 454 <td colspan="2"><input type="submit" class="button button-primary" 455 value="<?php _e( 'Save' ) ?>"></td> 456 </tr> 399 457 </tbody> 400 458 </table> … … 402 460 </div> 403 461 </div> 404 <?php 405 } 406 407 /** 408 * Get option value 409 * 410 * @param string $key Option key 411 * @param mixed $default Default value 412 * @param boolean $useCache Use cache 413 * @return mixed 414 */ 415 public function getOption($key = null, $default = null, $useCache = true) 416 { 417 $options = wp_cache_get('options', WPMETAOPTIMIZER_PLUGIN_KEY); 418 419 if (!$useCache || $options === false) { 420 $options = get_option(WPMETAOPTIMIZER_OPTION_KEY); 421 wp_cache_set('options', $options, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE); 422 } 423 424 if ($key != null) 425 return $options[$key] ?? $default; 426 427 return $options ? $options : $default; 428 } 429 430 /** 431 * Set plugin option 432 * 433 * @param string $key Option key 434 * @param mixed $value Option value 435 * @return boolean 436 */ 437 public function setOption($key, $value) 438 { 439 $options = $this->getOption(null, [], false); 440 if (strpos($key, '_white_list') !== false || strpos($key, '_black_list') !== false) 441 $value = sanitize_textarea_field($value); 442 else 443 $value = is_array($value) ? array_map('sanitize_text_field', $value) : sanitize_text_field($value); 444 $options[$key] = $value; 445 return update_option(WPMETAOPTIMIZER_OPTION_KEY, $options); 446 } 447 448 /** 449 * Get notice message HTML 450 * 451 * @param string $message Message text 452 * @param string $status Message status text 453 * @return string 454 */ 455 private function getNoticeMessageHTML($message, $status = 'success') 456 { 457 return '<div class="notice notice-' . $status . ' is-dismissible" ><p>' . $message . '</p></div> '; 458 } 459 460 /** 461 * Returns an instance of class 462 * @return Options 463 */ 464 static function getInstance() 465 { 466 if (self::$instance == null) 467 self::$instance = new Options(); 468 469 return self::$instance; 470 } 462 <?php 463 } 464 465 /** 466 * Get option value 467 * 468 * @param string $key Option key 469 * @param mixed $default Default value 470 * @param boolean $useCache Use cache 471 * 472 * @return mixed 473 */ 474 public function getOption( $key = null, $default = null, $useCache = true ) { 475 $options = wp_cache_get( 'options', WPMETAOPTIMIZER_PLUGIN_KEY ); 476 477 if ( ! $useCache || $options === false ) { 478 $options = get_option( WPMETAOPTIMIZER_OPTION_KEY ); 479 wp_cache_set( 'options', $options, WPMETAOPTIMIZER_PLUGIN_KEY, WPMETAOPTIMIZER_CACHE_EXPIRE ); 480 } 481 482 if ( $key != null ) 483 return $options[ $key ] ?? $default; 484 485 return $options ?: $default; 486 } 487 488 /** 489 * Set plugin option 490 * 491 * @param string $key Option key 492 * @param mixed $value Option value 493 * 494 * @return boolean 495 */ 496 public function setOption( $key, $value ) { 497 $options = $this->getOption( null, [], false ); 498 if ( strpos( $key, '_white_list' ) !== false || strpos( $key, '_black_list' ) !== false ) 499 $value = sanitize_textarea_field( $value ); 500 else 501 $value = is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : sanitize_text_field( $value ); 502 $options[ $key ] = $value; 503 504 return update_option( WPMETAOPTIMIZER_OPTION_KEY, $options ); 505 } 506 507 /** 508 * Get notice message HTML 509 * 510 * @param string $message Message text 511 * @param string $status Message status text 512 * 513 * @return string 514 */ 515 private function getNoticeMessageHTML( $message, $status = 'success' ) { 516 return '<div class="notice notice-' . $status . ' is-dismissible" ><p>' . $message . '</p></div> '; 517 } 518 519 /** 520 * Returns an instance of class 521 * 522 * @return Options 523 */ 524 static function getInstance() { 525 if ( self::$instance == null ) 526 self::$instance = new Options(); 527 528 return self::$instance; 529 } 471 530 } -
meta-optimizer/trunk/inc/PostQueries.php
r2802191 r2960316 2 2 3 3 namespace WPMetaOptimizer; 4 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 4 7 5 8 /** 6 9 * Query API: PostQueries class. 7 10 * 8 * @package WPMetaOptimizer11 * @package WPMetaOptimizer 9 12 * @subpackage Query 10 * @since 1.013 * @since 1.0 11 14 */ 12 13 class PostQueries 14 { 15 public static $instance = null; 16 private $Queries; 17 18 function __construct($Queries) 19 { 20 $this->Queries = $Queries; 21 add_filter('posts_groupby', [$this, 'changePostsGroupBy'], 9999, 2); 22 add_filter('posts_orderby', [$this, 'changePostsOrderBy'], 9999, 2); 23 } 24 25 /** 26 * Filters the ORDER BY clause of the query. 27 * @copyright Base on WP_Query:get_posts method. 28 * 29 * @since 1.5.1 30 * 31 * @global wpdb $wpdb WordPress database abstraction object. 32 * 33 * @param string $orderBy The ORDER BY clause of the query. 34 * @param \WP_Query $query The WP_Query instance (passed by reference). 35 */ 36 function changePostsOrderBy($orderBy, $query) 37 { 38 global $wpdb; 39 40 if (is_array($query->get('orderby')) || in_array($query->get('orderby'), ['meta_value', 'meta_value_num'])) { 41 $queryVars = $this->Queries->getQueryVars('post', $query->query); 42 $this->Queries->metaQuery->parse_query_vars($queryVars); 43 44 $query->set('orderby', $queryVars['orderby']); 45 46 $orderByQuery = $query->get('orderby'); 47 $orderQuery = $query->get('order'); 48 49 // Order by. 50 if (!empty($orderByQuery) && 'none' !== $orderByQuery) { 51 $orderby_array = array(); 52 53 if (is_array($orderByQuery)) { 54 foreach ($orderByQuery as $_orderby => $order) { 55 $orderby = addslashes_gpc(urldecode($_orderby)); 56 $parsed = $this->postParseOrderby($orderby); 57 58 if (!$parsed) 59 continue; 60 61 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder($order); 62 } 63 $orderBy_ = implode(', ', $orderby_array); 64 } else { 65 $orderByQuery = urldecode($orderByQuery); 66 $orderByQuery = addslashes_gpc($orderByQuery); 67 68 foreach (explode(' ', $orderByQuery) as $i => $orderby) { 69 $parsed = $this->postParseOrderby($orderby); 70 // Only allow certain values for safety. 71 if (!$parsed) { 72 continue; 73 } 74 75 $orderby_array[] = $parsed; 76 } 77 $orderBy_ = implode(' ' . $orderQuery . ', ', $orderby_array); 78 79 if (empty($orderBy_)) { 80 $orderBy_ = "{$wpdb->posts}.post_date " . $orderQuery; 81 } elseif (!empty($orderQuery)) { 82 $orderBy_ .= " {$orderQuery}"; 83 } 84 } 85 86 return $orderBy_; 87 } 88 } 89 90 return $orderBy; 91 } 92 93 /** 94 * Converts the given orderby alias (if allowed) to a properly-prefixed value. 95 * @copyright Base on WP_Query:parse_orderby method. 96 * 97 * @since 4.0.0 98 * 99 * @global wpdb $wpdb WordPress database abstraction object. 100 * 101 * @param string $orderby Alias for the field to order by. 102 * @return string|false Table-prefixed value to used in the ORDER clause. False otherwise. 103 */ 104 protected function postParseOrderby($orderby) 105 { 106 global $wpdb; 107 108 // Used to filter values. 109 $allowed_keys = array( 110 'post_name', 111 'post_author', 112 'post_date', 113 'post_title', 114 'post_modified', 115 'post_parent', 116 'post_type', 117 'name', 118 'author', 119 'date', 120 'title', 121 'modified', 122 'parent', 123 'type', 124 'ID', 125 'menu_order', 126 'comment_count', 127 'rand', 128 'post__in', 129 'post_parent__in', 130 'post_name__in', 131 ); 132 133 $primary_meta_key = ''; 134 $primary_meta_query = false; 135 $meta_clauses = $this->Queries->metaQuery->get_clauses(); 136 137 if (!empty($meta_clauses)) { 138 $primary_meta_query = isset($meta_clauses[$orderby]) ? $meta_clauses[$orderby] : reset($meta_clauses); 139 140 if (!empty($primary_meta_query['key'])) { 141 $primary_meta_key = $primary_meta_query['key']; 142 $allowed_keys[] = $primary_meta_key; 143 } 144 145 $allowed_keys[] = 'meta_value'; 146 $allowed_keys[] = 'meta_value_num'; 147 $allowed_keys = array_merge($allowed_keys, array_keys($meta_clauses)); 148 } 149 150 // If RAND() contains a seed value, sanitize and add to allowed keys. 151 $rand_with_seed = false; 152 if (preg_match('/RAND\(([0-9]+)\)/i', $orderby, $matches)) { 153 $orderby = sprintf('RAND(%s)', (int) $matches[1]); 154 $allowed_keys[] = $orderby; 155 $rand_with_seed = true; 156 } 157 158 if (!in_array($orderby, $allowed_keys, true)) { 159 return false; 160 } 161 162 $orderby_clause = ''; 163 164 switch ($orderby) { 165 case 'post_name': 166 case 'post_author': 167 case 'post_date': 168 case 'post_title': 169 case 'post_modified': 170 case 'post_parent': 171 case 'post_type': 172 case 'ID': 173 case 'menu_order': 174 case 'comment_count': 175 $orderby_clause = "{$wpdb->posts}.{$orderby}"; 176 break; 177 case 'rand': 178 $orderby_clause = 'RAND()'; 179 break; 180 case $primary_meta_key: 181 case 'meta_value': 182 if (!empty($primary_meta_query['type'])) { 183 $orderby_clause = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})"; 184 } else { 185 $orderby_clause = "{$primary_meta_query['alias']}.{$primary_meta_key}"; 186 } 187 break; 188 case 'meta_value_num': 189 $orderby_clause = "{$primary_meta_query['alias']}.{$primary_meta_key}+0"; 190 break; 191 case 'post__in': 192 if (!empty($this->queryVars['post__in'])) { 193 $orderby_clause = "FIELD({$wpdb->posts}.ID," . implode(',', array_map('absint', $this->queryVars['post__in'])) . ')'; 194 } 195 break; 196 case 'post_parent__in': 197 if (!empty($this->queryVars['post_parent__in'])) { 198 $orderby_clause = "FIELD( {$wpdb->posts}.post_parent," . implode(', ', array_map('absint', $this->queryVars['post_parent__in'])) . ' )'; 199 } 200 break; 201 case 'post_name__in': 202 if (!empty($this->queryVars['post_name__in'])) { 203 $post_name__in = array_map('sanitize_title_for_query', $this->queryVars['post_name__in']); 204 $post_name__in_string = "'" . implode("','", $post_name__in) . "'"; 205 $orderby_clause = "FIELD( {$wpdb->posts}.post_name," . $post_name__in_string . ' )'; 206 } 207 break; 208 default: 209 if (array_key_exists($orderby, $meta_clauses)) { 210 // $orderby corresponds to a meta_query clause. 211 $meta_clause = $meta_clauses[$orderby]; 212 $orderby_clause = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})"; 213 } elseif ($rand_with_seed) { 214 $orderby_clause = $orderby; 215 } else { 216 // Default: order by post field. 217 $orderby_clause = "{$wpdb->posts}.post_" . sanitize_key($orderby); 218 } 219 220 break; 221 } 222 223 return $orderby_clause; 224 } 225 226 /** 227 * Filters the GROUP BY clause of the query. 228 * 229 * @since 2.0.0 230 * 231 * @param string $groupby The GROUP BY clause of the query. 232 * @param \WP_Query $query The WP_Query instance (passed by reference). 233 */ 234 function changePostsGroupBy($groupby, $query) 235 { 236 return ""; 237 } 238 239 /** 240 * Returns an instance of class 241 * @return PostQueries 242 */ 243 static function getInstance($Queries) 244 { 245 if (self::$instance == null) 246 self::$instance = new PostQueries($Queries); 247 248 return self::$instance; 249 } 15 class PostQueries { 16 public static $instance = null; 17 private $Queries; 18 19 function __construct( $Queries ) { 20 $this->Queries = $Queries; 21 add_filter( 'posts_groupby', [ $this, 'changePostsGroupBy' ], 9999, 2 ); 22 add_filter( 'posts_orderby', [ $this, 'changePostsOrderBy' ], 9999, 2 ); 23 } 24 25 /** 26 * Filters the ORDER BY clause of the query. 27 * 28 * @param string $orderBy The ORDER BY clause of the query. 29 * @param \WP_Query $query The WP_Query instance (passed by reference). 30 * 31 * @global wpdb $wpdb WordPress database abstraction object. 32 * 33 * @copyright Base on WP_Query:get_posts method. 34 * 35 * @since 1.5.1 36 * 37 */ 38 function changePostsOrderBy( $orderBy, $query ) { 39 global $wpdb; 40 41 if ( is_array( $query->get( 'orderby' ) ) || in_array( $query->get( 'orderby' ), [ 42 'meta_value', 43 'meta_value_num' 44 ] ) ) { 45 $queryVars = $this->Queries->getQueryVars( 'post', $query->query ); 46 $this->Queries->metaQuery->parse_query_vars( $queryVars ); 47 48 $query->set( 'orderby', $queryVars['orderby'] ); 49 50 $orderByQuery = $query->get( 'orderby' ); 51 $orderQuery = $query->get( 'order' ); 52 53 // Order by. 54 if ( ! empty( $orderByQuery ) && 'none' !== $orderByQuery ) { 55 $orderby_array = array(); 56 57 if ( is_array( $orderByQuery ) ) { 58 foreach ( $orderByQuery as $_orderby => $order ) { 59 $orderby = addslashes_gpc( urldecode( $_orderby ) ); 60 $parsed = $this->postParseOrderby( $orderby ); 61 62 if ( ! $parsed ) 63 continue; 64 65 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder( $order ); 66 } 67 $orderBy_ = implode( ', ', $orderby_array ); 68 } else { 69 $orderByQuery = urldecode( $orderByQuery ); 70 $orderByQuery = addslashes_gpc( $orderByQuery ); 71 72 foreach ( explode( ' ', $orderByQuery ) as $orderby ) { 73 $parsed = $this->postParseOrderby( $orderby ); 74 // Only allow certain values for safety. 75 if ( ! $parsed ) { 76 continue; 77 } 78 79 $orderby_array[] = $parsed; 80 } 81 $orderBy_ = implode( ' ' . $orderQuery . ', ', $orderby_array ); 82 83 if ( empty( $orderBy_ ) ) { 84 $orderBy_ = "{$wpdb->posts}.post_date " . $orderQuery; 85 } elseif ( ! empty( $orderQuery ) ) { 86 $orderBy_ .= " {$orderQuery}"; 87 } 88 } 89 90 return $orderBy_; 91 } 92 } 93 94 return $orderBy; 95 } 96 97 /** 98 * Converts the given orderby alias (if allowed) to a properly-prefixed value. 99 * 100 * @param string $orderby Alias for the field to order by. 101 * 102 * @return string|false Table-prefixed value to used in the ORDER clause. False otherwise. 103 * @global \wpdb $wpdb WordPress database abstraction object. 104 * 105 * @copyright Base on WP_Query:parse_orderby method. 106 * 107 * @since 4.0.0 108 * 109 */ 110 protected function postParseOrderby( $orderby ) { 111 global $wpdb; 112 113 // Used to filter values. 114 $allowed_keys = array( 115 'post_name', 116 'post_author', 117 'post_date', 118 'post_title', 119 'post_modified', 120 'post_parent', 121 'post_type', 122 'name', 123 'author', 124 'date', 125 'title', 126 'modified', 127 'parent', 128 'type', 129 'ID', 130 'menu_order', 131 'comment_count', 132 'rand', 133 'post__in', 134 'post_parent__in', 135 'post_name__in', 136 ); 137 138 $primary_meta_key = ''; 139 $primary_meta_query = false; 140 $meta_clauses = $this->Queries->metaQuery->get_clauses(); 141 142 if ( ! empty( $meta_clauses ) ) { 143 $primary_meta_query = $meta_clauses[ $orderby ] ?? reset( $meta_clauses ); 144 145 if ( ! empty( $primary_meta_query['key'] ) ) { 146 $primary_meta_key = $primary_meta_query['key']; 147 $allowed_keys[] = $primary_meta_key; 148 } 149 150 $allowed_keys[] = 'meta_value'; 151 $allowed_keys[] = 'meta_value_num'; 152 $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_clauses ) ); 153 } 154 155 // If RAND() contains a seed value, sanitize and add to allowed keys. 156 $rand_with_seed = false; 157 if ( preg_match( '/RAND\(([0-9]+)\)/i', $orderby, $matches ) ) { 158 $orderby = sprintf( 'RAND(%s)', (int) $matches[1] ); 159 $allowed_keys[] = $orderby; 160 $rand_with_seed = true; 161 } 162 163 if ( ! in_array( $orderby, $allowed_keys, true ) ) { 164 return false; 165 } 166 167 $orderby_clause = ''; 168 169 switch ( $orderby ) { 170 case 'post_name': 171 case 'post_author': 172 case 'post_date': 173 case 'post_title': 174 case 'post_modified': 175 case 'post_parent': 176 case 'post_type': 177 case 'ID': 178 case 'menu_order': 179 case 'comment_count': 180 $orderby_clause = "{$wpdb->posts}.{$orderby}"; 181 break; 182 case 'rand': 183 $orderby_clause = 'RAND()'; 184 break; 185 case $primary_meta_key: 186 case 'meta_value': 187 if ( ! empty( $primary_meta_query['type'] ) ) { 188 $orderby_clause = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})"; 189 } else { 190 $orderby_clause = "{$primary_meta_query['alias']}.{$primary_meta_key}"; 191 } 192 break; 193 case 'meta_value_num': 194 $orderby_clause = "{$primary_meta_query['alias']}.{$primary_meta_key}+0"; 195 break; 196 case 'post__in': 197 if ( ! empty( $this->queryVars['post__in'] ) ) { 198 $orderby_clause = "FIELD({$wpdb->posts}.ID," . implode( ',', array_map( 'absint', $this->queryVars['post__in'] ) ) . ')'; 199 } 200 break; 201 case 'post_parent__in': 202 if ( ! empty( $this->queryVars['post_parent__in'] ) ) { 203 $orderby_clause = "FIELD( {$wpdb->posts}.post_parent," . implode( ', ', array_map( 'absint', $this->queryVars['post_parent__in'] ) ) . ' )'; 204 } 205 break; 206 case 'post_name__in': 207 if ( ! empty( $this->queryVars['post_name__in'] ) ) { 208 $post_name__in = array_map( 'sanitize_title_for_query', $this->queryVars['post_name__in'] ); 209 $post_name__in_string = "'" . implode( "','", $post_name__in ) . "'"; 210 $orderby_clause = "FIELD( {$wpdb->posts}.post_name," . $post_name__in_string . ' )'; 211 } 212 break; 213 default: 214 if ( array_key_exists( $orderby, $meta_clauses ) ) { 215 // $orderby corresponds to a meta_query clause. 216 $meta_clause = $meta_clauses[ $orderby ]; 217 $orderby_clause = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})"; 218 } elseif ( $rand_with_seed ) { 219 $orderby_clause = $orderby; 220 } else { 221 // Default: order by post field. 222 $orderby_clause = "{$wpdb->posts}.post_" . sanitize_key( $orderby ); 223 } 224 225 break; 226 } 227 228 return $orderby_clause; 229 } 230 231 /** 232 * Filters the GROUP BY clause of the query. 233 * 234 * @param string $groupby The GROUP BY clause of the query. 235 * @param \WP_Query $query The WP_Query instance (passed by reference). 236 * 237 * @since 2.0.0 238 * 239 */ 240 function changePostsGroupBy( $groupby, $query ) { 241 return ""; 242 } 243 244 /** 245 * Returns an instance of class 246 * 247 * @return PostQueries 248 */ 249 static function getInstance( $Queries ) { 250 if ( self::$instance == null ) 251 self::$instance = new PostQueries( $Queries ); 252 253 return self::$instance; 254 } 250 255 } -
meta-optimizer/trunk/inc/Queries.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 use WP_Query; 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 6 7 7 class Queries extends Base 8 { 9 public static $instance = null; 10 protected $Helpers; 8 class Queries extends Base { 9 public static $instance = null; 10 protected $Helpers; 11 11 12 /**13 * Metadata query container.14 *15 * @since 3.2.016 * @var MetaQuery A meta query instance.17 */18 public $metaQuery = false;12 /** 13 * Metadata query container. 14 * 15 * @since 3.2.0 16 * @var MetaQuery A meta query instance. 17 */ 18 public $metaQuery = false; 19 19 20 /**21 * Query vars set by the user.22 *23 * @since 3.1.024 * @var array25 */26 public $queryVars;20 /** 21 * Query vars set by the user. 22 * 23 * @since 3.1.0 24 * @var array 25 */ 26 public $queryVars; 27 27 28 function __construct() 29 { 30 parent::__construct(); 28 function __construct() { 29 parent::__construct(); 31 30 32 $this->Helpers= Helpers::getInstance();33 $this->metaQuery = new MetaQuery(false, $this->Helpers);31 $this->Helpers = Helpers::getInstance(); 32 $this->metaQuery = new MetaQuery( false, $this->Helpers ); 34 33 35 if ($this->Helpers->checkSupportWPQuery()) {36 add_filter('get_meta_sql', [$this, 'changeMetaSQL'], 9999, 6);34 if ( $this->Helpers->checkSupportWPQuery() ) { 35 add_filter( 'get_meta_sql', [ $this, 'changeMetaSQL' ], 9999, 6 ); 37 36 38 PostQueries::getInstance($this);39 CommentQueries::getInstance($this);40 UserQueries::getInstance($this);41 TermQueries::getInstance($this);42 }43 }37 PostQueries::getInstance( $this ); 38 CommentQueries::getInstance( $this ); 39 UserQueries::getInstance( $this ); 40 TermQueries::getInstance( $this ); 41 } 42 } 44 43 45 /**46 * Change meta SQL of default WordPress meta types47 *48 * @param string[] $sql Array containing the query's JOIN and WHERE clauses.49 * @param array $queries Array of meta queries.50 * @param string $type Type of meta. Possible values include but are not limited51 * to 'post', 'comment', 'blog', 'term', and 'user'.52 * @param string $primaryTablePrimary table.53 * @param string $primaryIDColumnPrimary column ID.54 * @param object $context The main query object that corresponds to the type, for55 * example a `WP_Query`, `WP_User_Query`, or `WP_Site_Query`.56 * @return string SQL Query 57 */ 58 function changeMetaSQL($sql, $queries, $type, $primaryTable, $primaryIDColumn, $context) 59 {60 if (!is_object($context))61 return $sql;44 /** 45 * Change meta SQL of default WordPress meta types 46 * 47 * @param string[] $sql Array containing the query's JOIN and WHERE clauses. 48 * @param array $queries Array of meta queries. 49 * @param string $type Type of meta. Possible values include but are not limited 50 * to 'post', 'comment', 'blog', 'term', and 'user'. 51 * @param string $primaryTable Primary table. 52 * @param string $primaryIDColumn Primary column ID. 53 * @param object $context The main query object that corresponds to the type, for 54 * example a `WP_Query`, `WP_User_Query`, or `WP_Site_Query`. 55 * 56 * @return string[] SQL Query 57 */ 58 function changeMetaSQL( $sql, $queries, $type, $primaryTable, $primaryIDColumn, $context ) { 59 if ( ! is_object( $context ) ) 60 return $sql; 62 61 63 $this->metaQuery = new MetaQuery(false, $this->Helpers);62 $this->metaQuery = new MetaQuery( false, $this->Helpers ); 64 63 65 $this->queryVars = $this->getQueryVars($type, $context->query_vars);64 $this->queryVars = $this->getQueryVars( $type, $context->query_vars ); 66 65 67 // Parse meta query.68 $this->metaQuery->parse_query_vars($this->queryVars);66 // Parse meta query. 67 $this->metaQuery->parse_query_vars( $this->queryVars ); 69 68 70 if (!empty($this->metaQuery->queries))71 $sql = $this->metaQuery->get_sql($type, $primaryTable, $primaryIDColumn, $this);69 if ( ! empty( $this->metaQuery->queries ) ) 70 $sql = $this->metaQuery->get_sql( $type, $primaryTable, $primaryIDColumn, $this ); 72 71 73 return $sql;74 }72 return $sql; 73 } 75 74 76 /**77 * Get query variables78 *79 * @param string $type Meta type80 * @param array $queryVarsCurrent query vars81 * @return array 82 */ 83 public function getQueryVars($type, $queryVars) 84 {85 // Change Meta Key86 if (isset($queryVars['meta_key']) && $queryVars['meta_key'])87 $queryVars['meta_key'] = $this->Helpers->translateColumnName($type, $queryVars['meta_key']);75 /** 76 * Get query variables 77 * 78 * @param string $type Meta type 79 * @param array $queryVars Current query vars 80 * 81 * @return array 82 */ 83 public function getQueryVars( $type, $queryVars ) { 84 // Change Meta Key 85 if ( isset( $queryVars['meta_key'] ) && $queryVars['meta_key'] ) 86 $queryVars['meta_key'] = $this->Helpers->translateColumnName( $type, $queryVars['meta_key'] ); 88 87 89 // Change Meta Query 90 if (isset($queryVars['meta_query']) && is_array($queryVars['meta_query'])) 91 foreach ($queryVars['meta_query'] as $key => $query) 92 if (isset($query['key'])) { 93 $keyIndex = $key; 94 if (is_string($key)) 95 $keyIndex = $this->Helpers->translateColumnName($type, $key); 96 if ($keyIndex !== $key) 97 unset($queryVars['meta_query'][$key]); 98 $query['key'] = $this->Helpers->translateColumnName($type, $query['key']); 99 $queryVars['meta_query'][$keyIndex] = $query; 100 } 88 // Change Meta Query 89 if ( isset( $queryVars['meta_query'] ) && is_array( $queryVars['meta_query'] ) ) 90 foreach ( $queryVars['meta_query'] as $key => $query ) { 91 if ( isset( $query['key'] ) ) { 92 $keyIndex = $key; 93 if ( is_string( $key ) ) 94 $keyIndex = $this->Helpers->translateColumnName( $type, $key ); 95 if ( $keyIndex !== $key ) 96 unset( $queryVars['meta_query'][ $key ] ); 97 $query['key'] = $this->Helpers->translateColumnName( $type, $query['key'] ); 98 $queryVars['meta_query'][ $keyIndex ] = $query; 99 } 100 } 101 101 102 // Change OrderBy103 if (isset($queryVars['orderby']) && is_array($queryVars['orderby'])) {104 foreach ($queryVars['orderby'] as $key => $order) {105 $keyIndex = $key;106 if (is_string($key))107 $keyIndex = $this->Helpers->translateColumnName($type, $key);108 if ($keyIndex !== $key)109 unset($queryVars['orderby'][$key]);102 // Change OrderBy 103 if ( isset( $queryVars['orderby'] ) && is_array( $queryVars['orderby'] ) ) { 104 foreach ( $queryVars['orderby'] as $key => $order ) { 105 $keyIndex = $key; 106 if ( is_string( $key ) ) 107 $keyIndex = $this->Helpers->translateColumnName( $type, $key ); 108 if ( $keyIndex !== $key ) 109 unset( $queryVars['orderby'][ $key ] ); 110 110 111 $queryVars['orderby'][$keyIndex] = $order;112 }113 }111 $queryVars['orderby'][ $keyIndex ] = $order; 112 } 113 } 114 114 115 return $queryVars;116 }115 return $queryVars; 116 } 117 117 118 /** 119 * Parse an 'order' query variable and cast it to ASC or DESC as necessary. 120 * @copyright Base on WordPress parse_order method 121 * 122 * @since 4.0.0 123 * 124 * @param string $order The 'order' query variable. 125 * @return string The sanitized 'order' query variable. 126 */ 127 public function parseOrder($order) 128 { 129 if (!is_string($order) || empty($order)) { 130 return 'DESC'; 131 } 118 /** 119 * Parse an 'order' query variable and cast it to ASC or DESC as necessary. 120 * 121 * @param string $order The 'order' query variable. 122 * 123 * @return string The sanitized 'order' query variable. 124 * @copyright Base on WordPress parse_order method 125 * 126 * @since 4.0.0 127 * 128 */ 129 public function parseOrder( $order ) { 130 if ( ! is_string( $order ) || empty( $order ) ) { 131 return 'DESC'; 132 } 132 133 133 if ('ASC' === strtoupper($order)) {134 return 'ASC';135 } else {136 return 'DESC';137 }138 }134 if ( 'ASC' === strtoupper( $order ) ) { 135 return 'ASC'; 136 } else { 137 return 'DESC'; 138 } 139 } 139 140 140 /**141 * Returns an instance of class142 * @return Queries 143 */ 144 static function getInstance() 145 {146 if (self::$instance == null)147 self::$instance = new Queries();141 /** 142 * Returns an instance of class 143 * 144 * @return Queries 145 */ 146 static function getInstance() { 147 if ( self::$instance == null ) 148 self::$instance = new Queries(); 148 149 149 return self::$instance;150 }150 return self::$instance; 151 } 151 152 } -
meta-optimizer/trunk/inc/TermQueries.php
r2802191 r2960316 2 2 3 3 namespace WPMetaOptimizer; 4 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 4 7 5 8 /** 6 9 * Taxonomy API: TermQueries class. 7 10 * 8 * @package WPMetaOptimizer11 * @package WPMetaOptimizer 9 12 * @subpackage Taxonomy 10 * @since 1.013 * @since 1.0 11 14 */ 12 13 class TermQueries 14 { 15 public static $instance = null; 16 private $Queries; 17 18 function __construct($Queries) 19 { 20 $this->Queries = $Queries; 21 22 add_action('pre_get_terms', [$this, 'changeTermQuery'], 9999, 3); 23 add_filter('terms_clauses', [$this, 'changeTermsClauses'], 9999, 3); 24 } 25 26 /** 27 * Change term meta query class 28 * 29 * @param \WP_Term_Query $termQuery Term query 30 */ 31 function changeTermQuery($termQuery) 32 { 33 $this->Queries->metaQuery = $termQuery->meta_query = new MetaQuery(); 34 $termQuery->meta_query->parse_query_vars($termQuery->query_vars); 35 $this->Queries->queryVars = $termQuery->query_vars; 36 } 37 38 /** 39 * Change term clauses query 40 * @copyright Base on WP_Term_Query:get_terms method. 41 * 42 * @param string[] $clauses { 43 * Associative array of the clauses for the query. 44 * 45 * @type string $fields The SELECT clause of the query. 46 * @type string $join The JOIN clause of the query. 47 * @type string $where The WHERE clause of the query. 48 * @type string $distinct The DISTINCT clause of the query. 49 * @type string $orderby The ORDER BY clause of the query. 50 * @type string $order The ORDER clause of the query. 51 * @type string $limits The LIMIT clause of the query. 52 * } 53 * @param string[] $taxonomies An array of taxonomy names. 54 * @param array $args An array of term query arguments. 55 * @return array 56 */ 57 function changeTermsClauses($clauses, $taxonomies, $args) 58 { 59 $this->Queries->queryVars['order'] = isset($this->Queries->queryVars['order']) ? strtoupper($this->Queries->queryVars['order']) : ''; 60 $order = $this->Queries->parseOrder($this->Queries->queryVars['order']); 61 // 'term_order' is a legal sort order only when joining the relationship table. 62 $_orderby = $this->Queries->queryVars['orderby']; 63 64 if (empty($this->Queries->queryVars['orderby']) || 'term_order' === $_orderby && empty($this->Queries->queryVars['object_ids'])) { 65 $ordersby = ['term_id']; 66 } elseif (is_array($_orderby)) { 67 $ordersby = $_orderby; 68 } else { 69 // 'orderby' values may be a comma- or space-separated list. 70 $ordersby = preg_split('/[,\s]+/', $_orderby); 71 } 72 73 $orderby_array = array(); 74 foreach ($ordersby as $_key => $_value) { 75 if (is_int($_key)) { 76 // Integer key means this is a flat array of 'orderby' fields. 77 $_orderby = $_value; 78 $_order = $order; 79 } else { 80 // Non-integer key means this the key is the field and the value is ASC/DESC. 81 $_orderby = $_key; 82 $_order = $_value; 83 } 84 85 $parsed = $this->termParseOrderby($_orderby); 86 87 if (!$parsed) 88 continue; 89 90 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder($_order); 91 } 92 93 if (!empty($orderby_array)) { 94 $clauses['orderby'] = 'ORDER BY ' . implode(', ', $orderby_array); 95 $clauses['order'] = ''; 96 } 97 98 // $clauses['distinct'] = ''; 99 100 return $clauses; 101 } 102 103 /** 104 * Parse and sanitize 'orderby' keys passed to the term query. 105 * @copyright Base on WP_Term_Query:parse_orderby method. 106 * 107 * @since 4.6.0 108 * 109 * @global wpdb $wpdb WordPress database abstraction object. 110 * 111 * @param string $orderby_raw Alias for the field to order by. 112 * @return string|false Value to used in the ORDER clause. False otherwise. 113 */ 114 protected function termParseOrderby($orderby_raw) 115 { 116 $_orderby = is_array($orderby_raw) ? reset(array_keys($orderby_raw)) : strtolower($orderby_raw); 117 118 $maybe_orderby_meta = false; 119 120 if (in_array($_orderby, array('term_id', 'name', 'slug', 'term_group'), true)) { 121 $orderby = "t.$_orderby"; 122 } elseif (in_array($_orderby, array('count', 'parent', 'taxonomy', 'term_taxonomy_id', 'description'), true)) { 123 $orderby = "tt.$_orderby"; 124 } elseif ('term_order' === $_orderby) { 125 $orderby = 'tr.term_order'; 126 } elseif ('include' === $_orderby && !empty($this->Queries->queryVars['include'])) { 127 $include = implode(',', wp_parse_id_list($this->Queries->queryVars['include'])); 128 $orderby = "FIELD( t.term_id, $include )"; 129 } elseif ('slug__in' === $_orderby && !empty($this->Queries->queryVars['slug']) && is_array($this->Queries->queryVars['slug'])) { 130 $slugs = implode("', '", array_map('sanitize_title_for_query', $this->Queries->queryVars['slug'])); 131 $orderby = "FIELD( t.slug, '" . $slugs . "')"; 132 } elseif ('none' === $_orderby) { 133 $orderby = ''; 134 } elseif (empty($_orderby) || 'id' === $_orderby || 'term_id' === $_orderby) { 135 $orderby = 't.term_id'; 136 } else { 137 $orderby = 't.name'; 138 139 // This may be a value of orderby related to meta. 140 $maybe_orderby_meta = true; 141 } 142 143 /** 144 * Filters the ORDERBY clause of the terms query. 145 * 146 * @since 2.8.0 147 * 148 * @param string $orderby `ORDERBY` clause of the terms query. 149 * @param array $args An array of term query arguments. 150 * @param string[] $taxonomies An array of taxonomy names. 151 */ 152 $orderby = apply_filters('get_terms_orderby', $orderby, $this->Queries->queryVars, $this->Queries->queryVars['taxonomy']); 153 154 // Run after the 'get_terms_orderby' filter for backward compatibility. 155 if ($maybe_orderby_meta) { 156 $maybe_orderby_meta = $this->termParseOrderbyMeta($_orderby); 157 if ($maybe_orderby_meta) { 158 $orderby = $maybe_orderby_meta; 159 } 160 } 161 162 return $orderby; 163 } 164 165 /** 166 * Generate the ORDER BY clause for an 'orderby' param that is potentially related to a meta query. 167 * @copyright Base on WP_Term_Query:parse_orderby_meta method. 168 * 169 * @since 4.6.0 170 * 171 * @param string $orderby_raw Raw 'orderby' value passed to WP_Term_Query. 172 * @return string ORDER BY clause. 173 */ 174 protected function termParseOrderbyMeta($orderby_raw) 175 { 176 $orderby = ''; 177 178 // Tell the meta query to generate its SQL, so we have access to table aliases. 179 $this->Queries->metaQuery->get_sql('term', 't', 'term_id'); 180 $meta_clauses = $this->Queries->metaQuery->get_clauses(); 181 if (!$meta_clauses || !$orderby_raw) { 182 return $orderby; 183 } 184 185 $allowed_keys = array(); 186 $primary_meta_key = null; 187 // $primary_meta_query = reset($meta_clauses); 188 $primary_meta_query = isset($meta_clauses[$orderby_raw]) ? $meta_clauses[$orderby_raw] : reset($meta_clauses); 189 if (!empty($primary_meta_query['key'])) { 190 $primary_meta_key = $primary_meta_query['key']; 191 $allowed_keys[] = $primary_meta_key; 192 } 193 194 $allowed_keys[] = 'meta_value'; 195 $allowed_keys[] = 'meta_value_num'; 196 $allowed_keys = array_merge($allowed_keys, array_keys($meta_clauses)); 197 198 if (!in_array($orderby_raw, $allowed_keys, true)) { 199 return $orderby; 200 } 201 202 switch ($orderby_raw) { 203 case $primary_meta_key: 204 case 'meta_value': 205 /* if (!empty($primary_meta_query['type'])) { 206 $orderby = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})"; 207 } else { 208 $orderby = "{$primary_meta_query['alias']}.meta_value"; 209 } */ 210 if (!empty($primary_meta_query['type'])) { 211 $orderby = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})"; 212 } else { 213 $orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}"; 214 } 215 216 break; 217 218 case 'meta_value_num': 219 // $orderby = "{$primary_meta_query['alias']}.meta_value+0"; 220 $orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}+0"; 221 break; 222 223 default: 224 if (array_key_exists($orderby_raw, $meta_clauses)) { 225 // $orderby corresponds to a meta_query clause. 226 $meta_clause = $meta_clauses[$orderby_raw]; 227 // $orderby = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})"; 228 $orderby = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})"; 229 } 230 break; 231 } 232 233 return $orderby; 234 } 235 236 /** 237 * Returns an instance of class 238 * @return TermQueries 239 */ 240 static function getInstance($Queries) 241 { 242 if (self::$instance == null) 243 self::$instance = new TermQueries($Queries); 244 245 return self::$instance; 246 } 15 class TermQueries { 16 public static $instance = null; 17 private $Queries; 18 19 function __construct( $Queries ) { 20 $this->Queries = $Queries; 21 22 add_action( 'pre_get_terms', [ $this, 'changeTermQuery' ], 9999, 3 ); 23 add_filter( 'terms_clauses', [ $this, 'changeTermsClauses' ], 9999, 3 ); 24 } 25 26 /** 27 * Change term meta query class 28 * 29 * @param \WP_Term_Query $termQuery Term query 30 */ 31 function changeTermQuery( $termQuery ) { 32 $this->Queries->metaQuery = $termQuery->meta_query = new MetaQuery(); 33 $termQuery->meta_query->parse_query_vars( $termQuery->query_vars ); 34 $this->Queries->queryVars = $termQuery->query_vars; 35 } 36 37 /** 38 * Change term clauses query 39 * 40 * @param string[] $clauses { 41 * Associative array of the clauses for the query. 42 * 43 * @type string $fields The SELECT clause of the query. 44 * @type string $join The JOIN clause of the query. 45 * @type string $where The WHERE clause of the query. 46 * @type string $distinct The DISTINCT clause of the query. 47 * @type string $orderby The ORDER BY clause of the query. 48 * @type string $order The ORDER clause of the query. 49 * @type string $limits The LIMIT clause of the query. 50 * } 51 * 52 * @param string[] $taxonomies An array of taxonomy names. 53 * @param array $args An array of term query arguments. 54 * 55 * @return array 56 * @copyright Base on WP_Term_Query:get_terms method. 57 * 58 */ 59 function changeTermsClauses( $clauses, $taxonomies, $args ) { 60 $this->Queries->queryVars['order'] = isset( $this->Queries->queryVars['order'] ) ? strtoupper( $this->Queries->queryVars['order'] ) : ''; 61 $order = $this->Queries->parseOrder( $this->Queries->queryVars['order'] ); 62 // 'term_order' is a legal sort order only when joining the relationship table. 63 $_orderby = $this->Queries->queryVars['orderby']; 64 65 if ( empty( $this->Queries->queryVars['orderby'] ) || 'term_order' === $_orderby && empty( $this->Queries->queryVars['object_ids'] ) ) { 66 $ordersby = [ 'term_id' ]; 67 } elseif ( is_array( $_orderby ) ) { 68 $ordersby = $_orderby; 69 } else { 70 // 'orderby' values may be a comma- or space-separated list. 71 $ordersby = preg_split( '/[,\s]+/', $_orderby ); 72 } 73 74 $orderby_array = array(); 75 foreach ( $ordersby as $_key => $_value ) { 76 if ( is_int( $_key ) ) { 77 // Integer key means this is a flat array of 'orderby' fields. 78 $_orderby = $_value; 79 $_order = $order; 80 } else { 81 // Non-integer key means this the key is the field and the value is ASC/DESC. 82 $_orderby = $_key; 83 $_order = $_value; 84 } 85 86 $parsed = $this->termParseOrderby( $_orderby ); 87 88 if ( ! $parsed ) 89 continue; 90 91 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder( $_order ); 92 } 93 94 if ( ! empty( $orderby_array ) ) { 95 $clauses['orderby'] = 'ORDER BY ' . implode( ', ', $orderby_array ); 96 $clauses['order'] = ''; 97 } 98 99 // $clauses['distinct'] = ''; 100 101 return $clauses; 102 } 103 104 /** 105 * Parse and sanitize 'orderby' keys passed to the term query. 106 * 107 * @param string $orderby_raw Alias for the field to order by. 108 * 109 * @return string|false Value to used in the ORDER clause. False otherwise. 110 * @global \wpdb $wpdb WordPress database abstraction object. 111 * 112 * @copyright Base on WP_Term_Query:parse_orderby method. 113 * 114 * @since 4.6.0 115 * 116 */ 117 protected function termParseOrderby( $orderby_raw ) { 118 $orderbyKeys = is_array( $orderby_raw ) ? array_keys( $orderby_raw ) : []; 119 $_orderby = is_array( $orderby_raw ) ? reset($orderbyKeys) : strtolower( $orderby_raw ); 120 121 $maybe_orderby_meta = false; 122 123 if ( in_array( $_orderby, array( 'term_id', 'name', 'slug', 'term_group' ), true ) ) { 124 $orderby = "t.$_orderby"; 125 } elseif ( in_array( $_orderby, array( 126 'count', 127 'parent', 128 'taxonomy', 129 'term_taxonomy_id', 130 'description' 131 ), true ) ) { 132 $orderby = "tt.$_orderby"; 133 } elseif ( 'term_order' === $_orderby ) { 134 $orderby = 'tr.term_order'; 135 } elseif ( 'include' === $_orderby && ! empty( $this->Queries->queryVars['include'] ) ) { 136 $include = implode( ',', wp_parse_id_list( $this->Queries->queryVars['include'] ) ); 137 $orderby = "FIELD( t.term_id, $include )"; 138 } elseif ( 'slug__in' === $_orderby && ! empty( $this->Queries->queryVars['slug'] ) && is_array( $this->Queries->queryVars['slug'] ) ) { 139 $slugs = implode( "', '", array_map( 'sanitize_title_for_query', $this->Queries->queryVars['slug'] ) ); 140 $orderby = "FIELD( t.slug, '" . $slugs . "')"; 141 } elseif ( 'none' === $_orderby ) { 142 $orderby = ''; 143 } elseif ( empty( $_orderby ) || 'id' === $_orderby || 'term_id' === $_orderby ) { 144 $orderby = 't.term_id'; 145 } else { 146 $orderby = 't.name'; 147 148 // This may be a value of orderby related to meta. 149 $maybe_orderby_meta = true; 150 } 151 152 /** 153 * Filters the ORDERBY clause of the terms query. 154 * 155 * @param string $orderby `ORDERBY` clause of the terms query. 156 * @param array $args An array of term query arguments. 157 * @param string[] $taxonomies An array of taxonomy names. 158 * 159 * @since 2.8.0 160 * 161 */ 162 $orderby = apply_filters( 'get_terms_orderby', $orderby, $this->Queries->queryVars, $this->Queries->queryVars['taxonomy'] ); 163 164 // Run after the 'get_terms_orderby' filter for backward compatibility. 165 if ( $maybe_orderby_meta ) { 166 $maybe_orderby_meta = $this->termParseOrderbyMeta( $_orderby ); 167 if ( $maybe_orderby_meta ) { 168 $orderby = $maybe_orderby_meta; 169 } 170 } 171 172 return $orderby; 173 } 174 175 /** 176 * Generate the ORDER BY clause for an 'orderby' param that is potentially related to a meta query. 177 * 178 * @param string $orderby_raw Raw 'orderby' value passed to WP_Term_Query. 179 * 180 * @return string ORDER BY clause. 181 * @copyright Base on WP_Term_Query:parse_orderby_meta method. 182 * 183 * @since 4.6.0 184 * 185 */ 186 protected function termParseOrderbyMeta( $orderby_raw ) { 187 $orderby = ''; 188 189 // Tell the meta query to generate its SQL, so we have access to table aliases. 190 $this->Queries->metaQuery->get_sql( 'term', 't', 'term_id' ); 191 $meta_clauses = $this->Queries->metaQuery->get_clauses(); 192 if ( ! $meta_clauses || ! $orderby_raw ) { 193 return $orderby; 194 } 195 196 $allowed_keys = array(); 197 $primary_meta_key = null; 198 // $primary_meta_query = reset($meta_clauses); 199 $primary_meta_query = isset( $meta_clauses[ $orderby_raw ] ) ? $meta_clauses[ $orderby_raw ] : reset( $meta_clauses ); 200 if ( ! empty( $primary_meta_query['key'] ) ) { 201 $primary_meta_key = $primary_meta_query['key']; 202 $allowed_keys[] = $primary_meta_key; 203 } 204 205 $allowed_keys[] = 'meta_value'; 206 $allowed_keys[] = 'meta_value_num'; 207 $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_clauses ) ); 208 209 if ( ! in_array( $orderby_raw, $allowed_keys, true ) ) { 210 return $orderby; 211 } 212 213 switch ( $orderby_raw ) { 214 case $primary_meta_key: 215 case 'meta_value': 216 /* if (!empty($primary_meta_query['type'])) { 217 $orderby = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})"; 218 } else { 219 $orderby = "{$primary_meta_query['alias']}.meta_value"; 220 } */ 221 if ( ! empty( $primary_meta_query['type'] ) ) { 222 $orderby = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})"; 223 } else { 224 $orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}"; 225 } 226 227 break; 228 229 case 'meta_value_num': 230 // $orderby = "{$primary_meta_query['alias']}.meta_value+0"; 231 $orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}+0"; 232 break; 233 234 default: 235 if ( array_key_exists( $orderby_raw, $meta_clauses ) ) { 236 // $orderby corresponds to a meta_query clause. 237 $meta_clause = $meta_clauses[ $orderby_raw ]; 238 // $orderby = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})"; 239 $orderby = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})"; 240 } 241 break; 242 } 243 244 return $orderby; 245 } 246 247 /** 248 * Returns an instance of class 249 * 250 * @return TermQueries 251 */ 252 static function getInstance( $Queries ) { 253 if ( self::$instance == null ) 254 self::$instance = new TermQueries( $Queries ); 255 256 return self::$instance; 257 } 247 258 } -
meta-optimizer/trunk/inc/UserQueries.php
r2802191 r2960316 3 3 namespace WPMetaOptimizer; 4 4 5 // Check run from WP 6 defined( 'ABSPATH' ) || die(); 7 5 8 /** 6 9 * User API: UserQueries class. 7 10 * 8 * @package WPMetaOptimizer11 * @package WPMetaOptimizer 9 12 * @subpackage Users 10 * @since 1.013 * @since 1.0 11 14 */ 15 class UserQueries { 16 public static $instance = null; 17 private $Queries; 12 18 13 class UserQueries 14 { 15 public static $instance = null; 16 private $Queries; 19 function __construct( $Queries ) { 20 $this->Queries = $Queries; 17 21 18 function __construct($Queries) 19 { 20 $this->Queries = $Queries; 22 add_action( 'pre_user_query', [ $this, 'changeUserQuery' ], 9999 ); 23 } 21 24 22 add_action('pre_user_query', [$this, 'changeUserQuery'], 9999); 23 } 25 /** 26 * Chnage user query 27 * 28 * @param \WP_User_Query $query instance of WP_User_Query (passed by reference). 29 * 30 * @return void 31 * @copyright Base on WP_User_Query:prepare_query method. 32 * 33 */ 34 function changeUserQuery( $query ) { 35 $qv = $this->Queries->queryVars; // $this->Queries->queryVars = $query->query_vars; 24 36 25 /** 26 * Chnage user query 27 * @copyright Base on WP_User_Query:prepare_query method. 28 * 29 * @param \WP_User_Query $query instance of WP_User_Query (passed by reference). 30 * 31 * @return void 32 */ 33 function changeUserQuery($query) 34 { 35 $qv = $this->Queries->queryVars; // $this->Queries->queryVars = $query->query_vars; 37 $qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : ''; 38 $order = $this->Queries->parseOrder( $qv['order'] ); 36 39 37 $qv['order'] = isset($qv['order']) ? strtoupper($qv['order']) : ''; 38 $order = $this->Queries->parseOrder($qv['order']); 40 if ( empty( $qv['orderby'] ) ) { 41 // Default order is by 'user_login'. 42 $ordersby = array( 'user_login' => $order ); 43 } elseif ( is_array( $qv['orderby'] ) ) { 44 $ordersby = $qv['orderby']; 45 } else { 46 // 'orderby' values may be a comma- or space-separated list. 47 $ordersby = preg_split( '/[,\s]+/', $qv['orderby'] ); 48 } 39 49 40 if (empty($qv['orderby'])) { 41 // Default order is by 'user_login'. 42 $ordersby = array('user_login' => $order); 43 } elseif (is_array($qv['orderby'])) { 44 $ordersby = $qv['orderby']; 45 } else { 46 // 'orderby' values may be a comma- or space-separated list. 47 $ordersby = preg_split('/[,\s]+/', $qv['orderby']); 48 } 50 $orderby_array = array(); 51 foreach ( $ordersby as $_key => $_value ) { 52 if ( ! $_value ) { 53 continue; 54 } 49 55 50 $orderby_array = array(); 51 foreach ($ordersby as $_key => $_value) { 52 if (!$_value) { 53 continue; 54 } 56 if ( is_int( $_key ) ) { 57 // Integer key means this is a flat array of 'orderby' fields. 58 $_orderby = $_value; 59 $_order = $order; 60 } else { 61 // Non-integer key means this the key is the field and the value is ASC/DESC. 62 $_orderby = $_key; 63 $_order = $_value; 64 } 55 65 56 if (is_int($_key)) { 57 // Integer key means this is a flat array of 'orderby' fields. 58 $_orderby = $_value; 59 $_order = $order; 60 } else { 61 // Non-integer key means this the key is the field and the value is ASC/DESC. 62 $_orderby = $_key; 63 $_order = $_value; 64 } 66 $parsed = $this->userParseOrderby( $_orderby, $query ); 65 67 66 $parsed = $this->userParseOrderby($_orderby, $query); 68 if ( ! $parsed ) 69 continue; 67 70 68 if (!$parsed) 69 continue; 71 if ( 'nicename__in' === $_orderby || 'login__in' === $_orderby ) { 72 $orderby_array[] = $parsed; 73 } else { 74 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder( $_order ); 75 } 76 } 70 77 71 if ('nicename__in' === $_orderby || 'login__in' === $_orderby) { 72 $orderby_array[] = $parsed; 73 } else { 74 $orderby_array[] = $parsed . ' ' . $this->Queries->parseOrder($_order); 75 } 76 } 78 // If no valid clauses were found, order by user_login. 79 if ( empty( $orderby_array ) ) { 80 $orderby_array[] = "user_login $order"; 81 } 77 82 78 // If no valid clauses were found, order by user_login. 79 if (empty($orderby_array)) { 80 $orderby_array[] = "user_login $order"; 81 } 83 $query->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array ); 82 84 83 $query->query_orderby = 'ORDER BY ' . implode(', ', $orderby_array); 85 // Remove DISTINCT from query fields 86 $query->query_fields = trim( str_replace( 'DISTINCT', '', $query->query_fields ) ); 87 } 84 88 85 // Remove DISTINCT from query fields 86 $query->query_fields = trim(str_replace('DISTINCT', '', $query->query_fields)); 87 } 89 /** 90 * Parses and sanitizes 'orderby' keys passed to the user query. 91 * 92 * @param string $orderby Alias for the field to order by. 93 * @param \WP_User_Query $query instance of WP_User_Query (passed by reference). 94 * 95 * @return string Value to used in the ORDER clause, if `$orderby` is valid. 96 * @global \wpdb $wpdb WordPress database abstraction object. 97 * 98 * @copyright Base on WP_User_Query:parse_orderby method. 99 * 100 */ 101 protected function userParseOrderby( $orderby, $query ) { 102 global $wpdb; 88 103 89 /** 90 * Parses and sanitizes 'orderby' keys passed to the user query. 91 * @copyright Base on WP_User_Query:parse_orderby method. 92 * 93 * @global wpdb $wpdb WordPress database abstraction object. 94 * 95 * @param string $orderby Alias for the field to order by. 96 * @param \WP_User_Query $query instance of WP_User_Query (passed by reference). 97 * 98 * @return string Value to used in the ORDER clause, if `$orderby` is valid. 99 */ 100 protected function userParseOrderby($orderby, $query) 101 { 102 global $wpdb; 104 $meta_query_clauses = $this->Queries->metaQuery->get_clauses(); 103 105 104 $meta_query_clauses = $this->Queries->metaQuery->get_clauses(); 106 $primary_meta_key = ''; 107 $primary_meta_query = false; 108 if ( ! empty( $meta_query_clauses ) ) { 109 $primary_meta_query = $meta_query_clauses[ $orderby ] ?? reset( $meta_query_clauses ); 105 110 106 $primary_meta_key = ''; 107 $primary_meta_query = false;108 if (!empty($meta_query_clauses)) { 109 $primary_meta_query = isset($meta_query_clauses[$orderby]) ? $meta_query_clauses[$orderby] : reset($meta_query_clauses); 111 if ( ! empty( $primary_meta_query['key'] ) ) { 112 $primary_meta_key = $primary_meta_query['key']; 113 } 114 } 110 115 111 if (!empty($primary_meta_query['key'])) { 112 $primary_meta_key = $primary_meta_query['key']; 113 } 114 } 115 116 $_orderby = ''; 117 if (in_array($orderby, array('login', 'nicename', 'email', 'url', 'registered'), true)) { 118 $_orderby = 'user_' . $orderby; 119 } elseif (in_array($orderby, array('user_login', 'user_nicename', 'user_email', 'user_url', 'user_registered'), true)) { 120 $_orderby = $orderby; 121 } elseif ('name' === $orderby || 'display_name' === $orderby) { 122 $_orderby = 'display_name'; 123 } elseif ('post_count' === $orderby) { 124 // @todo Avoid the JOIN. 125 $where = get_posts_by_author_sql('post'); 126 $this->Queries->query_from .= " LEFT OUTER JOIN ( 116 $_orderby = ''; 117 if ( in_array( $orderby, array( 'login', 'nicename', 'email', 'url', 'registered' ), true ) ) { 118 $_orderby = 'user_' . $orderby; 119 } elseif ( in_array( $orderby, array( 120 'user_login', 121 'user_nicename', 122 'user_email', 123 'user_url', 124 'user_registered' 125 ), true ) ) { 126 $_orderby = $orderby; 127 } elseif ( 'name' === $orderby || 'display_name' === $orderby ) { 128 $_orderby = 'display_name'; 129 } elseif ( 'post_count' === $orderby ) { 130 // @todo Avoid the JOIN. 131 $where = get_posts_by_author_sql( 'post' ); 132 $this->Queries->query_from .= " LEFT OUTER JOIN ( 127 133 SELECT post_author, COUNT(*) as post_count 128 134 FROM $wpdb->posts … … 131 137 ) p ON ({$wpdb->users}.ID = p.post_author) 132 138 "; 133 $_orderby= 'post_count';134 } elseif ('ID' === $orderby || 'id' === $orderby) {135 $_orderby = 'ID';136 } elseif ('meta_value' === $orderby || $query->get('meta_key') == $orderby) {137 // $_orderby = "$wpdb->usermeta.meta_value";138 if (!empty($primary_meta_query['type'])) {139 $_orderby = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})";140 } else {141 $_orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}";142 }143 } elseif ('meta_value_num' === $orderby) {144 // $_orderby = "$wpdb->usermeta.meta_value+0";145 $_orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}+0";146 } elseif ('include' === $orderby && !empty($this->Queries->queryVars['include'])) {147 $include = wp_parse_id_list($this->Queries->queryVars['include']);148 $include_sql = implode(',', $include);149 $_orderby = "FIELD( $wpdb->users.ID, $include_sql )";150 } elseif ('nicename__in' === $orderby) {151 $sanitized_nicename__in = array_map('esc_sql', $this->Queries->queryVars['nicename__in']);152 $nicename__in = implode("','", $sanitized_nicename__in);153 $_orderby = "FIELD( user_nicename, '$nicename__in' )";154 } elseif ('login__in' === $orderby) {155 $sanitized_login__in = array_map('esc_sql', $this->Queries->queryVars['login__in']);156 $login__in = implode("','", $sanitized_login__in);157 $_orderby = "FIELD( user_login, '$login__in' )";158 } elseif (isset($meta_query_clauses[$orderby])) {159 $meta_clause = $meta_query_clauses[$orderby];160 // $_orderby = sprintf('CAST(%s.meta_value AS %s)', esc_sql($meta_clause['alias']), esc_sql($meta_clause['cast']));161 $_orderby = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})";162 }139 $_orderby = 'post_count'; 140 } elseif ( 'ID' === $orderby || 'id' === $orderby ) { 141 $_orderby = 'ID'; 142 } elseif ( 'meta_value' === $orderby || $query->get( 'meta_key' ) == $orderby ) { 143 // $_orderby = "$wpdb->usermeta.meta_value"; 144 if ( ! empty( $primary_meta_query['type'] ) ) { 145 $_orderby = "CAST({$primary_meta_query['alias']}.{$primary_meta_key} AS {$primary_meta_query['cast']})"; 146 } else { 147 $_orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}"; 148 } 149 } elseif ( 'meta_value_num' === $orderby ) { 150 // $_orderby = "$wpdb->usermeta.meta_value+0"; 151 $_orderby = "{$primary_meta_query['alias']}.{$primary_meta_key}+0"; 152 } elseif ( 'include' === $orderby && ! empty( $this->Queries->queryVars['include'] ) ) { 153 $include = wp_parse_id_list( $this->Queries->queryVars['include'] ); 154 $include_sql = implode( ',', $include ); 155 $_orderby = "FIELD( $wpdb->users.ID, $include_sql )"; 156 } elseif ( 'nicename__in' === $orderby ) { 157 $sanitized_nicename__in = array_map( 'esc_sql', $this->Queries->queryVars['nicename__in'] ); 158 $nicename__in = implode( "','", $sanitized_nicename__in ); 159 $_orderby = "FIELD( user_nicename, '$nicename__in' )"; 160 } elseif ( 'login__in' === $orderby ) { 161 $sanitized_login__in = array_map( 'esc_sql', $this->Queries->queryVars['login__in'] ); 162 $login__in = implode( "','", $sanitized_login__in ); 163 $_orderby = "FIELD( user_login, '$login__in' )"; 164 } elseif ( isset( $meta_query_clauses[ $orderby ] ) ) { 165 $meta_clause = $meta_query_clauses[ $orderby ]; 166 // $_orderby = sprintf('CAST(%s.meta_value AS %s)', esc_sql($meta_clause['alias']), esc_sql($meta_clause['cast'])); 167 $_orderby = "CAST({$meta_clause['alias']}.{$primary_meta_key} AS {$meta_clause['cast']})"; 168 } 163 169 164 return $_orderby;165 }170 return $_orderby; 171 } 166 172 167 /**168 * Returns an instance of class169 * @return UserQueries 170 */ 171 static function getInstance($Queries) 172 {173 if (self::$instance == null)174 self::$instance = new UserQueries($Queries);173 /** 174 * Returns an instance of class 175 * 176 * @return UserQueries 177 */ 178 static function getInstance( $Queries ) { 179 if ( self::$instance == null ) 180 self::$instance = new UserQueries( $Queries ); 175 181 176 return self::$instance;177 }182 return self::$instance; 183 } 178 184 } -
meta-optimizer/trunk/readme.txt
r2802191 r2960316 4 4 Tags: Post Meta, User Meta, Comment Meta, Term Meta, Meta, Optimizer 5 5 Requires at least: 5.0 6 Tested up to: 6. 0.27 Stable tag: 1. 06 Tested up to: 6.3.1 7 Stable tag: 1.1 8 8 Requires PHP: 7.0 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 11 12 You can use Meta Optimizer to make your WordPress website load faster if you use meta information , for example Post/Comment/User/Term metas.12 You can use Meta Optimizer to make your WordPress website load faster if you use meta information. For example, Post / Comment / User / Term metas. 13 13 14 14 == Description == 15 15 16 WordPress saves every post /comment/user/term meta in new row. with this pluginyou can save all of them to single row, and each column will be a meta key.16 WordPress saves every post / comment / user / term meta in new row. with this plugin, you can save all of them to single row, and each column will be a meta key. 17 17 18 Plugin work with default WordPress functions and support all ofplugins use WordPress standard functions and hooks.18 Plugin work with default WordPress functions and support all plugins use WordPress standard functions and hooks. 19 19 20 20 ## Features 21 - Create database tables for each of WordPress meta tables (Post /Comment/User/Meta).21 - Create database tables for each of WordPress meta tables (Post / Comment / User / Meta). 22 22 - Support WordPress Queries 23 23 - Faster Queries & Easy Export … … 30 30 - [Meta Box – WordPress Custom Fields Framework](https://wordpress.org/plugins/meta-box/) and Pro version 31 31 - [CMB2](https://wordpress.org/plugins/cmb2/) 32 - And all ofplugins and themes use WordPress standard functions and hooks.32 - And all plugins and themes use WordPress standard functions and hooks. 33 33 34 34 ## Attention 35 If you use reserved column keys such as `post_id` for post meta, the plugin adds a suffix to the meta key. It creates a column based on the renamed key. As an example, if you save meta with key `post_id`, then plugin adds `_wpmork` suffix and creates column `post_id_wpmork`. In response to a query (WP_Query), the plugin automatically changes the meta key if necessary.35 If you use reserved column keys such as `post_id` for post-meta, the plugin adds a suffix to the meta key. It creates a column based on the renamed key. As an example, if you save meta with key `post_id`, then plugin adds `_wpmork` suffix and creates column `post_id_wpmork`. In response to a query (WP_Query), the plugin automatically changes the meta key if necessary. 36 36 37 37 [Update post meta](https://developer.wordpress.org/reference/functions/update_post_meta/) example … … 83 83 = Can I use this plugin for custom post types? = 84 84 85 Yes, of course. Even though the plugin supports the built-in types of post and page, it is well suited to storing meta data for custom post types.85 Yes, of course. Even though the plugin supports the built-in types of post and page, it is well suited to storing metadata for custom post types. 86 86 87 87 = Can I rename meta key in DB tables? = … … 98 98 == Changelog == 99 99 100 = 1.1 = 101 * Fix some bugs effected on save array meta value 102 100 103 = 1.0 = 101 104 * Release first version of plugin
Note: See TracChangeset
for help on using the changeset viewer.