Plugin Directory

Changeset 2960316


Ignore:
Timestamp:
08/29/2023 11:37:08 PM (2 years ago)
Author:
parselearn
Message:

Update to version 1.1

Location:
meta-optimizer/trunk
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • meta-optimizer/trunk/WPMetaOptimizer.php

    r2802191 r2960316  
    11<?php
    22
    3 /**
     3/*!
    44 * Plugin Name: Meta Optimizer
    5  * Version: 1.0
     5 * Version: 1.1
    66 * Plugin URI: https://parsakafi.github.io/wp-meta-optimizer
    77 * 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.
     
    1414
    1515// Check run from WP
    16 defined('ABSPATH') || die();
     16defined( 'ABSPATH' ) || die();
    1717
    1818require_once __DIR__ . '/inc/Base.php';
     
    2929require_once __DIR__ . '/inc/Integration.php';
    3030
    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);
     31define( 'WPMETAOPTIMIZER_PLUGIN_KEY', 'wp-meta-optimizer' );
     32define( 'WPMETAOPTIMIZER_OPTION_KEY', 'wp_meta_optimizer' );
     33define( 'WPMETAOPTIMIZER_PLUGIN_NAME', 'Meta Optimizer' );
     34define( 'WPMETAOPTIMIZER_PLUGIN_FILE_PATH', __FILE__ );
     35define( 'WPMETAOPTIMIZER_CACHE_EXPIRE', 30 );
    3636
    3737/**
    3838 * Main class run Meta Optimizer plugin
    3939 */
    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     }
     40class 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    }
    514517}
    515518
    516519WPMetaOptimizer::getInstance();
    517 register_activation_hook(__FILE__, array('WPMetaOptimizer\Install', 'install'));
     520register_activation_hook( __FILE__, array( 'WPMetaOptimizer\Install', 'install' ) );
  • meta-optimizer/trunk/assets/wpmo.js

    r2802191 r2960316  
    1 
    21jQuery(function ($) {
    32    $('.tooltip-title').hover(function (e) { // Hover event
     
    6665
    6766        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')))
    6968                return;
    7069
     
    102101
    103102        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))
    105104                return;
    106105
  • meta-optimizer/trunk/inc/Actions.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
    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
     6defined( 'ABSPATH' ) || die();
     7
     8class 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    }
    333327}
  • meta-optimizer/trunk/inc/Base.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
    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
     6defined( 'ABSPATH' ) || die();
    347
    35     function __construct()
    36     {
    37         global $wpdb;
     8class 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';
    3853
    39         $this->now = current_time('mysql');
     54    function __construct() {
     55        global $wpdb;
    4056
    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' );
    4758
    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        );
    5465
    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    }
    7896}
  • meta-optimizer/trunk/inc/CommentQueries.php

    r2802191 r2960316  
    22
    33namespace WPMetaOptimizer;
     4
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
    47
    58/**
    69 * Comment API: CommentQueries class.
    710 *
    8  * @package WPMetaOptimizer
     11 * @package    WPMetaOptimizer
    912 * @subpackage Comments
    10  * @since 1.0
     13 * @since      1.0
    1114 */
    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     }
     15class 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    }
    224229}
  • meta-optimizer/trunk/inc/Helpers.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
     7
    58use 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
     10class 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    }
    700715}
  • meta-optimizer/trunk/inc/Install.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
    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
     6defined( 'ABSPATH' ) || die();
    177
    18         if (!function_exists('dbDelta'))
    19             require_once(ABSPATH . str_replace('/', DIRECTORY_SEPARATOR, '/wp-admin/includes/upgrade.php'));
     8class 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;
    2018
    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' ) );
    2721
    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}` (
    3132                  `meta_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    3233                  `{$type}_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
     
    3738                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
    3839
    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        }
    4344
    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' => 1
    67             );
     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            );
    6869
    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' );
    7173
    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    }
    7787}
  • meta-optimizer/trunk/inc/Integration.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
    5 class Integration extends Base
    6 {
    7     public static $instance = null;
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
    87
    9     function __construct()
    10     {
    11         add_filter('acf/pre_load_metadata', [$this, 'acfGetMeta'], 10, 4);
    12     }
     8class Integration extends Base {
     9    public static $instance = null;
    1310
    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    }
    2714
    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
    3228
    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'];
    3533
    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 ? '_' : '';
    3936
    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
    4440
    45         return $check; // null
    46     }
     41        if ( $type !== 'option' ) {
     42            $metaValue = get_metadata( $type, $id, "{$prefix}{$name}", true );
    4743
    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        }
    5646
    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    }
    5961}
  • meta-optimizer/trunk/inc/MetaQuery.php

    r2802191 r2960316  
    22
    33namespace WPMetaOptimizer;
     4
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
    47
    58/**
    69 * Meta API: MetaQuery class
    7  * @copyright This class base on code from WordPress Meta Query class (WP_Meta_Query).
    810 *
    9  * @package WPMetaOptimizer
     11 * @copyright  This class base on code from WordPress Meta Query class (WP_Meta_Query).
     12 *
     13 * @package    WPMetaOptimizer
    1014 * @subpackage Meta
    11  * @since 4.4.0
     15 * @since      4.4.0
    1216 */
    1317
     
    2428 * @since 1.0
    2529 */
    26 class MetaQuery
    27 {
     30class MetaQuery {
    2831    /**
    2932     * Helpers class.
     
    111114     * Constructor.
    112115     *
    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:
    130129     *                                            - '='
    131130     *                                            - '!='
     
    140139     *                                            - 'NOT EXISTS' (alias of '!=')
    141140     *                                            Default is 'IN' when `$key` is an array, '=' otherwise.
    142      *         @type string          $type_key    MySQL data type that the meta_key column will be CAST to for
     141     * @type string          $type_key            MySQL data type that the meta_key column will be CAST to for
    143142     *                                            comparisons. Accepts 'BINARY' for case-sensitive regular expression
    144143     *                                            comparisons. Default is ''.
    145      *         @type string|string[] $value       Meta value or values to filter by.
    146      *         @type string          $compare     MySQL 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:
    147146     *                                            - '=',
    148147     *                                            - '!='
     
    163162     *                                            - 'NOT EXISTS'
    164163     *                                            Default is 'IN' when `$value` is an array, '=' otherwise.
    165      *         @type string          $type        MySQL data type that the meta_value column will be CAST to for
     164     * @type string          $type                MySQL data type that the meta_value column will be CAST to for
    166165     *                                            comparisons. Accepts:
    167166     *                                            - 'NUMERIC'
     
    175174     *                                            - 'UNSIGNED'
    176175     *                                            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 )
    183188            $this->Helpers = $helpers;
    184189        else
    185190            $this->Helpers = Helpers::getInstance();
    186191
    187         if (!$meta_query)
     192        if ( ! $meta_query )
    188193            return;
    189194
    190         if (isset($meta_query['relation']) && 'OR' === strtoupper($meta_query['relation'])) {
     195        if ( isset( $meta_query['relation'] ) && 'OR' === strtoupper( $meta_query['relation'] ) ) {
    191196            $this->relation = 'OR';
    192197        } else {
     
    194199        }
    195200
    196         $this->queries = $this->sanitize_query($meta_query);
     201        $this->queries = $this->sanitize_query( $meta_query );
    197202    }
    198203
     
    202207     * Eliminates empty items and ensures that a 'relation' is set.
    203208     *
    204      * @since 4.1.0
    205      *
    206209     * @param array $queries Array of query clauses.
     210     *
    207211     * @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 ) {
    211216        $clean_queries = array();
    212217
    213         if (!is_array($queries)) {
     218        if ( ! is_array( $queries ) ) {
    214219            return $clean_queries;
    215220        }
    216221
    217         foreach ($queries as $key => $query) {
    218             if ('relation' === $key) {
     222        foreach ( $queries as $key => $query ) {
     223            if ( 'relation' === $key ) {
    219224                $relation = $query;
    220             } elseif (!is_array($query)) {
     225            } elseif ( ! is_array( $query ) ) {
    221226                continue;
    222227
    223228                // 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'] );
    227232                }
    228233
    229                 $clean_queries[$key] = $query;
     234                $clean_queries[ $key ] = $query;
    230235
    231236                // Otherwise, it's a nested query, so we recurse.
    232237            } 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;
    237242                }
    238243            }
    239244        }
    240245
    241         if (empty($clean_queries)) {
     246        if ( empty( $clean_queries ) ) {
    242247            return $clean_queries;
    243248        }
    244249
    245250        // Sanitize the 'relation' key provided in the query.
    246         if (isset($relation) && 'OR' === strtoupper($relation)) {
     251        if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) {
    247252            $clean_queries['relation'] = 'OR';
    248253            $this->has_or_relation     = true;
     
    253258            * simplifies the logic around combining key-only queries.
    254259            */
    255         } elseif (1 === count($clean_queries)) {
     260        } elseif ( 1 === count( $clean_queries ) ) {
    256261            $clean_queries['relation'] = 'OR';
    257262
     
    270275     * a 'value' array key.
    271276     *
    272      * @since 4.1.0
    273      *
    274277     * @param array $query Meta query arguments.
     278     *
    275279     * @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'] );
    280285    }
    281286
     
    283288     * Constructs a meta query based on 'meta_*' query vars
    284289     *
     290     * @param array $qv The query variables
     291     *
    285292     * @since 3.2.0
    286293     *
    287      * @param array $qv The query variables
    288      */
    289     public function parse_query_vars($qv)
    290     {
     294     */
     295    public function parse_query_vars( $qv ) {
    291296        $meta_query = array();
    292297
     
    298303         */
    299304        $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"];
    303308            }
    304309        }
    305310
    306311        // 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'] ) ) {
    308313            $primary_meta_query['value'] = $qv['meta_value'];
    309314        }
    310315
    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 ) ) {
    314319            $meta_query = array(
    315320                'relation' => 'AND',
     
    317322                $existing_meta_query,
    318323            );
    319         } elseif (!empty($primary_meta_query)) {
     324        } elseif ( ! empty( $primary_meta_query ) ) {
    320325            $meta_query = array(
    321326                $primary_meta_query,
    322327            );
    323         } elseif (!empty($existing_meta_query)) {
     328        } elseif ( ! empty( $existing_meta_query ) ) {
    324329            $meta_query = $existing_meta_query;
    325330        }
    326331
    327         $this->__construct($meta_query);
     332        $this->__construct( $meta_query );
    328333    }
    329334
     
    331336     * Return the appropriate alias for the given meta type if applicable.
    332337     *
     338     * @param string $type MySQL type to cast meta_value.
     339     *
     340     * @return string MySQL type.
    333341     * @since 3.7.0
    334342     *
    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 ) ) {
    341346            return 'CHAR';
    342347        }
    343348
    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 ) ) {
    347352            return 'CHAR';
    348353        }
    349354
    350         if ('NUMERIC' === $meta_type) {
     355        if ( 'NUMERIC' === $meta_type ) {
    351356            $meta_type = 'SIGNED';
    352357        }
     
    357362    /**
    358363     * Generates SQL clauses to be appended to a main query.
    359      *
    360      * @since 3.2.0
    361364     *
    362365     * @param string $type              Type of meta. Possible values include but are not limited
     
    366369     * @param object $context           Optional. The main query object that corresponds to the type, for
    367370     *                                  example a `WP_Query`, `WP_User_Query`, or `WP_Site_Query`.
     371     *
    368372     * @return string[]|false {
    369373     *     Array containing JOIN and WHERE SQL clauses to append to the main query,
    370374     *     or false if no table exists for the requested meta type.
    371375     *
    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 )
    380385            return false;
    381386
     
    383388
    384389        $this->meta_table     = $meta_table;
    385         $this->meta_id_column = sanitize_key($type . '_id');
     390        $this->meta_id_column = sanitize_key( $type . '_id' );
    386391
    387392        $this->primary_table     = $primary_table;
     
    394399         * be LEFT. Otherwise posts with no metadata will be excluded from results.
    395400         */
    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'] );
    398403        }
    399404
    400405        /**
    401406         * Filters the meta query's generated SQL.
    402          *
    403          * @since 3.1.0
    404407         *
    405408         * @param string[] $sql               Array containing the query's JOIN and WHERE clauses.
     
    411414         * @param object   $context           The main query object that corresponds to the type, for
    412415         *                                    example a `WP_Query`, `WP_User_Query`, or `WP_Site_Query`.
     416         *
     417         * @since 3.1.0
     418         *
    413419         */
    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        ) );
    415428    }
    416429
     
    421434     * out to maintain parity with the other Query classes.
    422435     *
    423      * @since 4.1.0
    424      *
    425436     * @return string[] {
    426437     *     Array containing JOIN and WHERE SQL clauses to append to the main query.
    427438     *
    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() {
    434446        /*
    435447         * $queries are passed by reference to get_sql_for_query() for recursion.
     
    437449         */
    438450        $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'] ) ) {
    442454            $sql['where'] = ' AND ' . $sql['where'];
    443455        }
     
    451463     * If nested subqueries are found, this method recurses the tree to
    452464     * produce the properly nested SQL.
    453      *
    454      * @since 4.1.0
    455465     *
    456466     * @param array $query Query to parse (passed by reference).
    457467     * @param int   $depth Optional. Number of tree levels deep we currently are.
    458468     *                     Used to calculate indentation. Default 0.
     469     *
    459470     * @return string[] {
    460471     *     Array containing JOIN and WHERE SQL clauses to append to a single query array.
    461472     *
    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 ) {
    468480        $sql_chunks = array(
    469481            'join'  => array(),
     
    477489
    478490        $indent = '';
    479         for ($i = 0; $i < $depth; $i++) {
     491        for ( $i = 0; $i < $depth; $i ++ ) {
    480492            $indent .= '  ';
    481493        }
    482494
    483         foreach ($query as $key => &$clause) {
    484             if ('relation' === $key) {
     495        foreach ( $query as $key => &$clause ) {
     496            if ( 'relation' === $key ) {
    485497                $relation = $query['relation'];
    486             } elseif (is_array($clause)) {
     498            } elseif ( is_array( $clause ) ) {
    487499
    488500                // 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 ) {
    494506                        $sql_chunks['where'][] = '';
    495                     } elseif (1 === $where_count) {
     507                    } elseif ( 1 === $where_count ) {
    496508                        $sql_chunks['where'][] = $clause_sql['where'][0];
    497509                    } else {
    498                         $sql_chunks['where'][] = '( ' . implode(' AND ', $clause_sql['where']) . ' )';
     510                        $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )';
    499511                    }
    500512
    501                     $sql_chunks['join'] = array_merge($sql_chunks['join'], $clause_sql['join']);
     513                    $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] );
    502514                    // This is a subquery, so we recurse.
    503515                } else {
    504                     $clause_sql = $this->get_sql_for_query($clause, $depth + 1);
     516                    $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 );
    505517
    506518                    $sql_chunks['where'][] = $clause_sql['where'];
     
    511523
    512524        // 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 ) ) {
    517529            $relation = 'AND';
    518530        }
    519531
    520532        // 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'] ) );
    523535        }
    524536
    525537        // 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 . ')';
    528540        }
    529541
     
    535547     *
    536548     * "First-order" means that it's an array with a 'key' or 'value'.
    537      *
    538      * @since 4.1.0
    539      *
    540      * @global wpdb $wpdb WordPress database abstraction object.
    541549     *
    542550     * @param array  $clause       Query clause (passed by reference).
     
    544552     * @param string $clause_key   Optional. The array key used to name the clause in the original `$meta_query`
    545553     *                             parameters. If not provided, a key will be generated automatically.
     554     *
    546555     * @return array {
    547556     *     Array containing JOIN and WHERE SQL clauses to append to a first-order query.
    548557     *
    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 = '' ) {
    555567        global $wpdb;
    556568
     
    560572        );
    561573
    562         if (isset($clause['compare'])) {
    563             $clause['compare'] = strtoupper($clause['compare']);
     574        if ( isset( $clause['compare'] ) ) {
     575            $clause['compare'] = strtoupper( $clause['compare'] );
    564576        } else {
    565             $clause['compare'] = isset($clause['value']) && is_array($clause['value']) ? 'IN' : '=';
     577            $clause['compare'] = isset( $clause['value'] ) && is_array( $clause['value'] ) ? 'IN' : '=';
    566578        }
    567579
     
    589601        );
    590602
    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 ) ) {
    592604            $clause['compare'] = '=';
    593605        }
    594606
    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'] );
    597609        } 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 ) ) {
    602614            $clause['compare_key'] = '=';
    603615        }
     
    610622
    611623        // 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 );
    614626
    615627        // TODO: Disabled, since duplicate joins are not needed
    616         if (!$i && false === $alias) {
     628        if ( ! $i && false === $alias ) {
    617629            $alias = $i ? 'mt' . $i : $this->meta_table;
    618630
    619631            // JOIN clauses for NOT EXISTS have their own syntax.
    620             if ('NOT EXISTS' === $meta_compare) {
     632            if ( 'NOT EXISTS' === $meta_compare ) {
    621633                $join .= " INNER JOIN $this->meta_table";
    622634                $join .= $i ? " AS $alias" : '';
     
    639651            $this->table_aliases[] = $alias;
    640652            $sql_chunks['join'][]  = $join;
    641         }else{
     653        } else {
    642654            $alias = $this->meta_table;
    643655        }
     
    647659
    648660        // 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 );
    651663        $clause['cast'] = $meta_type;
    652664
    653665        // 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 ) {
    655667            $clause_key = $clause['alias'];
    656668        }
     
    659671        $iterator        = 1;
    660672        $clause_key_base = $clause_key;
    661         while (isset($this->clauses[$clause_key])) {
     673        while ( isset( $this->clauses[ $clause_key ] ) ) {
    662674            $clause_key = $clause_key_base . '-' . $iterator;
    663             $iterator++;
     675            $iterator ++;
    664676        }
    665677
    666678        // Store the clause in our flat array.
    667         $this->clauses[$clause_key] = &$clause;
     679        $this->clauses[ $clause_key ] = &$clause;
    668680
    669681        // Next, build the WHERE clause.
    670682
    671683        // 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 ) {
    674686                $sql_chunks['where'][] = $alias . '.' . $clause['key'] . ' IS NULL';
    675687            } else { // TODO: Column name query
     
    680692                 * nested clause.
    681693                 */
    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 ) ) {
    683701                    // Negative clauses may be reused.
    684                     $i                     = count($this->table_aliases);
     702                    $i                     = count( $this->table_aliases );
    685703                    $subquery_alias        = $i ? 'mt' . $i : $this->meta_table;
    686704                    $this->table_aliases[] = $subquery_alias;
    687705
    688                     $meta_compare_string_start  = 'NOT EXISTS (';
     706                    $meta_compare_string_start = 'NOT EXISTS (';
    689707                    $meta_compare_string_start .= "SELECT 1 FROM $wpdb->postmeta $subquery_alias ";
    690708                    $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';
    692710                    $meta_compare_string_end   .= ')';
    693711                }
    694712
    695                 switch ($meta_compare_key) {
     713                switch ( $meta_compare_key ) {
    696714                    case '=':
    697715                    case 'EXISTS':
     
    699717                        break;
    700718                    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.InterpolatedNotPrepared
     719                        $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
    703721                        break;
    704722                    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.NotPrepared
     723                        $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
    707725                        break;
    708726                    case 'RLIKE':
    709727                    case 'REGEXP':
    710728                        $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'] ) ) {
    712730                            $cast = 'BINARY';
    713731                        } else {
    714732                            $cast = '';
    715733                        }
    716                         $where = $wpdb->prepare("$alias.meta_key $operator $cast %s", trim($clause['key'])); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     734                        $where = $wpdb->prepare( "$alias.meta_key $operator $cast %s", trim( $clause['key'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    717735                        break;
    718736
     
    720738                    case 'NOT EXISTS':
    721739                        $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.NotPrepared
     740                        $where               = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    723741                        break;
    724742                    case 'NOT LIKE':
    725743                        $meta_compare_string = $meta_compare_string_start . "AND $subquery_alias.meta_key LIKE %s " . $meta_compare_string_end;
    726744
    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.NotPrepared
     745                        $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
    729747                        break;
    730748                    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 ) . ') ';
    732750                        $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.NotPrepared
     751                        $where               = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    734752                        break;
    735753                    case 'NOT REGEXP':
    736754                        $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'] ) ) {
    738756                            $cast = 'BINARY';
    739757                        } else {
     
    742760
    743761                        $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.NotPrepared
     762                        $where               = $wpdb->prepare( $meta_compare_string, $clause['key'] ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    745763                        break;
    746764                }
     
    751769
    752770        // meta_value.
    753         if (array_key_exists('value', $clause)) {
     771        if ( array_key_exists( 'value', $clause ) ) {
    754772            $meta_value = $clause['value'];
    755773
    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 );
    759777                }
    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 ) {
    765783                case 'IN':
    766784                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 );
    769787                    break;
    770788
    771789                case 'BETWEEN':
    772790                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] );
    774792                    break;
    775793
    776794                case 'LIKE':
    777795                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 );
    780798                    break;
    781799
    782                     // EXISTS with a value is interpreted as '='.
     800                // EXISTS with a value is interpreted as '='.
    783801                case 'EXISTS':
    784802                    $meta_compare = '=';
    785                     $where        = $wpdb->prepare('%s', $meta_value);
     803                    $where        = $wpdb->prepare( '%s', $meta_value );
    786804                    break;
    787805
    788                     // 'value' is ignored for NOT EXISTS.
     806                // 'value' is ignored for NOT EXISTS.
    789807                case 'NOT EXISTS':
    790808                    $where = '';
     
    792810
    793811                default:
    794                     $where = $wpdb->prepare('%s', $meta_value);
     812                    $where = $wpdb->prepare( '%s', $meta_value );
    795813                    break;
    796814            }
    797815
    798             if ($where) {
    799                 if ('CHAR' === $meta_type) {
     816            if ( $where ) {
     817                if ( 'CHAR' === $meta_type ) {
    800818                    $sql_chunks['where'][] = "$alias.{$clause['key']} {$meta_compare} {$where}";
    801819                } else {
     
    809827         * be joined in parentheses.
    810828         */
    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'] ) . ' )' );
    813831        }
    814832
     
    822840     * a value of 'orderby' corresponding to a meta clause.
    823841     *
     842     * @return array Meta clauses.
    824843     * @since 4.2.0
    825844     *
    826      * @return array Meta clauses.
    827      */
    828     public function get_clauses()
    829     {
     845     */
     846    public function get_clauses() {
    830847        return $this->clauses;
    831848    }
     
    845862     * connected by the relation 'OR'.
    846863     *
    847      * @since 4.1.0
    848      *
    849864     * @param array $clause       Query clause.
    850865     * @param array $parent_query Parent query of $clause.
     866     *
    851867     * @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 ) {
    855872        $alias = false;
    856873
    857         foreach ($parent_query as $sibling) {
     874        foreach ( $parent_query as $sibling ) {
    858875            // If the sibling has no alias yet, there's nothing to check.
    859             if (empty($sibling['alias'])) {
     876            if ( empty( $sibling['alias'] ) ) {
    860877                continue;
    861878            }
    862879
    863880            // 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 ) ) {
    865882                continue;
    866883            }
     
    869886
    870887            // 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', '>', '>=', '<', '<=' );
    873890
    874891                // 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'] );
    883900                break;
    884901            }
     
    888905         * Filters the table alias identified as compatible with the current clause.
    889906         *
     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         *
    890912         * @since 4.1.0
    891913         *
    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.
    896914         */
    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 );
    898916    }
    899917
     
    905923     * method can be used in these cases to determine whether such a clause is necessary.
    906924     *
     925     * @return bool True if the query contains any `OR` relations, otherwise false.
    907926     * @since 4.3.0
    908927     *
    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() {
    913930        return $this->has_or_relation;
    914931    }
  • meta-optimizer/trunk/inc/Options.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
    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
     6defined( 'ABSPATH' ) || die();
     7
     8class 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        ?>
    107114        <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' => [] ) ); ?>
    110119
    111120            <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>
    115127            </div>
    116128
    117129            <div id="tables-tab-content" class="wpmo-tab-content <?php echo $currentTab != 'tables' ? 'hidden' : '' ?>">
    118                 <?php
    119                 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>
    124136                    <p>
    125                         <?php
    126                         _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                        ?>
    131143                    </p>
    132144
    133145                    <table class="wp-list-table widefat fixed striped table-view-list table-sticky-head">
    134146                        <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>
    143157                        </thead>
    144158                        <tbody>
    145                             <?php
    146                             $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                             else
    187                                 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                        ?>
    189203                        </tbody>
    190204                    </table>
    191205                    <br>
    192                 <?php
    193                 }
    194                 ?>
     206                    <?php
     207                }
     208                ?>
    195209            </div>
    196210
    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' : '' ?>">
    198213                <form action="" method="post">
    199214                    <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 ); ?>
    201216                    <table>
    202217                        <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> &nbsp;';
     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                            ?>
    203328                            <tr>
    204                                 <th><?php _e('Support WordPress Query', 'meta-optimizer') ?></th>
     329                                <td><?php echo esc_html( $table['title'] ) ?></td>
    205330                                <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>
    210341                                </td>
    211342                            </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> &nbsp;';
    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>
    308350                        </tbody>
    309351                    </table>
     
    314356                <form action="" method="post">
    315357                    <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 ); ?>
    317359                    <table>
    318360                        <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>
    399457                        </tbody>
    400458                    </table>
     
    402460            </div>
    403461        </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    }
    471530}
  • meta-optimizer/trunk/inc/PostQueries.php

    r2802191 r2960316  
    22
    33namespace WPMetaOptimizer;
     4
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
    47
    58/**
    69 * Query API: PostQueries class.
    710 *
    8  * @package WPMetaOptimizer
     11 * @package    WPMetaOptimizer
    912 * @subpackage Query
    10  * @since 1.0
     13 * @since      1.0
    1114 */
    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     }
     15class 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    }
    250255}
  • meta-optimizer/trunk/inc/Queries.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
    5 use WP_Query;
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
    67
    7 class Queries extends Base
    8 {
    9     public static $instance = null;
    10     protected $Helpers;
     8class Queries extends Base {
     9    public static $instance = null;
     10    protected $Helpers;
    1111
    12     /**
    13     * Metadata query container.
    14     *
    15     * @since 3.2.0
    16     * @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;
    1919
    20     /**
    21     * Query vars set by the user.
    22     *
    23     * @since 3.1.0
    24     * @var array
    25     */
    26     public $queryVars;
     20    /**
     21    * Query vars set by the user.
     22    *
     23    * @since 3.1.0
     24    * @var array
     25    */
     26    public $queryVars;
    2727
    28     function __construct()
    29     {
    30         parent::__construct();
     28    function __construct() {
     29        parent::__construct();
    3130
    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 );
    3433
    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 );
    3736
    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    }
    4443
    45     /**
    46     * Change meta SQL of default WordPress meta types
    47     *
    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 limited
    51     *                                    to 'post', 'comment', 'blog', 'term', and 'user'.
    52      * @param string   $primaryTable     Primary table.
    53      * @param string   $primaryIDColumn Primary column ID.
    54     * @param object   $context           The main query object that corresponds to the type, for
    55     *                                    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;
    6261
    63         $this->metaQuery = new MetaQuery(false, $this->Helpers);
     62        $this->metaQuery = new MetaQuery( false, $this->Helpers );
    6463
    65         $this->queryVars = $this->getQueryVars($type, $context->query_vars);
     64        $this->queryVars = $this->getQueryVars( $type, $context->query_vars );
    6665
    67         // Parse meta query.
    68         $this->metaQuery->parse_query_vars($this->queryVars);
     66        // Parse meta query.
     67        $this->metaQuery->parse_query_vars( $this->queryVars );
    6968
    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 );
    7271
    73         return $sql;
    74     }
     72        return $sql;
     73    }
    7574
    76     /**
    77     * Get query variables
    78     *
    79     * @param string $type      Meta type
    80      * @param array $queryVars Current query vars
    81      * @return array
    82      */
    83     public function getQueryVars($type, $queryVars)
    84     {
    85         // Change Meta Key
    86         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'] );
    8887
    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            }
    101101
    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]);
     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 ] );
    110110
    111                 $queryVars['orderby'][$keyIndex] = $order;
    112             }
    113         }
     111                $queryVars['orderby'][ $keyIndex ] = $order;
     112            }
     113        }
    114114
    115         return $queryVars;
    116     }
     115        return $queryVars;
     116    }
    117117
    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        }
    132133
    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    }
    139140
    140     /**
    141     * Returns an instance of class
    142      * @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();
    148149
    149         return self::$instance;
    150     }
     150        return self::$instance;
     151    }
    151152}
  • meta-optimizer/trunk/inc/TermQueries.php

    r2802191 r2960316  
    22
    33namespace WPMetaOptimizer;
     4
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
    47
    58/**
    69 * Taxonomy API: TermQueries class.
    710 *
    8  * @package WPMetaOptimizer
     11 * @package    WPMetaOptimizer
    912 * @subpackage Taxonomy
    10  * @since 1.0
     13 * @since      1.0
    1114 */
    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     }
     15class 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    }
    247258}
  • meta-optimizer/trunk/inc/UserQueries.php

    r2802191 r2960316  
    33namespace WPMetaOptimizer;
    44
     5// Check run from WP
     6defined( 'ABSPATH' ) || die();
     7
    58/**
    69 * User API: UserQueries class.
    710 *
    8  * @package WPMetaOptimizer
     11 * @package    WPMetaOptimizer
    912 * @subpackage Users
    10  * @since 1.0
     13 * @since      1.0
    1114 */
     15class UserQueries {
     16    public static $instance = null;
     17    private $Queries;
    1218
    13 class UserQueries
    14 {
    15     public static $instance = null;
    16     private $Queries;
     19    function __construct( $Queries ) {
     20        $this->Queries = $Queries;
    1721
    18     function __construct($Queries)
    19     {
    20         $this->Queries = $Queries;
     22        add_action( 'pre_user_query', [ $this, 'changeUserQuery' ], 9999 );
     23    }
    2124
    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;
    2436
    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'] );
    3639
    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        }
    3949
    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            }
    4955
    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            }
    5565
    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 );
    6567
    66             $parsed = $this->userParseOrderby($_orderby, $query);
     68            if ( ! $parsed )
     69                continue;
    6770
    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        }
    7077
    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        }
    7782
    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 );
    8284
    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    }
    8488
    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;
    88103
    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();
    103105
    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 );
    105110
    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        }
    110115
    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 (
    127133                SELECT post_author, COUNT(*) as post_count
    128134                FROM $wpdb->posts
     
    131137            ) p ON ({$wpdb->users}.ID = p.post_author)
    132138            ";
    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        }
    163169
    164         return $_orderby;
    165     }
     170        return $_orderby;
     171    }
    166172
    167     /**
    168     * Returns an instance of class
    169      * @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 );
    175181
    176         return self::$instance;
    177     }
     182        return self::$instance;
     183    }
    178184}
  • meta-optimizer/trunk/readme.txt

    r2802191 r2960316  
    44Tags: Post Meta, User Meta, Comment Meta, Term Meta, Meta, Optimizer
    55Requires at least: 5.0
    6 Tested up to: 6.0.2
    7 Stable tag: 1.0
     6Tested up to: 6.3.1
     7Stable tag: 1.1
    88Requires PHP: 7.0
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1111
    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.
     12You can use Meta Optimizer to make your WordPress website load faster if you use meta information. For example, Post / Comment / User / Term metas.
    1313
    1414== Description ==
    1515
    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.
     16WordPress 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.
    1717
    18 Plugin work with default WordPress functions and support all of plugins use WordPress standard functions and hooks.
     18Plugin work with default WordPress functions and support all plugins use WordPress standard functions and hooks.
    1919
    2020## 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).
    2222- Support WordPress Queries
    2323- Faster Queries & Easy Export
     
    3030- [Meta Box – WordPress Custom Fields Framework](https://wordpress.org/plugins/meta-box/) and Pro version
    3131- [CMB2](https://wordpress.org/plugins/cmb2/)
    32 - And all of plugins and themes use WordPress standard functions and hooks.
     32- And all plugins and themes use WordPress standard functions and hooks.
    3333
    3434## 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.
     35If 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.
    3636
    3737[Update post meta](https://developer.wordpress.org/reference/functions/update_post_meta/) example
     
    8383= Can I use this plugin for custom post types? =
    8484
    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.
     85Yes, 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.
    8686
    8787= Can I rename meta key in DB tables? =
     
    9898== Changelog ==
    9999
     100= 1.1 =
     101* Fix some bugs effected on save array meta value
     102
    100103= 1.0 =
    101104* Release first version of plugin
Note: See TracChangeset for help on using the changeset viewer.