/home/tuzdhajd/ardaughters.org/wp-content/plugins/custom-php-settings/src/Backend/Backend.php
<?php

namespace CustomPhpSettings\Backend;

use CustomPhpSettings\Plugin\Settings\Settings;
use function CustomPhpSettings\cps_fs;
class Backend {
    const VERSION = '2.4.1';

    const SETTINGS_NAME = 'custom_php_settings';

    const MENU_SLUG = 'custom-php-settings';

    const MARKER = 'CUSTOM PHP SETTINGS';

    const CPS_NONCE = 'custom_php_settings';

    const FIELD_SETTINGS = 'settings';

    const FIELD_SETTING_NAME = 'name';

    const FIELD_SETTING_INDEX = 'settingIndex';

    const FIELD_PHP_SETTINGS = 'php_settings';

    const FIELD_PHP = 'php';

    const FIELD_ENV = 'environment';

    const FIELD_UPDATE_CONFIG = 'update_config';

    const FIELD_RESTORE_CONFIG = 'restore_config';

    const FIELD_TRIM_COMMENTS = 'trim_comments';

    const FIELD_TRIM_WHITESPACES = 'trim_whitespaces';

    const FIELD_NOTIFICATIONS = 'notes';

    const FIELD_VERSION = 'version';

    const PLAN_FREE = 'free';

    const PLAN_BASIC = 'basic';

    const PLAN_PROFESSIONAL = 'professional';

    const PLAN_AGENCY = 'agency';

    /**
     *
     * @var Settings
     */
    private $settings;

    /**
     * @var string $capability
     */
    private $capability = 'manage_options';

    /**
     * @var string $currentTab
     */
    private $currentTab = '';

    /**
     * @var string $currentSection
     */
    private $currentSection = '';

    /**
     * @var \WP_Filesystem $fileSystem
     */
    private $fileSystem = null;

    /**
     * @param Settings $settings
     */
    public function __construct( $settings ) {
        // Allow people to change what capability is required to use this plugin.
        $this->capability = apply_filters( 'custom_php_settings_cap', $this->capability );
        $this->settings = $settings;
        $this->checkForUpgrade();
        $this->setTabs();
        $this->addActions();
        $this->addFilters();
        $this->sortSuperGlobals();
    }

    /**
     * Sort super globals.
     */
    protected function sortSuperGlobals() {
        ksort( $_COOKIE );
        ksort( $_ENV );
        ksort( $_SERVER );
    }

    /**
     * Localize plugin.
     */
    public function localize() {
        load_plugin_textdomain( 'custom-php-settings', false, dirname( plugin_basename( __FILE__ ) ) . '/../../languages' );
    }

    /**
     * Add actions.
     */
    public function addActions() {
        add_action( 'init', array($this, 'localize') );
        add_action( 'admin_menu', array($this, 'addMenu') );
        add_action( 'in_admin_header', array($this, 'addHeader') );
        add_action( 'admin_post_custom_php_settings_save_settings', array($this, 'saveSettings') );
        add_action( 'admin_enqueue_scripts', array($this, 'addScripts') );
        add_action( 'custom_php_settings_admin_notices', array($this, 'renderNotices') );
        add_action( 'wp_ajax_custom_php_settings_dismiss_notice', array($this, 'doDismissNotice') );
    }

    /**
     * Add filters.
     */
    public function addFilters() {
        add_filter( 'admin_footer_text', array($this, 'adminFooter') );
        add_filter(
            'plugin_action_links',
            array($this, 'addActionLinks'),
            10,
            2
        );
        add_filter(
            'plugin_row_meta',
            array($this, 'filterPluginRowMeta'),
            10,
            4
        );
    }

    /**
     * Marks a notification as dismissed.
     *
     * @param string $id
     * @return bool
     */
    private function dismissNotice( $id ) {
        $notes = $this->settings->get( 'notes' );
        foreach ( $notes as $key => $note ) {
            if ( $note['id'] === (int) $id ) {
                $notes[$key]['dismissed'] = true;
                $notes[$key]['time'] = time();
                $this->settings->set( 'notes', $notes );
                $this->settings->save();
                return true;
            }
        }
    }

    /**
     * Resets a notification.
     *
     * @param string $id
     * @return bool
     */
    public function resetNotice( $id ) {
        $notes = $this->settings->get( 'notes' );
        foreach ( $notes as $key => $note ) {
            if ( $note['id'] === (int) $id ) {
                $notes[$key]['dismissed'] = false;
                $notes[$key]['time'] = time();
                $this->settings->set( 'notes', $notes );
                $this->settings->save();
                return true;
            }
        }
    }

    /**
     * Returns a notification by name.
     *
     * @param string $name
     * @return mixed|null
     */
    public function getNoticeByName( $name ) {
        $notes = $this->settings->get( 'notes' );
        return ( isset( $notes[$name] ) ? $notes[$name] : null );
    }

