<?php

/**
 * Extra Functions
 *
 * Collections of extra functions to avoid repeatition
 *
 * @copyright   Copyright (c) 2016, Jeffrey Carandang
 * @since       4.0
 */

function widgetopts_sanitize_array(&$array)
{
    foreach ($array as &$value) {
        if (!is_array($value)) {
            // sanitize if value is not an array
            $value = sanitize_text_field($value);
        } else {
            // go inside this function again
            widgetopts_sanitize_array($value);
        }
    }

    return $array;
}

function widgetopts_is_checked($array, $key)
{
    return (isset($array[$key]) && '1' == $array[$key]) ? 'checked="checked"' : '';
}

/*
 * Check if http or https available on link
 */
function widgetopts_addhttp($url)
{
    if (!preg_match("~^(?:f|ht)tps?://~i", $url)) {
        $url = "http://" . $url;
    }
    return $url;
}


/**
 * Register Global Variables for easier access
 *
 *
 * @since 5.0
 * @return array
 */
function widgetopts_global_taxonomies()
{
    $taxonomies = get_option('widgetopts_global_taxonomies');

    if (empty($taxonomies)) {

        $tax_args = array(
            'public'   => true
        );
        $tax_output     = 'objects'; // or objects
        $tax_operator   = 'and'; // 'and' or 'or'
        $taxonomies     = get_taxonomies($tax_args, $tax_output, $tax_operator);
        unset($taxonomies['post_format']);

        // Let's let devs alter that value coming in
        $taxonomies = apply_filters('widgetopts_update_global_taxonomies', $taxonomies);
        update_option('widgetopts_global_taxonomies', $taxonomies);
    }

    return apply_filters('widgetopts_get_global_taxonomies', $taxonomies);
}

function widgetopts_global_types()
{
    /*$types = get_option( 'widgetopts_global_types' );
 	if( empty( $types ) ) {
         $types  = get_post_types( array(
                                'public' => true,
                            ), 'object' );
        // Let's let devs alter that value coming in
        $types = apply_filters( 'widgetopts_update_global_types', $types );
        update_option( 'widgetopts_global_types', $types );
 	}*/

    $types = get_post_types(array('public' => true), 'object');
    $types = apply_filters('widgetopts_update_global_types', $types);

    return apply_filters('widgetopts_get_global_types', $types);
}

function widgetopts_global_pages()
{
    $pages = get_option('widgetopts_global_all_pages');

    //old pages object
    // if( empty( $pages ) ) {
    //     $pages  = get_posts( array(
    //                             'post_type'     => 'page',
    //                             'post_status'   => 'publish',
    //                             'numberposts'   => -1,
    //                             'orderby'       => 'title',
    //                             'order'         => 'ASC',
    //                             'fields'        => array('ID', 'name')
    //                         ));
    //
    //     // Let's let devs alter that value coming in
    //     $pages = apply_filters( 'widgetopts_update_global_pages', $pages );
    //     update_option( 'widgetopts_global_pages', $pages );
    // }

    //create new pages object
    if (empty($pages)) {
        global $wpdb;

        $pages  = $wpdb->get_results("SELECT ID, post_title, post_parent FROM $wpdb->posts WHERE post_type = 'page' AND post_status = 'publish' ORDER BY post_title ASC ");

        // Let's let devs alter that value coming in
        $pages = apply_filters('widgetopts_update_global_pages', $pages);
        update_option('widgetopts_global_all_pages', $pages);
    }

    return apply_filters('widgetopts_get_global_pages', $pages);
}

function widgetopts_global_categories()
{
    $categories = get_option('widgetopts_global_categories');

    if (empty($categories)) {
        $categories = get_categories(array(
            'hide_empty'    => false
        ));

        // Let's let devs alter that value coming in
        $categories = apply_filters('widgetopts_update_global_categories', $categories);
        update_option('widgetopts_global_categories', $categories);
    }

    return apply_filters('widgetopts_global_categories', $categories);
}

/*
Page Walker Class
*/
if (!class_exists('WidgetOpts_Pages_Checkboxes')) :
    class WidgetOpts_Pages_Checkboxes extends Walker_Page
    {

        function start_lvl(&$output, $depth = 0, $args = array())
        {
            $output .= "\n<div class='widgetopts-chldrn'>\n";
        }

        function end_lvl(&$output, $depth = 0, $args = array())
        {
            $output .= "</div>\n";
        }

        function start_el(&$output, $page, $depth = 0, $args = array(), $current_page = 0)
        {
            if ($depth) {
                $indent = str_repeat('&mdash; ', $depth);
            } else {
                $indent = '';
            }



            if ('' === $page->post_title) {
                $page->post_title = sprintf(__('#%d (no title)', 'widget-options'), $page->ID);
            }

            $pages_values = array();
            if (isset($args['params']['visibility']['pages'])) {
                $pages_values = $args['params']['visibility']['pages'];
            }

            if (isset($pages_values[$page->ID]) && $pages_values[$page->ID] == '1') {
                $checked = 'checked="checked"';
            } else {
                $checked = '';
            }

            $output .= '<p>' . $indent;

            $output .= '<input type="checkbox" name="' . $args['namespace'] . '[extended_widget_opts][visibility][pages][' . $page->ID . ']" id="' . $args['id'] . '-opts-pages-' . $page->ID . '" value="1" ' . $checked . '/>';

            $output .= '<label for="' . $args['id'] . '-opts-pages-' . $page->ID . '">' . $page->post_title . '</label>';
        }

        function end_el(&$output, $page, $depth = 0, $args = array())
        {
            $output .= "</p>\n";
        }
    }
endif;

function widgetopts_set_http_headers()
{
    $httpheaders = [];
    foreach ($_SERVER as $key => $value) {
        if (substr($key, 0, 5) === 'HTTP_') {
            $httpheaders[$key] = $value;
        }
    }
    return $httpheaders;
}

/**
 * Checks if the request is coming from a mobile device based on HTTP headers.
 *
 * @return bool True if the request is from a mobile device, false otherwise.
 */