    /**
     * Ajax handler for dismissing notifications.
     */
    public function doDismissNotice() {
        check_ajax_referer( self::CPS_NONCE );
        if ( !current_user_can( 'administrator' ) ) {
            return wp_send_json_error( __( 'You are not allowed to perform this action.', 'custom-php-settings' ) );
        }
        if ( !filter_input( INPUT_POST, 'id', FILTER_VALIDATE_INT ) ) {
            return wp_send_json_error( __( 'No valid notification id supplied.', 'custom-php-settings' ) );
        }
        if ( !$this->dismissNotice( $_POST['id'] ) ) {
            return wp_send_json_error( __( 'Notification could not be found.', 'custom-php-settings' ) );
        }
        wp_send_json_success();
    }

    /**
     * Render any notifications.
     */
    public function renderNotices() {
        $notes = $this->settings->get( 'notes' );
        usort( $notes, function ( $a, $b ) {
            return ( $a['weight'] === $b['weight'] ? 0 : $a['weight'] - $b['weight'] );
        } );
        foreach ( $notes as $note ) {
            if ( is_callable( [$this, $note['callback']] ) && (!$note['dismissed'] || !$note['persistent'] && time() - $note['time'] > 30 * 24 * 60 * 60) ) {
                ?>
                <div id="note-<?php 
                echo $note['id'];
                ?>" class="custom-php-settings-notice notice notice-<?php 
                echo $note['type'];
                echo ( $note['dismissible'] ? ' is-dismissible' : '' );
                ?>">
                <?php 
                echo call_user_func( array($this, $note['callback']) );
                ?>
                </div>
                <?php 
            }
        }
    }

    /**
     * Adds review admin notification.
     */
    public static function addReviewNotice() {
        ?>
        <h3><?php 
        _e( 'Thank you for using Custom PHP Settings!', 'custom-php-settings' );
        ?></h3>
        <p><?php 
        echo sprintf( __( 'If you use and enjoy Custom PHP Settings, I would be really grateful if you could give it a positive review at <a href="%s" target="_blank">wordpress.org</a>.', 'custom-php-settings' ), 'https://wordpress.org/support/plugin/custom-php-settings/reviews/?rate=5#new-post' );
        ?></p>
        <p><?php 
        _e( 'Doing this would help me keeping the plugin free and up to date.', 'custom-php-settings' );
        ?></p>
        <p><?php 
        _e( 'If you are feeling generous and would like to support me, you can always buy me a coffee at:', 'custom-php-settings' );
        ?> <a target="_blank" href="https://www.buymeacoffee.com/cyclonecode">https://www.buymeacoffee.com/cyclonecode</a></p>
        <p><?php 
        _e( 'Please make sure to leave your e-mail address, and I will make sure to add you to the supporter section in the readme =)', 'custom-php-settings' );
        ?></p>
        <p><?php 
        _e( 'Thank you very much!', 'custom-php-settings' );
        ?></p>
        <?php 
    }

    /**
     * Adds support admin notification.
     */
    public static function addSupportNotice() {
        ?>
        <h3><?php 
        _e( 'Do you have any feedback or need support?', 'custom-php-settings' );
        ?></h3>
        <p><?php 
        echo sprintf( __( 'If you have any requests for improvement or just need some help. Do not hesitate to open a ticket in the <a href="%s" target="_blank">support section</a>.', 'custom-php-settings' ), 'https://wordpress.org/support/plugin/custom-php-settings/#new-topic-0' );
        ?></p>
        <p><?php 
        echo sprintf( __( 'I can also be reached by email at <a href="%s">%s</a>', 'custom-php-settings' ), 'mailto:customphpsettings@gmail.com?subject=Custom PHP Settings Support', 'customphpsettings@gmail.com' );
        ?></p>
        <p><?php 
        echo sprintf( __( 'There is also a Slack channel that you can <a target="_blank" href="%s">join</a>.', 'custom-php-settings' ), 'https://join.slack.com/t/cyclonecode/shared_invite/zt-6bdtbdab-n9QaMLM~exHP19zFDPN~AQ' );
        ?></p>
        <p><?php 
        _e( 'I hope you will have an awesome day!', 'custom-php-settings' );
        ?></p>
        <?php 
    }

    /**
     * Render admin header.
     */
    public function addHeader() {
        if ( get_current_screen()->id !== 'toplevel_page_custom-php-settings' ) {
            return;
        }
        $sectionText = array(
            'general'     => __( 'Editor', 'custom-php-settings' ),
            'backup'      => __( 'Backup', 'custom-php-settings' ),
            'apache'      => __( 'Apache Information', 'custom-php-settings' ),
            'php-info'    => __( 'PHP Information', 'custom-php-settings' ),
            'gd'          => __( 'GD Library', 'custom-php-settings' ),
            'wordpress'   => __( 'WordPress', 'custom-php-settings' ),
            'database'    => __( 'Database', 'custom-php-settings' ),
            'extensions'  => __( 'Loaded Extensions', 'custom-php-settings' ),
            'settings'    => __( 'Current PHP Settings', 'custom-php-settings' ),
            'cookie-vars' => __( '$_COOKIE Variables', 'custom-php-settings' ),
            'server-vars' => __( '$_SERVER Variables', 'custom-php-settings' ),
            'env-vars'    => __( '$_ENV Variables', 'custom-php-settings' ),
            'status'      => __( 'Status', 'custom-php-settings' ),
        );
        if ( $this->currentTab === 'info' && !empty( $this->currentSection ) ) {
            $title = ' | ' . $sectionText[$this->currentSection];
        } else {
            $title = ( $this->currentTab ? ' | ' . $sectionText[$this->currentTab] : '' );
        }
        ?>
        <div id="custom-php-settings-admin-header">
            <div><img width="64" src="<?php 
        echo plugin_dir_url( __FILE__ );
        ?>assets/icon-128x128.png" alt="<?php 
        _e( 'Custom PHP Settings', 'custom-php-settings' );
        ?>" />
                <h1><?php 
        _e( 'Custom PHP Settings', 'custom-php-settings' );
        echo $title;
        ?></h1>
            </div>
        </div>
        <?php 
    }

    /**
     * Add action link on plugins page.
     *
     * @param array $links
     * @param string $file
     *
     * @return mixed
     */
    public function addActionLinks( $links, $file ) {
        $settings_link = '<a href="' . admin_url( 'admin.php?page=' . self::MENU_SLUG ) . '">' . __( 'Settings', 'custom-php-settings' ) . '</a>';
        if ( !cps_fs()->is_free_plan() && $file === 'custom-php-settings-pro/bootstrap.php' ) {
            array_unshift( $links, $settings_link );
        }
        if ( cps_fs()->is_free_plan() && $file === 'custom-php-settings/bootstrap.php' ) {
            array_unshift( $links, $settings_link );
        }
        return $links;
    }

    /**
     * Filters the array of row meta for each plugin in the Plugins list table.
     *
     * @param string[] $plugin_meta An array of the plugin's metadata.
     * @param string   $plugin_file Path to the plugin file relative to the plugins' directory.
     * @return string[] An array of the plugin's metadata.
     */
    public function filterPluginRowMeta( array $plugin_meta, $plugin_file ) {
        if ( $plugin_file !== 'custom-php-settings/bootstrap.php' ) {
            return $plugin_meta;
        }
        $plugin_meta[] = sprintf( '<a target="_blank" href="%1$s"><span class="dashicons dashicons-star-filled" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>%2$s</a>', 'https://www.buymeacoffee.com/cyclonecode', esc_html_x( 'Sponsor', 'verb', 'custom-php-settings' ) );
        $plugin_meta[] = sprintf( '<a target="_blank" href="%1$s"><span class="dashicons dashicons-thumbs-up" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>%2$s</a>', 'https://wordpress.org/support/plugin/custom-php-settings/reviews/?rate=5#new-post', esc_html_x( 'Rate', 'verb', 'custom-php-settings' ) );
        $plugin_meta[] = sprintf( '<a target="_blank" href="%1$s"><span class="dashicons dashicons-editor-help" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>%2$s</a>', 'https://wordpress.org/support/plugin/custom-php-settings/#new-topic-0', esc_html_x( 'Support', 'verb', 'custom-php-settings' ) );
        return $plugin_meta;
    }

    /**
     * Add scripts.
     */
    public function addScripts( $hook ) {
        if ( $hook === 'toplevel_page_custom-php-settings' ) {
            // Added in WordPress 4.1.
            if ( function_exists( 'wp_enqueue_code_editor' ) && ($this->getCurrentTab() === 'general' || $this->getCurrentTab() === 'backup') ) {
                wp_enqueue_code_editor( array() );
            }
            wp_enqueue_script(
                'custom-php-settings',
                plugin_dir_url( __FILE__ ) . 'js/admin.js',
                array('jquery-effects-pulsate'),
                self::VERSION,
                true
            );
            wp_localize_script( 'custom-php-settings', 'cps_params', array(
                '_ajax_nonce' => wp_create_nonce( self::CPS_NONCE ),
                'plan'        => cps_fs()->get_plan_name(),
                'i10n'        => array(
                    'Remove'  => __( 'Remove', 'custom-php-settings' ),
                    'Restore' => __( 'Restore', 'custom-php-settings' ),
                    'Show'    => __( 'Show', 'custom-php-settings' ),
                    'Hide'    => __( 'Hide', 'custom-php-settings' ),
                ),
            ) );
        }
        wp_enqueue_style(
            'custom-php-settings',
            plugin_dir_url( __FILE__ ) . 'css/admin.css',
            array(),
            self::VERSION
        );
    }