function widgetopts_is_mobile()
{
    global $widgetopts_http_headers;

    // Define mobile headers and their corresponding values
    $mobile_headers = [
        'HTTP_ACCEPT' => ['values' => ['application/x-obml2d', 'application/vnd.rim.html', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml']],
        'HTTP_UA_CPU' => ['values' => ['ARM']],
        'HTTP_X_WAP_PROFILE' => null,
        'HTTP_PROFILE' => null,
        'HTTP_X_HUAWEI_USERID' => null,
        'HTTP_X_WAP_CLIENTID' => null,
        'HTTP_WAP_CONNECTION' => null,
        'HTTP_X_OPERAMINI_PHONE_UA' => null,
        'HTTP_X_NOKIA_GATEWAY_ID' => null,
        'HTTP_X_VODAFONE_3GPDPCONTEXT' => null,
        'HTTP_X_ORANGE_ID' => null,
        'HTTP_X_ATT_DEVICEID' => null,
        'HTTP_UA_OS' => null,
        'HTTP_X_MOBILE_GATEWAY' => null,
    ];

    // Check each mobile header for a match
    foreach ($mobile_headers as $key => $value) {
        if (isset($widgetopts_http_headers[$key])) {
            if (isset($value['values']) && is_array($value['values'])) {
                foreach ($value['values'] as $_match) {
                    if (strpos($widgetopts_http_headers[$key], $_match) !== false) {
                        return true; // Found a match, request is from a mobile device
                    }
                }
            } else {
                return true; // Found a match, request is from a mobile device
            }
        }
    }

    // If no match is found, try another method
    return widgetopts_is_mobile_or_tablet();
}

function widgetopts_is_mobile_or_tablet($tablet_only = false)
{
    global $widgetopts_user_agent;
    $user_agent = $widgetopts_user_agent;

    // Check for common mobile and tablet User-Agent patterns
    $mobile_patterns = array(
        '/android/i',
        '/iphone|ipod/i',
        '/blackberry/i',
        '/iemobile/i',
        '/windows phone/i',
        '/opera mini/i',
        '/mobile safari/i'
    );

    $tablet_patterns = array(
        '/ipad/i',
        '/android(?!.*mobile)/i',
        '/kindle|silk/i',
        '/playbook/i',
        '/tablet/i'
    );

    if ($tablet_only === false) {
        // Check if the User-Agent matches any of the mobile patterns
        foreach ($mobile_patterns as $pattern) {
            if (preg_match($pattern, $user_agent)) {
                return true; // Request is from a mobile device
            }
        }
    }

    // Check if the User-Agent matches any of the tablet patterns
    foreach ($tablet_patterns as $pattern) {
        if (preg_match($pattern, $user_agent)) {
            return true; // Request is from a tablet device
        }
    }

    // Additional checks for specific tablet devices or browsers
    if (stripos($user_agent, 'Macintosh') !== false && stripos($user_agent, 'iPad') !== false) {
        return true; // Request is from an iPad
    }

    return false; // Request is neither from a mobile nor tablet device (default to desktop)
}

/**
 * Check if the device is tablet.
 * Returns true if any type of tablet device detected
 * @return bool
 */
function widgetopts_is_tablet()
{
    global $widgetopts_user_agent;

    $tablets = array(
        'NookTablet'        => 'Android.*Nook|NookColor|nook browser|BNRV200|BNRV200A|BNTV250|BNTV250A|BNTV400|BNTV600|LogicPD Zoom2',
        'ToshibaTablet'     => 'Android.*(AT100|AT105|AT200|AT205|AT270|AT275|AT300|AT305|AT1S5|AT500|AT570|AT700|AT830)|TOSHIBA.*FOLIO',
        'GoogleTablet' => 'Android.*Pixel C',
        'NexusTablet' => 'Android.*Nexus[\s]+(7|9|10)',
        'AcerTablet'        => 'Android.*; \b(A100|A101|A110|A200|A210|A211|A500|A501|A510|A511|A700|A701|W500|W500P|W501|W501P|W510|W511|W700|G100|G100W|B1-A71|B1-710|B1-711|A1-810|A1-811|A1-830)\b|W3-810|\bA3-A10\b|\bA3-A11\b|\bA3-A20\b|\bA3-A30|A3-A40',
        'SamsungTablet' => 'SAMSUNG.*Tablet|Galaxy.*Tab|SC-01C|GT-P1000|GT-P1003|GT-P1010|GT-P3105|GT-P6210|GT-P6800|GT-P6810|GT-P7100|GT-P7300|GT-P7310|GT-P7500|GT-P7510|SCH-I800|SCH-I815|SCH-I905|SGH-I957|SGH-I987|SGH-T849|SGH-T859|SGH-T869|SPH-P100|GT-P3100|GT-P3108|GT-P3110|GT-P5100|GT-P5110|GT-P6200|GT-P7320|GT-P7511|GT-N8000|GT-P8510|SGH-I497|SPH-P500|SGH-T779|SCH-I705|SCH-I915|GT-N8013|GT-P3113|GT-P5113|GT-P8110|GT-N8010|GT-N8005|GT-N8020|GT-P1013|GT-P6201|GT-P7501|GT-N5100|GT-N5105|GT-N5110|SHV-E140K|SHV-E140L|SHV-E140S|SHV-E150S|SHV-E230K|SHV-E230L|SHV-E230S|SHW-M180K|SHW-M180L|SHW-M180S|SHW-M180W|SHW-M300W|SHW-M305W|SHW-M380K|SHW-M380S|SHW-M380W|SHW-M430W|SHW-M480K|SHW-M480S|SHW-M480W|SHW-M485W|SHW-M486W|SHW-M500W|GT-I9228|SCH-P739|SCH-I925|GT-I9200|GT-P5200|GT-P5210|GT-P5210X|SM-T311|SM-T310|SM-T310X|SM-T210|SM-T210R|SM-T211|SM-P600|SM-P601|SM-P605|SM-P900|SM-P901|SM-T217|SM-T217A|SM-T217S|SM-P6000|SM-T3100|SGH-I467|XE500|SM-T110|GT-P5220|GT-I9200X|GT-N5110X|GT-N5120|SM-P905|SM-T111|SM-T2105|SM-T315|SM-T320|SM-T320X|SM-T321|SM-T520|SM-T525|SM-T530NU|SM-T230NU|SM-T330NU|SM-T900|XE500T1C|SM-P605V|SM-P905V|SM-T337V|SM-T537V|SM-T707V|SM-T807V|SM-P600X|SM-P900X|SM-T210X|SM-T230|SM-T230X|SM-T325|GT-P7503|SM-T531|SM-T330|SM-T530|SM-T705|SM-T705C|SM-T535|SM-T331|SM-T800|SM-T700|SM-T537|SM-T807|SM-P907A|SM-T337A|SM-T537A|SM-T707A|SM-T807A|SM-T237|SM-T807P|SM-P607T|SM-T217T|SM-T337T|SM-T807T|SM-T116NQ|SM-T116BU|SM-P550|SM-T350|SM-T550|SM-T9000|SM-P9000|SM-T705Y|SM-T805|GT-P3113|SM-T710|SM-T810|SM-T815|SM-T360|SM-T533|SM-T113|SM-T335|SM-T715|SM-T560|SM-T670|SM-T677|SM-T377|SM-T567|SM-T357T|SM-T555|SM-T561|SM-T713|SM-T719|SM-T813|SM-T819|SM-T580|SM-T355Y?|SM-T280|SM-T817A|SM-T820|SM-W700|SM-P580|SM-T587|SM-P350|SM-P555M|SM-P355M|SM-T113NU|SM-T815Y|SM-T585|SM-T285|SM-T825|SM-W708|SM-T835|SM-T830|SM-T837V|SM-T720|SM-T510|SM-T387V|SM-P610|SM-T290|SM-T515|SM-T590|SM-T595|SM-T725|SM-T817P|SM-P585N0|SM-T395|SM-T295|SM-T865|SM-P610N|SM-P615|SM-T970|SM-T380|SM-T5950|SM-T905|SM-T231|SM-T500|SM-T860|SM-T536|SM-T837A|SM-X200|SM-T220|SM-T870|SM-X906C',
        'Kindle'            => 'Kindle|Silk.*Accelerated|Android.*\b(KFOT|KFTT|KFJWI|KFJWA|KFOTE|KFSOWI|KFTHWI|KFTHWA|KFAPWI|KFAPWA|WFJWAE|KFSAWA|KFSAWI|KFASWI|KFARWI|KFFOWI|KFGIWI|KFMEWI)\b|Android.*Silk/[0-9.]+ like Chrome/[0-9.]+ (?!Mobile)',
        'SurfaceTablet'     => 'Windows NT [0-9.]+; ARM;.*(Tablet|ARMBJS)',
        'HPTablet'          => 'HP Slate (7|8|10)|HP ElitePad 900|hp-tablet|EliteBook.*Touch|HP 8|Slate 21|HP SlateBook 10',
        'AsusTablet'        => '^.*PadFone((?!Mobile).)*$|Transformer|TF101|TF101G|TF300T|TF300TG|TF300TL|TF700T|TF700KL|TF701T|TF810C|ME171|ME301T|ME302C|ME371MG|ME370T|ME372MG|ME172V|ME173X|ME400C|Slider SL101|\bK00F\b|\bK00C\b|\bK00E\b|\bK00L\b|TX201LA|ME176C|ME102A|\bM80TA\b|ME372CL|ME560CG|ME372CG|ME302KL| K01A | K010 | K011 | K017 | K01E |ME572C|ME103K|ME170C|ME171C|\bME70C\b|ME581C|ME581CL|ME8510C|ME181C|P01Y|PO1MA|P01Z|\bP027\b|\bP024\b|\bP00C\b',
        'BlackBerryTablet'  => 'PlayBook|RIM Tablet',
        'HTCtablet'         => 'HTC_Flyer_P512|HTC Flyer|HTC Jetstream|HTC-P715a|HTC EVO View 4G|PG41200|PG09410',
        'MotorolaTablet'    => 'xoom|sholest|MZ615|MZ605|MZ505|MZ601|MZ602|MZ603|MZ604|MZ606|MZ607|MZ608|MZ609|MZ615|MZ616|MZ617',
        'LGTablet'          => '\bL-06C|LG-V909|LG-V900|LG-V700|LG-V510|LG-V500|LG-V410|LG-V400|LG-VK810\b',
        'FujitsuTablet'     => 'Android.*\b(F-01D|F-02F|F-05E|F-10D|M532|Q572)\b',
        'PrestigioTablet'   => 'PMP3170B|PMP3270B|PMP3470B|PMP7170B|PMP3370B|PMP3570C|PMP5870C|PMP3670B|PMP5570C|PMP5770D|PMP3970B|PMP3870C|PMP5580C|PMP5880D|PMP5780D|PMP5588C|PMP7280C|PMP7280C3G|PMP7280|PMP7880D|PMP5597D|PMP5597|PMP7100D|PER3464|PER3274|PER3574|PER3884|PER5274|PER5474|PMP5097CPRO|PMP5097|PMP7380D|PMP5297C|PMP5297C_QUAD|PMP812E|PMP812E3G|PMP812F|PMP810E|PMP880TD|PMT3017|PMT3037|PMT3047|PMT3057|PMT7008|PMT5887|PMT5001|PMT5002',
        'LenovoTablet'      => 'Lenovo TAB|Idea(Tab|Pad)( A1|A10| K1|)|ThinkPad([ ]+)?Tablet|YT3-850M|YT3-X90L|YT3-X90F|YT3-X90X|Lenovo.*(S2109|S2110|S5000|S6000|K3011|A3000|A3500|A1000|A2107|A2109|A1107|A5500|A7600|B6000|B8000|B8080)(-|)(FL|F|HV|H|)|TB-X103F|TB-X304X|TB-X304F|TB-X304L|TB-X505F|TB-X505L|TB-X505X|TB-X605F|TB-X605L|TB-8703F|TB-8703X|TB-8703N|TB-8704N|TB-8704F|TB-8704X|TB-8704V|TB-7304F|TB-7304I|TB-7304X|Tab2A7-10F|Tab2A7-20F|TB2-X30L|YT3-X50L|YT3-X50F|YT3-X50M|YT-X705F|YT-X703F|YT-X703L|YT-X705L|YT-X705X|TB2-X30F|TB2-X30L|TB2-X30M|A2107A-F|A2107A-H|TB3-730F|TB3-730M|TB3-730X|TB-7504F|TB-7504X|TB-X704F|TB-X104F|TB3-X70F|TB-X705F|TB-8504F|TB3-X70L|TB3-710F|TB-X704L|TB-J606F|TB-X606F|TB-X306X|YT-J706X',
        'DellTablet'        => 'Venue 11|Venue 8|Venue 7|Dell Streak 10|Dell Streak 7',
        'XiaomiTablet'      => '21051182G',
        'iPad' => 'iPad|iPad.*Mobile',
        'YarvikTablet'      => 'Android.*\b(TAB210|TAB211|TAB224|TAB250|TAB260|TAB264|TAB310|TAB360|TAB364|TAB410|TAB411|TAB420|TAB424|TAB450|TAB460|TAB461|TAB464|TAB465|TAB467|TAB468|TAB07-100|TAB07-101|TAB07-150|TAB07-151|TAB07-152|TAB07-200|TAB07-201-3G|TAB07-210|TAB07-211|TAB07-212|TAB07-214|TAB07-220|TAB07-400|TAB07-485|TAB08-150|TAB08-200|TAB08-201-3G|TAB08-201-30|TAB09-100|TAB09-211|TAB09-410|TAB10-150|TAB10-201|TAB10-211|TAB10-400|TAB10-410|TAB13-201|TAB274EUK|TAB275EUK|TAB374EUK|TAB462EUK|TAB474EUK|TAB9-200)\b',
        'MedionTablet'      => 'Android.*\bOYO\b|LIFE.*(P9212|P9514|P9516|S9512)|LIFETAB',
        'ArnovaTablet'      => '97G4|AN10G2|AN7bG3|AN7fG3|AN8G3|AN8cG3|AN7G3|AN9G3|AN7dG3|AN7dG3ST|AN7dG3ChildPad|AN10bG3|AN10bG3DT|AN9G2',
        'IntensoTablet'     => 'INM8002KP|INM1010FP|INM805ND|Intenso Tab|TAB1004',
        'IRUTablet'         => 'M702pro',
        'MegafonTablet'     => 'MegaFon V9|\bZTE V9\b|Android.*\bMT7A\b',
        'EbodaTablet'       => 'E-Boda (Supreme|Impresspeed|Izzycomm|Essential)',
        'AllViewTablet'           => 'Allview.*(Viva|Alldro|City|Speed|All TV|Frenzy|Quasar|Shine|TX1|AX1|AX2)',
        'ArchosTablet'      => '\b(101G9|80G9|A101IT)\b|Qilive 97R|Archos5|\bARCHOS (70|79|80|90|97|101|FAMILYPAD|)(b|c|)(G10| Cobalt| TITANIUM(HD|)| Xenon| Neon|XSK| 2| XS 2| PLATINUM| CARBON|GAMEPAD)\b',
        'AinolTablet'       => 'NOVO7|NOVO8|NOVO10|Novo7Aurora|Novo7Basic|NOVO7PALADIN|novo9-Spark',
        'NokiaLumiaTablet'  => 'Lumia 2520',
        'SonyTablet'        => 'Sony.*Tablet|Xperia Tablet|Sony Tablet S|SO-03E|SGPT12|SGPT13|SGPT114|SGPT121|SGPT122|SGPT123|SGPT111|SGPT112|SGPT113|SGPT131|SGPT132|SGPT133|SGPT211|SGPT212|SGPT213|SGP311|SGP312|SGP321|EBRD1101|EBRD1102|EBRD1201|SGP351|SGP341|SGP511|SGP512|SGP521|SGP541|SGP551|SGP621|SGP641|SGP612|SOT31|SGP771|SGP611|SGP612|SGP712',
        'PhilipsTablet'     => '\b(PI2010|PI3000|PI3100|PI3105|PI3110|PI3205|PI3210|PI3900|PI4010|PI7000|PI7100)\b',
        'CubeTablet'        => 'Android.*(K8GT|U9GT|U10GT|U16GT|U17GT|U18GT|U19GT|U20GT|U23GT|U30GT)|CUBE U8GT',
        'CobyTablet'        => 'MID1042|MID1045|MID1125|MID1126|MID7012|MID7014|MID7015|MID7034|MID7035|MID7036|MID7042|MID7048|MID7127|MID8042|MID8048|MID8127|MID9042|MID9740|MID9742|MID7022|MID7010',
        'MIDTablet'         => 'M9701|M9000|M9100|M806|M1052|M806|T703|MID701|MID713|MID710|MID727|MID760|MID830|MID728|MID933|MID125|MID810|MID732|MID120|MID930|MID800|MID731|MID900|MID100|MID820|MID735|MID980|MID130|MID833|MID737|MID960|MID135|MID860|MID736|MID140|MID930|MID835|MID733|MID4X10',
        'MSITablet' => 'MSI \b(Primo 73K|Primo 73L|Primo 81L|Primo 77|Primo 93|Primo 75|Primo 76|Primo 73|Primo 81|Primo 91|Primo 90|Enjoy 71|Enjoy 7|Enjoy 10)\b',
        'SMiTTablet'        => 'Android.*(\bMID\b|MID-560|MTV-T1200|MTV-PND531|MTV-P1101|MTV-PND530)',
        'PyleAudioTablet'   => '\b(PTBL10CEU|PTBL10C|PTBL72BC|PTBL72BCEU|PTBL7CEU|PTBL7C|PTBL92BC|PTBL92BCEU|PTBL9CEU|PTBL9CUK|PTBL9C)\b',
        'RockChipTablet'    => 'Android.*(RK2818|RK2808A|RK2918|RK3066)|RK2738|RK2808A',
        'FlyTablet'         => 'IQ310|Fly Vision',
        'bqTablet'          => 'Android.*(bq)?.*\b(Elcano|Curie|Edison|Maxwell|Kepler|Pascal|Tesla|Hypatia|Platon|Newton|Livingstone|Cervantes|Avant|Aquaris ([E|M]10|M8))\b|Maxwell.*Lite|Maxwell.*Plus',
        'HuaweiTablet'      => 'MediaPad|MediaPad 7 Youth|IDEOS S7|S7-201c|S7-202u|S7-101|S7-103|S7-104|S7-105|S7-106|S7-201|S7-Slim|M2-A01L|BAH-L09|BAH-W09|AGS-L09|CMR-AL19|KOB2-L09|BG2-U01|BG2-W09|BG2-U03',
        'NecTablet'         => '\bN-06D|\bN-08D',
        'PantechTablet'     => 'Pantech.*P4100',
        'BronchoTablet'     => 'Broncho.*(N701|N708|N802|a710)',
        'DanewTablet'       => 'DSlide.*\b(700|701R|702|703R|704|802|970|971|972|973|974|1010|1012)\b',
        'VersusTablet'      => 'TOUCHPAD.*[78910]|\bTOUCHTAB\b',
        'ZyncTablet'        => 'z1000|Z99 2G|z930|z990|z909|Z919|z900',
        'PositivoTablet'    => 'TB07STA|TB10STA|TB07FTA|TB10FTA',
        'NabiTablet'        => 'Android.*\bNabi',
        'KoboTablet'        => 'Kobo Touch|\bK080\b|\bVox\b Build|\bArc\b Build',
        'TexetTablet'       => 'NaviPad|TB-772A|TM-7045|TM-7055|TM-9750|TM-7016|TM-7024|TM-7026|TM-7041|TM-7043|TM-7047|TM-8041|TM-9741|TM-9747|TM-9748|TM-9751|TM-7022|TM-7021|TM-7020|TM-7011|TM-7010|TM-7023|TM-7025|TM-7037W|TM-7038W|TM-7027W|TM-9720|TM-9725|TM-9737W|TM-1020|TM-9738W|TM-9740|TM-9743W|TB-807A|TB-771A|TB-727A|TB-725A|TB-719A|TB-823A|TB-805A|TB-723A|TB-715A|TB-707A|TB-705A|TB-709A|TB-711A|TB-890HD|TB-880HD|TB-790HD|TB-780HD|TB-770HD|TB-721HD|TB-710HD|TB-434HD|TB-860HD|TB-840HD|TB-760HD|TB-750HD|TB-740HD|TB-730HD|TB-722HD|TB-720HD|TB-700HD|TB-500HD|TB-470HD|TB-431HD|TB-430HD|TB-506|TB-504|TB-446|TB-436|TB-416|TB-146SE|TB-126SE',
        'PlaystationTablet' => 'Playstation.*(Portable|Vita)',
        'TrekstorTablet'    => 'ST10416-1|VT10416-1|ST70408-1|ST702xx-1|ST702xx-2|ST80208|ST97216|ST70104-2|VT10416-2|ST10216-2A|SurfTab',
        'AdvanTablet'       => 'Android.* \b(E3A|T3X|T5C|T5B|T3E|T3C|T3B|T1J|T1F|T2A|T1H|T1i|E1C|T1-E|T5-A|T4|E1-B|T2Ci|T1-B|T1-D|O1-A|E1-A|T1-A|T3A|T4i)\b ',
        'DanyTechTablet' => 'Genius Tab G3|Genius Tab S2|Genius Tab Q3|Genius Tab G4|Genius Tab Q4|Genius Tab G-II|Genius TAB GII|Genius TAB GIII|Genius Tab S1',
        'GalapadTablet'     => 'Android [0-9.]+; [a-z-]+; \bG1\b',
        'MicromaxTablet'    => 'Funbook|Micromax.*\b(P250|P560|P360|P362|P600|P300|P350|P500|P275)\b',
        'KarbonnTablet'     => 'Android.*\b(A39|A37|A34|ST8|ST10|ST7|Smart Tab3|Smart Tab2)\b',
        'AllFineTablet'     => 'Fine7 Genius|Fine7 Shine|Fine7 Air|Fine8 Style|Fine9 More|Fine10 Joy|Fine11 Wide',
        'PROSCANTablet'     => '\b(PEM63|PLT1023G|PLT1041|PLT1044|PLT1044G|PLT1091|PLT4311|PLT4311PL|PLT4315|PLT7030|PLT7033|PLT7033D|PLT7035|PLT7035D|PLT7044K|PLT7045K|PLT7045KB|PLT7071KG|PLT7072|PLT7223G|PLT7225G|PLT7777G|PLT7810K|PLT7849G|PLT7851G|PLT7852G|PLT8015|PLT8031|PLT8034|PLT8036|PLT8080K|PLT8082|PLT8088|PLT8223G|PLT8234G|PLT8235G|PLT8816K|PLT9011|PLT9045K|PLT9233G|PLT9735|PLT9760G|PLT9770G)\b',
        'YONESTablet' => 'BQ1078|BC1003|BC1077|RK9702|BC9730|BC9001|IT9001|BC7008|BC7010|BC708|BC728|BC7012|BC7030|BC7027|BC7026',
        'ChangJiaTablet'    => 'TPC7102|TPC7103|TPC7105|TPC7106|TPC7107|TPC7201|TPC7203|TPC7205|TPC7210|TPC7708|TPC7709|TPC7712|TPC7110|TPC8101|TPC8103|TPC8105|TPC8106|TPC8203|TPC8205|TPC8503|TPC9106|TPC9701|TPC97101|TPC97103|TPC97105|TPC97106|TPC97111|TPC97113|TPC97203|TPC97603|TPC97809|TPC97205|TPC10101|TPC10103|TPC10106|TPC10111|TPC10203|TPC10205|TPC10503',
        'GUTablet'          => 'TX-A1301|TX-M9002|Q702|kf026',
        'PointOfViewTablet' => 'TAB-P506|TAB-navi-7-3G-M|TAB-P517|TAB-P-527|TAB-P701|TAB-P703|TAB-P721|TAB-P731N|TAB-P741|TAB-P825|TAB-P905|TAB-P925|TAB-PR945|TAB-PL1015|TAB-P1025|TAB-PI1045|TAB-P1325|TAB-PROTAB[0-9]+|TAB-PROTAB25|TAB-PROTAB26|TAB-PROTAB27|TAB-PROTAB26XL|TAB-PROTAB2-IPS9|TAB-PROTAB30-IPS9|TAB-PROTAB25XXL|TAB-PROTAB26-IPS10|TAB-PROTAB30-IPS10',
        'OvermaxTablet'     => 'OV-(SteelCore|NewBase|Basecore|Baseone|Exellen|Quattor|EduTab|Solution|ACTION|BasicTab|TeddyTab|MagicTab|Stream|TB-08|TB-09)|Qualcore 1027',
        'HCLTablet'         => 'HCL.*Tablet|Connect-3G-2.0|Connect-2G-2.0|ME Tablet U1|ME Tablet U2|ME Tablet G1|ME Tablet X1|ME Tablet Y2|ME Tablet Sync',
        'DPSTablet'         => 'DPS Dream 9|DPS Dual 7',
        'VistureTablet'     => 'V97 HD|i75 3G|Visture V4( HD)?|Visture V5( HD)?|Visture V10',
        'CrestaTablet'     => 'CTP(-)?810|CTP(-)?818|CTP(-)?828|CTP(-)?838|CTP(-)?888|CTP(-)?978|CTP(-)?980|CTP(-)?987|CTP(-)?988|CTP(-)?989',
        'MediatekTablet' => '\bMT8125|MT8389|MT8135|MT8377\b',
        'ConcordeTablet' => 'Concorde([ ]+)?Tab|ConCorde ReadMan',
        'GoCleverTablet' => 'GOCLEVER TAB|A7GOCLEVER|M1042|M7841|M742|R1042BK|R1041|TAB A975|TAB A7842|TAB A741|TAB A741L|TAB M723G|TAB M721|TAB A1021|TAB I921|TAB R721|TAB I720|TAB T76|TAB R70|TAB R76.2|TAB R106|TAB R83.2|TAB M813G|TAB I721|GCTA722|TAB I70|TAB I71|TAB S73|TAB R73|TAB R74|TAB R93|TAB R75|TAB R76.1|TAB A73|TAB A93|TAB A93.2|TAB T72|TAB R83|TAB R974|TAB R973|TAB A101|TAB A103|TAB A104|TAB A104.2|R105BK|M713G|A972BK|TAB A971|TAB R974.2|TAB R104|TAB R83.3|TAB A1042',
        'ModecomTablet' => 'FreeTAB 9000|FreeTAB 7.4|FreeTAB 7004|FreeTAB 7800|FreeTAB 2096|FreeTAB 7.5|FreeTAB 1014|FreeTAB 1001 |FreeTAB 8001|FreeTAB 9706|FreeTAB 9702|FreeTAB 7003|FreeTAB 7002|FreeTAB 1002|FreeTAB 7801|FreeTAB 1331|FreeTAB 1004|FreeTAB 8002|FreeTAB 8014|FreeTAB 9704|FreeTAB 1003',
        'VoninoTablet'  => '\b(Argus[ _]?S|Diamond[ _]?79HD|Emerald[ _]?78E|Luna[ _]?70C|Onyx[ _]?S|Onyx[ _]?Z|Orin[ _]?HD|Orin[ _]?S|Otis[ _]?S|SpeedStar[ _]?S|Magnet[ _]?M9|Primus[ _]?94[ _]?3G|Primus[ _]?94HD|Primus[ _]?QS|Android.*\bQ8\b|Sirius[ _]?EVO[ _]?QS|Sirius[ _]?QS|Spirit[ _]?S)\b',
        'ECSTablet'     => 'V07OT2|TM105A|S10OT1|TR10CS1',
        'StorexTablet'  => 'eZee[_\']?(Tab|Go)[0-9]+|TabLC7|Looney Tunes Tab',
        'VodafoneTablet' => 'SmartTab([ ]+)?[0-9]+|SmartTabII10|SmartTabII7|VF-1497|VFD 1400',
        'EssentielBTablet' => 'Smart[ \']?TAB[ ]+?[0-9]+|Family[ \']?TAB2',
        'RossMoorTablet' => 'RM-790|RM-997|RMD-878G|RMD-974R|RMT-705A|RMT-701|RME-601|RMT-501|RMT-711',
        'iMobileTablet'        => 'i-mobile i-note',
        'TolinoTablet'  => 'tolino tab [0-9.]+|tolino shine',
        'AudioSonicTablet' => '\bC-22Q|T7-QC|T-17B|T-17P\b',
        'AMPETablet' => 'Android.* A78 ',
        'SkkTablet' => 'Android.* (SKYPAD|PHOENIX|CYCLOPS)',
        'NexoTablet' => 'NEXO NOVA|NEXO 10|NEXO AVIO|NEXO FREE|NEXO GO|NEXO EVO|NEXO 3G|NEXO SMART|NEXO KIDDO|NEXO MOBI',
        'TecnoTablet' => 'TECNO P9|TECNO DP8D',
        'JXDTablet' => 'Android.* \b(F3000|A3300|JXD5000|JXD3000|JXD2000|JXD300B|JXD300|S5800|S7800|S602b|S5110b|S7300|S5300|S602|S603|S5100|S5110|S601|S7100a|P3000F|P3000s|P101|P200s|P1000m|P200m|P9100|P1000s|S6600b|S908|P1000|P300|S18|S6600|S9100)\b',
        'iJoyTablet' => 'Tablet (Spirit 7|Essentia|Galatea|Fusion|Onix 7|Landa|Titan|Scooby|Deox|Stella|Themis|Argon|Unique 7|Sygnus|Hexen|Finity 7|Cream|Cream X2|Jade|Neon 7|Neron 7|Kandy|Scape|Saphyr 7|Rebel|Biox|Rebel|Rebel 8GB|Myst|Draco 7|Myst|Tab7-004|Myst|Tadeo Jones|Tablet Boing|Arrow|Draco Dual Cam|Aurix|Mint|Amity|Revolution|Finity 9|Neon 9|T9w|Amity 4GB Dual Cam|Stone 4GB|Stone 8GB|Andromeda|Silken|X2|Andromeda II|Halley|Flame|Saphyr 9,7|Touch 8|Planet|Triton|Unique 10|Hexen 10|Memphis 4GB|Memphis 8GB|Onix 10)',
        'FX2Tablet' => 'FX2 PAD7|FX2 PAD10',
        'XoroTablet'        => 'KidsPAD 701|PAD[ ]?712|PAD[ ]?714|PAD[ ]?716|PAD[ ]?717|PAD[ ]?718|PAD[ ]?720|PAD[ ]?721|PAD[ ]?722|PAD[ ]?790|PAD[ ]?792|PAD[ ]?900|PAD[ ]?9715D|PAD[ ]?9716DR|PAD[ ]?9718DR|PAD[ ]?9719QR|PAD[ ]?9720QR|TelePAD1030|Telepad1032|TelePAD730|TelePAD731|TelePAD732|TelePAD735Q|TelePAD830|TelePAD9730|TelePAD795|MegaPAD 1331|MegaPAD 1851|MegaPAD 2151',
        'ViewsonicTablet'   => 'ViewPad 10pi|ViewPad 10e|ViewPad 10s|ViewPad E72|ViewPad7|ViewPad E100|ViewPad 7e|ViewSonic VB733|VB100a',
        'VerizonTablet' => 'QTAQZ3|QTAIR7|QTAQTZ3|QTASUN1|QTASUN2|QTAXIA1',
        'OdysTablet'        => 'LOOX|XENO10|ODYS[ -](Space|EVO|Xpress|NOON)|\bXELIO\b|Xelio10Pro|XELIO7PHONETAB|XELIO10EXTREME|XELIOPT2|NEO_QUAD10',
        'CaptivaTablet'     => 'CAPTIVA PAD',
        'KocasoTablet' => '\b(TB-1207)\b',
        'HisenseTablet' => '\b(F5281|E2371)\b',
        'IconbitTablet' => 'NetTAB|NT-3702|NT-3702S|NT-3702S|NT-3603P|NT-3603P|NT-0704S|NT-0704S|NT-3805C|NT-3805C|NT-0806C|NT-0806C|NT-0909T|NT-0909T|NT-0907S|NT-0907S|NT-0902S|NT-0902S',
        'TeclastTablet' => 'T98 4G|\bP80\b|\bX90HD\b|X98 Air|X98 Air 3G|\bX89\b|P80 3G|\bX80h\b|P98 Air|\bX89HD\b|P98 3G|\bP90HD\b|P89 3G|X98 3G|\bP70h\b|P79HD 3G|G18d 3G|\bP79HD\b|\bP89s\b|\bA88\b|\bP10HD\b|\bP19HD\b|G18 3G|\bP78HD\b|\bA78\b|\bP75\b|G17s 3G|G17h 3G|\bP85t\b|\bP90\b|\bP11\b|\bP98t\b|\bP98HD\b|\bG18d\b|\bP85s\b|\bP11HD\b|\bP88s\b|\bA80HD\b|\bA80se\b|\bA10h\b|\bP89\b|\bP78s\b|\bG18\b|\bP85\b|\bA70h\b|\bA70\b|\bG17\b|\bP18\b|\bA80s\b|\bA11s\b|\bP88HD\b|\bA80h\b|\bP76s\b|\bP76h\b|\bP98\b|\bA10HD\b|\bP78\b|\bP88\b|\bA11\b|\bA10t\b|\bP76a\b|\bP76t\b|\bP76e\b|\bP85HD\b|\bP85a\b|\bP86\b|\bP75HD\b|\bP76v\b|\bA12\b|\bP75a\b|\bA15\b|\bP76Ti\b|\bP81HD\b|\bA10\b|\bT760VE\b|\bT720HD\b|\bP76\b|\bP73\b|\bP71\b|\bP72\b|\bT720SE\b|\bC520Ti\b|\bT760\b|\bT720VE\b|T720-3GE|T720-WiFi',
        'OndaTablet' => '\b(V975i|Vi30|VX530|V701|Vi60|V701s|Vi50|V801s|V719|Vx610w|VX610W|V819i|Vi10|VX580W|Vi10|V711s|V813|V811|V820w|V820|Vi20|V711|VI30W|V712|V891w|V972|V819w|V820w|Vi60|V820w|V711|V813s|V801|V819|V975s|V801|V819|V819|V818|V811|V712|V975m|V101w|V961w|V812|V818|V971|V971s|V919|V989|V116w|V102w|V973|Vi40)\b[\s]+|V10 \b4G\b',
        'JaytechTablet'     => 'TPC-PA762',
        'TelstraTablet'     => 'T-Hub2',
        'BlaupunktTablet'   => 'Endeavour 800NG|Endeavour 1010',
        'Hudl'              => 'Hudl HT7S3|Hudl 2',
        'DigmaTablet' => '\b(iDx10|iDx9|iDx8|iDx7|iDxD7|iDxD8|iDsQ8|iDsQ7|iDsQ8|iDsD10|iDnD7|3TS804H|iDsQ11|iDj7|iDs10)\b',
        'EvolioTablet' => 'ARIA_Mini_wifi|Aria[ _]Mini|Evolio X10|Evolio X7|Evolio X8|\bEvotab\b|\bNeura\b',
        'LavaTablet' => 'QPAD E704|\bIvoryS\b|E-TAB IVORY|\bE-TAB\b',
        'GenericTablet'     => 'Android.*\b97D\b|Tablet(?!.*PC)|BNTV250A|MID-WCDMA|LogicPD Zoom2|\bA7EB\b|CatNova8|A1_07|CT704|CT1002|\bM721\b|rk30sdk|\bEVOTAB\b|M758A|ET904|ALUMIUM10|Smartfren Tab|Endeavour 1010|Tablet-PC-4|Tagi Tab|\bM6pro\b|CT1020W|arc 10HD|\bTP750\b|\bQTAQZ3\b|WVT101|TM1088|KT107',
        'AocTablet' => 'MW0811|MW0812|MW0922|MTK8382|MW1031|MW0831|MW0821|MW0931|MW0712',
        'PocketBookTablet' => 'Pocketbook',
        'MpmanTablet' => 'MP11 OCTA|MP10 OCTA|MPQC1114|MPQC1004|MPQC994|MPQC974|MPQC973|MPQC804|MPQC784|MPQC780|\bMPG7\b|MPDCG75|MPDCG71|MPDC1006|MP101DC|MPDC9000|MPDC905|MPDC706HD|MPDC706|MPDC705|MPDC110|MPDC100|MPDC99|MPDC97|MPDC88|MPDC8|MPDC77|MP709|MID701|MID711|MID170|MPDC703|MPQC1010',
        'CelkonTablet' => 'CT695|CT888|CT[\s]?910|CT7 Tab|CT9 Tab|CT3 Tab|CT2 Tab|CT1 Tab|C820|C720|\bCT-1\b',
        'WolderTablet' => 'miTab \b(DIAMOND|SPACE|BROOKLYN|NEO|FLY|MANHATTAN|FUNK|EVOLUTION|SKY|GOCAR|IRON|GENIUS|POP|MINT|EPSILON|BROADWAY|JUMP|HOP|LEGEND|NEW AGE|LINE|ADVANCE|FEEL|FOLLOW|LIKE|LINK|LIVE|THINK|FREEDOM|CHICAGO|CLEVELAND|BALTIMORE-GH|IOWA|BOSTON|SEATTLE|PHOENIX|DALLAS|IN 101|MasterChef)\b',
        'MediacomTablet' => 'M-MPI10C3G|M-SP10EG|M-SP10EGP|M-SP10HXAH|M-SP7HXAH|M-SP10HXBH|M-SP8HXAH|M-SP8MXA',
        'MiTablet' => '\bMI PAD\b|\bHM NOTE 1W\b',
        'UbislateTablet' => 'UbiSlate[\s]?7C',
        'NibiruTablet' => 'Nibiru M1|Nibiru Jupiter One',
        'LeaderTablet' => 'TBLT10Q|TBLT10I|TBL-10WDKB|TBL-10WDKBO2013|TBL-W230V2|TBL-W450|TBL-W500|SV572|TBLT7I|TBA-AC7-8G|TBLT79|TBL-8W16|TBL-10W32|TBL-10WKB|TBL-W100',
    );

    $is_tablet = false;
    foreach ($tablets as $brand => $pattern) {
        // Check if the tablet matches the current brand's pattern
        if (preg_match('#' . $pattern . '#i', $widgetopts_user_agent)) {
            // Tablet matched, so echo the brand
            $is_tablet = true;
            break;
        }
    }

    // If no match is found, try another method, check only the most common keyword
    if (!$is_tablet) {
        $is_tablet = widgetopts_is_mobile_or_tablet(true);
    }

    return $is_tablet;
}

/**
 * Retrieves the user agent string from HTTP headers.
 *
 * @return string The concatenated user agent string.
 */
function widgetopts_get_user_agent()
{
    global $widgetopts_http_headers;

    $user_agent = '';

    // Define the user agent headers to check
    $user_agents = [
        'HTTP_USER_AGENT',
        'HTTP_X_ORIGINAL_USER_AGENT',
        'HTTP_X_OPERAMINI_PHONE_UA',
        'HTTP_X_BOLT_PHONE_UA',
        'HTTP_X_DEVICE_USER_AGENT',
        'HTTP_X_SKYFIRE_PHONE',
        'HTTP_X_UCBROWSER_DEVICE_UA',
        'HTTP_DEVICE_STOCK_UA',
    ];

    // Concatenate user agents from available headers
    foreach ($user_agents as $ua) {
        if (isset($widgetopts_http_headers[$ua]) && !empty($widgetopts_http_headers[$ua])) {
            $user_agent .= $widgetopts_http_headers[$ua] . " ";
        }
    }

    return trim($user_agent); // Trim the concatenated user agent string and return
}

/**
 * Safely evaluates a boolean expression and prevents any output from being printed.
 *
 * This function uses eval() to evaluate the provided expression. It performs
 * basic validation to ensure that the expression consists of valid boolean logic.
 * Output generated by the eval() is suppressed using output buffering.
 *
 * @param string $expression The boolean expression to evaluate.
 * @return bool Returns true or false based on the evaluated expression, or false on error.
 */
function widgetopts_safe_eval($expression)
{
    if (widgetopts_is_widget_or_post_preview()) {
        // Always return true for previews unless the user is an administrator
        if (!current_user_can('administrator')) {
            return true;
        }
    }

    $validation_result = widgetopts_validate_expression($expression);

    if ($validation_result['valid'] === false) {
        return false;
    }

    if (stristr($expression, "return") === false) {
        $expression = "return (" . $expression . ");";
    }

    ob_start();
    try {
        $result = (bool) (@eval($expression));
    } catch (\Exception $e) {
        $result = false;
    } catch (\Error $e) {
        $result = false;
    } catch (\ParseError $e) {
        $result = false;
    } catch (\Throwable $e) {
        $result = false;
    }
    ob_end_clean();

    return $result;
}

/**
 * Validate the expression if it contains dangerous and unallowed patterns.
 */
function widgetopts_validate_expression($expression)
{
    // Ensure the expression has no backtick execution
    if (stripos($expression, '`') !== false) {
        return ['valid' => false, 'message' => 'Backticks execution is not allowed.'];
    }

    // List of potentially harmful patterns and their messages
    $dangerous_patterns = [
        '/\b(insert|update|delete|replace|select|drop|alter|truncate|grant|revoke)\b/i' => 'Potential SQL injection detected.',
        '/\b(wp_insert_post|wp_update_post|wp_delete_post|wp_insert_user|wp_update_user|wp_delete_user|add_option|update_option|delete_option|wpdb)\b/i' => 'Unsafe WordPress database functions found.',
        '/\b(file_put_contents|file_get_contents|fopen|fwrite|unlink|rename|chmod|chown|chgrp|copy|scandir)\b/i' => 'File system manipulation functions are not allowed.',
        '/\b(wp_remote_get|wp_remote_post|curl_init|curl_exec|curl_setopt|open_basedir|fsockopen|proc_nice|stream_socket_server|stream_socket_client)\b/i' => 'Potential remote execution functions detected.',
        '/\b(eval|assert|system|exec|shell_exec|passthru|proc_open|popen|pcntl_exec|dl|include|require|include_once|require_once)\b/i' => 'Execution functions are not allowed.',
        '/\b(base64_decode|hex2bin|mb_decode_mimeheader|str_rot13)\b/i' => 'Encoding/decoding functions that may be used for obfuscation are not allowed.',
        '/\b(call_user_func|call_user_func_array|create_function|compact|extract|parse_str|ReflectionClass|ReflectionMethod|ReflectionProperty)\b/i' => 'Dynamic function execution is not allowed.',
        '/\b(str_replace|str_ireplace|preg_replace|preg_replace_callback|preg_replace_callback_array)\b/i' => 'String replacement functions are restricted due to potential obfuscation.',
        '/`[^`]*`/' => 'Backticks execution is not allowed.',
        '/\[\s*[\'"]?[a-zA-Z0-9_]+\s*\.\s*[\'"]?[a-zA-Z0-9_]+\s*\]/i' => 'Concatenated function execution is not allowed.',
        '/\b(str_replace|preg_replace|preg_replace_callback|preg_replace_callback_array)\s*\(\s*[\'"]\s*\.\s*[\'"]/' => 'Potential function name obfuscation detected.',
        '/\$\w+\s*\[\s*[\'"]?\d+[\'"]?\s*\]\s*\(/' => 'Dynamic function execution using arrays is not allowed.',
    ];

    $is_valid = true;
    // Check for dangerous patterns
    foreach ($dangerous_patterns as $pattern => $message) {
        if (preg_match($pattern, $expression)) {
            $is_valid = false;
            break;
        }
    }

    if ($is_valid === false) {
        return ['valid' => false, 'message' => $message];
    }

    // Retrieve allowed functions
    $allowed_functions = array_merge(widgetopts_get_allowed_php_functions(), widgetopts_get_allowed_wp_functions());
    $allowed_functions = apply_filters('widgetopts_allowed_php_functions', $allowed_functions);

    // Validate using token-based function checking
    $is_valid = widgetopts_validate_code_with_tokens($expression, $allowed_functions);
    $is_valid = apply_filters('widgetopts_validate_allowed_commands', $is_valid);

    // Return validation result
    return $is_valid ? ['valid' => true, 'message' => 'Expression is safe.'] : ['valid' => false, 'message' => 'Expression contains disallowed function calls.'];
}

/**
 * Returns a list of allowed PHP functions.
 */
function widgetopts_get_allowed_php_functions()
{
    return [
        // String Manipulation
        'strlen',
        'substr',
        'strtolower',
        'strtoupper',
        'trim',
        'ltrim',
        'rtrim',
        'preg_match',
        'explode',
        'implode',
        'htmlspecialchars',
        'htmlentities',
        'strip_tags',
        'str_repeat',
        'str_split',
        'strpos',
        'strrpos',
        'stripos',
        'strripos',
        'substr_replace',
        'wordwrap',

        // Array Manipulation
        'array_merge',
        'array_diff',
        'array_filter',
        'array_map',
        'array_keys',
        'array_values',
        'in_array',
        'count',
        'sizeof',
        'array_slice',
        'array_push',
        'array_pop',
        'array_reduce',
        'array_intersect',
        'array_unique',
        'array_column',
        'array_reverse',

        // Math Functions
        'abs',
        'round',
        'floor',
        'ceil',
        'min',
        'max',
        'pow',
        'sqrt',
        'rand',
        'mt_rand',
        'number_format',
        'log',
        'exp',
        'bcadd',
        'bcsub',
        'bcmul',
        'bcdiv',

        // Date/Time
        'time',
        'date',
        'strtotime',
        'mktime',
        'checkdate',
        'date_default_timezone_get',
        'gmdate',

        // JSON Functions
        'json_decode',
        'json_encode',

        // Variable Handling
        'isset',
        'empty',
        'unset',
        'is_array',
        'is_bool',
        'is_callable',
        'is_countable',
        'is_double',
        'is_float',
        'is_int',
        'is_integer',
        'is_iterable',
        'is_long',
        'is_null',
        'is_numeric',
        'is_object',
        'is_real',
        'is_scalar',
        'is_string',
        'is_subclass_of',
        'is_uploaded_file',
        'is_writable',
        'is_readable',
        'is_dir',
        'is_file',
        'is_link',
        'intval',
        'strval',
        'floatval',
        'doubleval',
        'boolval',
        'realpath',

        // File Operations (Read-Only)
        'filesize',
        'pathinfo',
        'basename',
        'dirname',
        'file_exists',
        'readfile',
    ];
}

/**
 * Returns a list of allowed WordPress functions.
 */
function widgetopts_get_allowed_wp_functions()
{
    return [
        // Database Operations
        'get_option',
        'get_post',
        'get_user_meta',
        'get_bloginfo',
        'get_theme_mod',

        // Escaping and Sanitizing
        'esc_url',
        'esc_html',
        'esc_attr',
        'esc_js',
        'sanitize_text_field',
        'sanitize_email',
        'sanitize_title',
        'sanitize_key',

        // URL and Links
        'get_permalink',
        'get_home_url',
        'get_site_url',
        'get_admin_url',
        'wp_parse_url',
        'urlencode',
        'urldecode',
        'rawurlencode',
        'rawurldecode',
        'parse_url',
        'http_build_query',

        // Security and Authentication
        'wp_verify_nonce',
        'check_admin_referer',
        'current_user_can',
        'is_user_logged_in',
        'wp_nonce_field',
        'wp_nonce_url',

        // Formatting
        'wp_kses',
        'wp_kses_post',
        'wp_trim_words',
        'wpautop',

        // Retrieval of Metadata and Post Details
        'get_post_meta',
        'get_page_template_slug',
        'get_the_author_meta',
        'get_the_terms',
        'get_comments',
        'get_category',
        'get_terms',
        'get_nav_menu_locations',
        'get_posts',
        'get_post_status',
        'get_comment_meta',
        'get_taxonomy',
        'get_object_taxonomies',
        'get_post_type',
        'get_the_ID',
        'get_the_title',
        'get_edit_post_link',

        // Plugins and Themes
        'is_plugin_active',
        'get_plugins',
        'get_theme_support',

        // Utilities
        'wp_generate_uuid4',

        // User and Authentication
        'get_userdata',
        'get_user_by',
        'get_users',
        'get_author_posts_url',
        'get_user_roles',
        'get_users_by_role',
        'wp_get_current_user',

        // Media and Files
        'wp_get_attachment_image_src',
        'wp_get_attachment_url',
        'wp_get_attachment_metadata',
        'wp_get_attachment_image',
        'wp_get_attachment_thumb_url',

        // File Handling
        'wp_get_upload_dir',
        'wp_handle_upload',
        'wp_check_filetype_and_ext',
        'wp_get_file_upload_url',

        // Taxonomies
        'get_term_meta',
        'get_the_category_list',
        'get_the_tag_list',
        'get_the_term_list',

        // Checking Functions
        'is_admin',
        'is_network_admin',
        'is_main_query',
        'is_single',
        'is_singular',
        'is_category',
        'is_tag',
        'is_page',
        'is_front_page',
        'is_home',
        'is_attachment',
        'is_archive',
        'is_date',
        'is_year',
        'is_month',
        'is_day',
        'is_author',
        'is_search',
        'is_404',
        'is_multisite',
        'is_customize_preview',
    ];
}

/**
 * Validate PHP code against allowed functions and detect obfuscated calls.
 *
 * @param string $code The PHP code to validate.
 * @param array $allowed_functions List of explicitly allowed functions.
 * @return bool Returns true if the code is considered safe, false otherwise.
 */
function widgetopts_validate_code_with_tokens($code, $allowed_functions)
{
    $code = stripslashes($code);
    // Ensure correct PHP opening tag before tokenizing
    if (strpos(trim($code), '<?php') !== 0) {
        $code = "<?php " . $code;
    }

    $tokens = token_get_all($code);
    $is_safe = true;
    $last_token = null;

    foreach ($tokens as $index => $token) {
        if (is_array($token)) {
            $token_type = $token[0];
            $token_value = $token[1];

            // **Fix: Properly detect function calls inside conditions**
            if ($token_type === T_STRING) {
                $function_name = strtolower($token_value);
                $next_token = $tokens[$index + 1] ?? null;

                if ($next_token === '(' || (is_array($next_token) && $next_token[0] === T_WHITESPACE && ($tokens[$index + 2] ?? null) === '(')) {
                    if (!in_array($function_name, array_map('strtolower', $allowed_functions))) {
                        $is_safe = false;
                        break;
                    }
                }
            }

            // **Fix: Detect if T_ENCAPSED_AND_WHITESPACE is being treated as code**
            if ($token_type === T_ENCAPSED_AND_WHITESPACE) {
                $is_safe = false;
                break;
            }

            // **Fix: Dynamic Function Execution (`$func()` or `['test']()`)**
            if ($token_type === T_VARIABLE || $token_type === T_CONSTANT_ENCAPSED_STRING) {
                $next_token = $tokens[$index + 1] ?? null;
                if ($next_token === '(') {
                    $is_safe = false;
                    break;
                }
            }

            $last_token = $token;
        }
    }

    return $is_safe;
}

function widgetopts_is_widget_or_post_preview()
{
    // Check if it's a block editor preview
    if (defined('REST_REQUEST') && REST_REQUEST && isset($_GET['context']) && $_GET['context'] === 'edit') {
        return true;
    }

    // Check if it's a post/page preview
    if (is_preview() || (isset($_GET['preview']) && $_GET['preview'] == 'true')) {
        return true;
    }

    // Check if it's a Customizer preview
    if (is_customize_preview()) {
        return true;
    }

    return false;
}