    /**
     * Check if any updates needs to be performed.
     */
    public function checkForUpgrade() {
        if ( version_compare( $this->settings->get( 'version', '' ), self::VERSION, '<' ) ) {
            $defaults = array(
                self::FIELD_SETTINGS      => array(array(
                    self::FIELD_SETTING_NAME     => __( 'Default', 'custom-php-settings' ),
                    self::FIELD_PHP              => array(),
                    self::FIELD_ENV              => array(),
                    self::FIELD_UPDATE_CONFIG    => false,
                    self::FIELD_RESTORE_CONFIG   => false,
                    self::FIELD_TRIM_COMMENTS    => true,
                    self::FIELD_TRIM_WHITESPACES => true,
                )),
                self::FIELD_NOTIFICATIONS => array(),
            );
            $defaults['notes']['support'] = array(
                'id'          => 2,
                'weight'      => 2,
                'persistent'  => true,
                'time'        => 0,
                'type'        => 'warning',
                'name'        => 'support',
                'callback'    => 'addSupportNotice',
                'dismissed'   => true,
                'dismissible' => false,
            );
            $defaults['notes']['review'] = array(
                'id'          => 1,
                'weight'      => 1,
                'persistent'  => false,
                'time'        => 0,
                'type'        => 'info',
                'name'        => 'review',
                'callback'    => 'addReviewNotice',
                'dismissed'   => false,
                'dismissible' => true,
            );
            // Update to new settings format.
            if ( $this->settings->get( self::FIELD_VERSION ) < '2.0.0' ) {
                $currentSettings = array(array(
                    self::FIELD_SETTING_NAME     => __( 'Default', 'custom-php-settings' ),
                    self::FIELD_PHP              => $this->settings->get( self::FIELD_PHP_SETTINGS, array() ),
                    self::FIELD_ENV              => array(),
                    self::FIELD_UPDATE_CONFIG    => $this->settings->get( self::FIELD_UPDATE_CONFIG, false ),
                    self::FIELD_RESTORE_CONFIG   => $this->settings->get( self::FIELD_RESTORE_CONFIG, false ),
                    self::FIELD_TRIM_COMMENTS    => $this->settings->get( self::FIELD_TRIM_COMMENTS, true ),
                    self::FIELD_TRIM_WHITESPACES => $this->settings->get( self::FIELD_TRIM_WHITESPACES, true ),
                ));
                $this->settings->remove( self::FIELD_UPDATE_CONFIG );
                $this->settings->remove( self::FIELD_RESTORE_CONFIG );
                $this->settings->remove( self::FIELD_TRIM_COMMENTS );
                $this->settings->remove( self::FIELD_TRIM_WHITESPACES );
                $this->settings->remove( self::FIELD_PHP_SETTINGS );
                $this->settings->set( self::FIELD_SETTINGS, $currentSettings );
                $this->settings->set( self::FIELD_SETTING_INDEX, 0 );
                $this->settings->set( self::FIELD_NOTIFICATIONS, $defaults['notes'] );
            }
            $notes = $this->settings->get( self::FIELD_NOTIFICATIONS );
            // Special handling for persistent notes.
            foreach ( $defaults['notes'] as $id => $note ) {
                if ( $note['persistent'] && isset( $notes[$id] ) ) {
                    $defaults['notes'][$id]['dismissed'] = $notes[$id]['dismissed'];
                }
            }
            $this->settings->set( self::FIELD_NOTIFICATIONS, $defaults['notes'] );
            // Set defaults.
            foreach ( $defaults as $key => $value ) {
                $this->settings->add( $key, $value );
            }
            $this->settings->set( self::FIELD_VERSION, self::VERSION )->save();
        }
    }

    /**
     * Set active tab and section.
     */
    protected function setTabs() {
        $this->currentTab = ( isset( $_GET['tab'] ) ? $_GET['tab'] : 'general' );
        $this->currentSection = ( isset( $_GET['section'] ) ? $_GET['section'] : 'php-info' );
    }

    /**
     * Returns the active tab.
     *
     * @return string
     */
    protected function getCurrentTab() {
        return $this->currentTab;
    }

    /**
     * Returns the active section.
     *
     * @return string
     */
    protected function getCurrentSection() {
        return $this->currentSection;
    }

    /**
     * Triggered when plugin is activated.
     */
    public static function activate() {
    }

    /**
     * Triggered when plugin is deactivated.
     * Removes any changes in the .htaccess file made by the plugin.
     */
    public static function deActivate() {
        self::removeSettings();
    }

    /**
     * Uninstalls the plugin.
     */
    public static function delete() {
        self::removeSettings();
        delete_option( self::SETTINGS_NAME );
    }

    /**
     * Remove any current settings from either .htaccess or user ini file.
     */
    protected static function removeSettings() {
        $settings = new Settings(self::SETTINGS_NAME);
        $settingIndex = $settings->get( self::FIELD_SETTING_INDEX, 0 );
        $currentSetting = $settings->get( self::FIELD_SETTINGS )[$settingIndex];
        if ( $currentSetting[self::FIELD_RESTORE_CONFIG] ) {
            $configFile = self::getConfigFilePath();
            self::addMarker(
                $configFile,
                self::MARKER,
                array(),
                ( self::getCGIMode() ? ';' : '#' )
            );
        }
    }

    /**
     * Adds customized text to footer in admin dashboard.
     *
     * @param string $footer_text
     *
     * @return string
     */
    public function adminFooter( $footer_text ) {
        $screen = get_current_screen();
        if ( $screen->id === 'toplevel_page_custom-php-settings' ) {
            $rate_text = sprintf( __( 'Thank you for using <a href="%1$s" target="_blank">Custom PHP Settings</a>! Please <a href="%2$s" target="_blank">rate us on WordPress.org</a>', 'custom-php-settings' ), 'https://wordpress.org/plugins/custom-php-settings', 'https://wordpress.org/support/plugin/custom-php-settings/reviews/?rate=5#new-post' );
            return '<span>' . $rate_text . '</span>';
        } else {
            return $footer_text;
        }
    }

    /**
     * Add menu item for plugin.
     */
    public function addMenu() {
        add_menu_page(
            __( 'Custom PHP Settings', 'custom-php-settings' ),
            __( 'Custom PHP Settings', 'custom-php-settings' ),
            $this->capability,
            self::MENU_SLUG,
            array($this, 'doSettingsPage'),
            'dashicons-cogwheel'
        );
    }

    /**
     * Add message to be displayed in settings form.
     *
     * @param string $message
     * @param string $type
     */
    protected function addSettingsMessage( $message, $type = 'error' ) {
        add_settings_error(
            'custom-php-settings',
            esc_attr( 'custom-php-settings-updated' ),
            $message,
            $type
        );
    }

    /**
     * Check if PHP is running in CGI/Fast-CGI mode or not.
     *
     * @return bool
     */
    protected static function getCGIMode() {
        return substr( php_sapi_name(), -3 ) === 'cgi';
    }

    /**
     * Gets an array of environment variables to insert into configuration file.
     *
     * @return array
     */
    protected function getVariablesAsArray() {
        $section = array();
        $section[] = '<IfModule mod_env.c>';
        $settingIndex = $this->settings->get( self::FIELD_SETTING_INDEX, 0 );
        foreach ( $this->settings->settings[$settingIndex][self::FIELD_ENV] as $key => $variable ) {
            $name = key( $variable );
            $value = $variable[$name];
            $section[] = 'SetEnv ' . $name . ' ' . $value;
        }
        $section[] = '</IfModule>';
        return $section;
    }

    /**
     * Gets an array of settings to insert into configuration file.
     *
     * @return array
     */
    protected function getSettingsAsArray() {
        $cgiMode = $this->getCGIMode();
        $section = array();
        $settingIndex = $this->settings->get( self::FIELD_SETTING_INDEX, 0 );
        foreach ( $this->settings->settings[$settingIndex][self::FIELD_PHP] as $key => $value ) {
            if ( empty( $value ) ) {
                if ( !$this->settings->settings[$settingIndex][self::FIELD_TRIM_WHITESPACES] ) {
                    $section[] = '';
                }
            } elseif ( $value[0] === '#' ) {
                if ( !$this->settings->settings[$settingIndex][self::FIELD_TRIM_COMMENTS] ) {
                    $section[] = $value;
                }
            } else {
                $setting = explode( '=', trim( $value ) );
                $section[] = ( $cgiMode ? $setting[0] . '=' . $setting[1] : 'php_value ' . $setting[0] . ' ' . $setting[1] );
            }
        }
        return $section;
    }

    /**
     * Inserts an array of strings into a file (.htaccess ), placing it between
     * BEGIN and END markers.
     *
     * Replaces existing marked info. Retains surrounding
     * data. Creates file if none exists.
     *
     * This is a customized version of insert_with_markers in core.
     *
     * @param string $filename Filename to alter.
     * @param string $marker The marker to alter.
     * @param array|string $insertion The new content to insert.
     * @param string $comment Type of character to use for comments.
     * @return bool True on write success, false on failure.
     */
    protected static function addMarker(
        $filename,
        $marker,
        $insertion,
        $comment = '#'
    ) {
        if ( !is_array( $insertion ) ) {
            $insertion = explode( "\n", $insertion );
        }
        $start_marker = "{$comment} BEGIN {$marker}";
        $end_marker = "{$comment} END {$marker}";
        $fp = @fopen( $filename, 'r+' );
        if ( !$fp ) {
            return false;
        }
        // Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired.
        flock( $fp, LOCK_EX );
        $lines = array();
        while ( !feof( $fp ) ) {
            $lines[] = rtrim( fgets( $fp ), "\r\n" );
        }
        // Split out the existing file into the preceding lines, and those that appear after the marker
        $pre_lines = $post_lines = $existing_lines = array();
        $found_marker = $found_end_marker = false;
        foreach ( $lines as $line ) {
            if ( !$found_marker && false !== strpos( $line, $start_marker ) ) {
                $found_marker = true;
                continue;
            } elseif ( !$found_end_marker && false !== strpos( $line, $end_marker ) ) {
                $found_end_marker = true;
                continue;
            }
            if ( !$found_marker ) {
                $pre_lines[] = $line;
            } elseif ( $found_marker && $found_end_marker ) {
                $post_lines[] = $line;
            } else {
                $existing_lines[] = $line;
            }
        }
        // Check to see if there was a change
        if ( $existing_lines === $insertion ) {
            flock( $fp, LOCK_UN );
            fclose( $fp );
            return true;
        }
        // Generate the new file data
        $new_file_data = implode( "\n", array_merge(
            $pre_lines,
            array($start_marker),
            $insertion,
            array($end_marker),
            $post_lines
        ) );
        // Write to the start of the file, and truncate it to that length
        fseek( $fp, 0 );
        $bytes = fwrite( $fp, $new_file_data );
        if ( $bytes ) {
            ftruncate( $fp, ftell( $fp ) );
        }
        fflush( $fp );
        flock( $fp, LOCK_UN );
        fclose( $fp );
        return (bool) $bytes;
    }

    /**
     * Try to store settings in either .htaccess or .ini file.
     */
    protected function updateConfigFile( $fileName = null ) {
        $configFile = ( $fileName ?: self::getConfigFilePath() );
        if ( self::createIfNotExist( $configFile ) === false ) {
            /* translators: %s: Name of configuration file */
            $this->addSettingsMessage( sprintf( __( '%s does not exists or is not writable.', 'custom-php-settings' ), $configFile ) );
            return;
        }
        $section = $this->getSettingsAsArray();
        if ( !$fileName ) {
            /* translators: %s: Name of configuration file */
            $message = sprintf( __( 'Settings updated and stored in %s.', 'custom-php-settings' ), $configFile );
            if ( self::getCGIMode() ) {
                $message .= '<br />' . sprintf( __( 'You may need to wait for up to %d seconds before any changes takes effect.', 'custom-php-settings' ), ini_get( 'user_ini.cache_ttl' ) );
            }
            $this->addSettingsMessage( $message, 'updated' );
        }
        self::addMarker(
            $configFile,
            self::MARKER,
            $section,
            ( self::getCGIMode() ? ';' : '#' )
        );
    }

    /**
     * Check so file exists and is writable.
     *
     * @param string $filename
     *
     * @return bool
     */
    protected static function createIfNotExist( $filename ) {
        $fp = null;
        if ( !file_exists( $filename ) ) {
            if ( !is_writable( dirname( $filename ) ) ) {
                return false;
            }
            if ( !touch( $filename ) ) {
                return false;
            }
            // Make sure the file is created with a minimum set of permissions.
            $perms = fileperms( $filename );
            if ( $perms ) {
                chmod( $filename, $perms | 0644 );
            }
        } elseif ( !($fp = @fopen( $filename, 'a' )) ) {
            return false;
        }
        if ( $fp ) {
            fclose( $fp );
        }
    }

    /**
     * Return information about the WordPress environment.
     *
     * @return array
     */
    protected function getWordPressInfo() {
        $fields = array(
            __( 'Version', 'custom-php-settings' )                 => 'version',
            __( 'Name', 'custom-php-settings' )                    => 'name',
            __( 'Description', 'custom-php-settings' )             => 'description',
            __( 'WordPress Address (URL)', 'custom-php-settings' ) => 'wpurl',
            __( 'Site Address (URL)', 'custom-php-settings' )      => 'url',
            __( 'Stylesheet URL', 'custom-php-settings' )          => 'stylesheet_url',
            __( 'Stylesheet Directory', 'custom-php-settings' )    => 'stylesheet_directory',
            __( 'Template URL', 'custom-php-settings' )            => 'template_url',
            __( 'Pingback URL', 'custom-php-settings' )            => 'pingback_url',
            __( 'Admin Email', 'custom-php-settings' )             => 'admin_email',
            __( 'Charset', 'custom-php-settings' )                 => 'charset',
            __( 'HTML Type', 'custom-php-settings' )               => 'html_type',
            __( 'Language', 'custom-php-settings' )                => 'language',
        );
        $data = array();
        foreach ( $fields as $key => $field ) {
            $data[$key] = get_bloginfo( $field );
        }
        $data = array_merge( $data, array(
            __( 'Text Direction', 'custom-php-settings' )   => ( is_rtl() ? 'rtl' : 'ltr' ),
            __( 'Multisite', 'custom-php-settings' )        => __( ( is_multisite() ? 'yes' : 'no' ), 'custom-php-settings' ),
            __( 'Debug mode', 'custom-php-settings' )       => __( ( WP_DEBUG ? 'yes' : 'no' ), 'custom-php-settings' ),
            __( 'Memory limit', 'custom-php-settings' )     => WP_MEMORY_LIMIT,
            __( 'Cron', 'custom-php-settings' )             => __( ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ? 'disabled' : 'enabled' ), 'custom-php-settings' ),
            __( 'Timezone', 'custom-php-settings' )         => wp_timezone_string(),
            __( 'Development Mode', 'custom-php-settings' ) => wp_get_development_mode(),
            __( 'Environment Type', 'custom-php-settings' ) => wp_get_environment_type(),
            __( 'Server Software', 'custom-php-settings' )  => ( isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : 'N/A' ),
        ) );
        return $data;
    }

    /**
     * Returns information about the database environment.
     *
     * @return array
     */
    protected function getDatabaseInfo() {
        global $wpdb;
        $host = $wpdb->dbhost;
        $host = explode( ':', $host );
        $port = 3306;
        if ( count( $host ) > 1 ) {
            $port = (int) $host[1];
        }
        $host = $host[0];
        return array(
            __( 'Version', 'custom-php-settings' )   => $wpdb->db_server_info(),
            __( 'Database', 'custom-php-settings' )  => $wpdb->dbname,
            __( 'User', 'custom-php-settings' )      => $wpdb->dbuser,
            __( 'Password', 'custom-php-settings' )  => $wpdb->dbpassword,
            __( 'Host', 'custom-php-settings' )      => $host,
            __( 'Port', 'custom-php-settings' )      => $port,
            __( 'Prefix', 'custom-php-settings' )    => $wpdb->get_blog_prefix( get_current_blog_id() ),
            __( 'Charset', 'custom-php-settings' )   => $wpdb->charset,
            __( 'Collation', 'custom-php-settings' ) => $wpdb->collate,
        );
    }

    /**
     * Validates so a line is either a comment, blank line or a valid setting.
     *
     * @param string $setting
     *
     * @return int
     */
    protected function isValidSetting( $setting ) {
        $iniSettings = array_keys( $this->getIniSettings() );
        $setting = explode( '=', preg_replace( '/("[^"\\r\\n]+")|\\s*/', '\\1', html_entity_decode( $setting ) ) );
        // Lock down for specific settings for the free version.
        $lockedSettings = array();
        $lockedSettings = [
            'max_file_uploads',
            'max_input_vars',
            'max_input_time',
            'post_max_size'
        ];
        $iniSettings = array_filter( $iniSettings, function ( $setting ) use($lockedSettings) {
            return !in_array( $setting, $lockedSettings );
        } );
        if ( count( $setting ) === 1 ) {
            if ( strlen( $setting[0] ) === 0 ) {
                // This is a blank line.
                return 2;
            } elseif ( $setting[0][0] === '#' ) {
                // This is a comment.
                return 2;
            } elseif ( in_array( $setting[0], $iniSettings ) ) {
                /* translators: %s: Name of PHP setting */
                $this->addSettingsMessage( sprintf( __( '%s must be in the format: key=value', 'custom-php-settings' ), $setting[0] ) . '<br />' );
                return -2;
            }
        } elseif ( count( $setting ) === 2 ) {
            if ( !cps_fs()->is__premium_only() && in_array( $setting[0], $lockedSettings ) ) {
                /* translators: %s: Name of PHP setting */
                $this->addSettingsMessage( sprintf( __( 'You need to use the <a href="' . cps_fs()->get_upgrade_url() . '">premium version</a> to change the %s value.', 'custom-php-settings' ), $setting[0] ) . '<br />' );
                return -1;
            }
            if ( in_array( $setting[0], $iniSettings ) ) {
                // This is a valid setting.
                return 1;
            } elseif ( $setting[0][0] === '#' ) {
                // this is a comment
                return 2;
            }
        }
        /* translators: %s: Name of PHP setting */
        $this->addSettingsMessage( sprintf( __( '%s is not a valid setting.', 'custom-php-settings' ), $setting[0] ) . '<br />' );
        return -1;
    }

    /**
     * Check if a setting with the specific name exists.
     *
     * @param string $name
     * @return bool
     */
    protected function settingExists( $name ) {
        $names = array_map( function ( $setting ) {
            return $setting[self::FIELD_SETTING_NAME];
        }, $this->settings->get( self::FIELD_SETTINGS ) );
        return in_array( $name, $names );
    }

    /**
     * Returns the current active setting array.
     *
     * @return mixed
     */
    protected function getCurrentSetting() {
        $settingIndex = $this->settings->get( self::FIELD_SETTING_INDEX, 0 );
        return $this->settings->get( self::FIELD_SETTINGS )[$settingIndex];
    }

    /**
     * Handle form data for configuration page.
     */
    public function saveSettings() {
        // Verify nonce and referer.
        check_admin_referer( 'custom-php-settings-action', 'custom-php-settings-nonce' );
        // Validate so user has correct privileges.
        if ( !current_user_can( $this->capability ) ) {
            die( __( 'You are not allowed to perform this action.', 'custom-php-settings' ) );
        }
        if ( filter_input( INPUT_POST, 'custom-php-settings', FILTER_UNSAFE_RAW ) ) {
            // Filter and sanitize form values.
            $settings = array();
            $raw_settings = filter_input( INPUT_POST, 'settings', FILTER_UNSAFE_RAW );
            $raw_settings = array_map( 'trim', explode( PHP_EOL, trim( $raw_settings ) ) );
            foreach ( $raw_settings as $key => $value ) {
                if ( ($type = $this->isValidSetting( $value )) > 0 ) {
                    if ( $type === 1 ) {
                        // Remove whitespaces in everything but quotes.
                        $setting = explode( '=', preg_replace( '/("[^"\\r\\n]+")|\\s*/', '\\1', html_entity_decode( $value ) ) );
                        $settings[$key] = str_replace( ';', '', implode( '=', $setting ) );
                    } else {
                        $settings[$key] = str_replace( ';', '', $value );
                    }
                }
            }
            $currentSettings = $this->settings->get( self::FIELD_SETTINGS );
            $settingIndex = $this->settings->get( self::FIELD_SETTING_INDEX, 0 );
            $currentSettings[$settingIndex][self::FIELD_PHP] = $settings;
            $currentSettings[$settingIndex][self::FIELD_UPDATE_CONFIG] = (bool) filter_input( INPUT_POST, 'update_config', FILTER_VALIDATE_BOOLEAN );
            $currentSettings[$settingIndex][self::FIELD_RESTORE_CONFIG] = (bool) filter_input( INPUT_POST, 'restore_config', FILTER_VALIDATE_BOOLEAN );
            $currentSettings[$settingIndex][self::FIELD_TRIM_COMMENTS] = (bool) filter_input( INPUT_POST, 'trim_comments', FILTER_VALIDATE_BOOLEAN );
            $currentSettings[$settingIndex][self::FIELD_TRIM_WHITESPACES] = (bool) filter_input( INPUT_POST, 'trim_whitespaces', FILTER_VALIDATE_BOOLEAN );
            $this->settings->set( self::FIELD_SETTINGS, $currentSettings );
            $this->settings->save();
            if ( $currentSettings[$settingIndex][self::FIELD_UPDATE_CONFIG] ) {
                $this->updateConfigFile();
            }
            // Check if we should activate the support notification.
            if ( ($notice = $this->getNoticeByName( 'support' )) && $notice['time'] === 0 ) {
                $this->resetNotice( $notice['id'] );
            }
        }
        set_transient( 'cps_settings_errors', get_settings_errors() );
        wp_safe_redirect( wp_get_referer() );
    }

    /**
     * Returns absolute path to configuration file.
     */
    protected static function getConfigFilePath() {
        return get_home_path() . (( self::getCGIMode() ? ini_get( 'user_ini.filename' ) : '.htaccess' ));
    }

    /**
     * Get all non-system settings.
     *
     * @return array
     */
    protected function getIniSettings() {
        return array_filter( ini_get_all(), function ( $item ) {
            return $item['access'] !== INI_SYSTEM;
        } );
    }

    /**
     * Display the settings page.
     */
    public function doSettingsPage() {
        // Display any settings messages
        $setting_errors = get_transient( 'cps_settings_errors' );
        if ( $setting_errors ) {
            foreach ( $setting_errors as $error ) {
                $this->addSettingsMessage( $error['message'], $error['type'] );
            }
            delete_transient( 'cps_settings_errors' );
        }
        if ( $this->getCurrentTab() === 'info' && $this->getCurrentSection() ) {
            $template = __DIR__ . '/views/cps-' . $this->currentSection . '.php';
        } else {
            $template = __DIR__ . '/views/cps-' . $this->currentTab . '.php';
        }
        if ( file_exists( $template ) ) {
            require_once $template;
        }
    }

    /**
     * Format bytes
     *
     * @param $bytes
     * @param int $precision
     * @return string
     */
    public function formatBytes( $bytes, $precision = 2 ) {
        $units = array(
            'B',
            'KB',
            'MB',
            'GB',
            'TB'
        );
        $bytes = max( $bytes, 0 );
        $pow = floor( (( $bytes ? log( $bytes ) : 0 )) / log( 1024 ) );
        $pow = min( $pow, count( $units ) - 1 );
        $bytes /= pow( 1024, $pow );
        // $bytes /= (1 << (10 * $pow));
        return round( $bytes, $precision ) . ' ' . $units[$pow];
    }

}