Edit file File name : class.A2OptDrupal8.php Content :<?php class A2OptDrupal8 extends A2OptBase { public $app; public $db_connection; public $db_host; public $db_prefix; public $db_name; public $db_user; public $db_passwd; public $memcached_socket; public $db_port; public $is_cagefs; public $dr_ver; public function __construct($app) { global $cpanel; $this->exit_on_error = false; $this->db_connection = new MySQLi(); $this->db_name = ''; $this->db_host = ''; $this->db_prefix = ''; $this->db_user = ''; $this->db_port = '3306'; $this->db_password = ''; $this->app = $app; $this->path = $app->path; $this->domain = $app->domain; $this->dr_ver = $app->dr_ver; $this->type = 'Drupal'; $this->db_connect(); if ($this->dr_ver == 9) { $this->no_memcached = true; $this->type = 'Drupal 9'; } if (is_null($this->db_connection)) { $this->dr_log('Drupal8 Unable to connect to DB ' . $this->path); $this->log_action('Unable to connect to DB ' . $this->path); $this->type = null; return; } $this->title = $this->check_app_title($this->get_site_name(), $app); $this->set_instance(); $this->is_cagefs = $this->is_cagefs(); $this->exit_on_error = true; } public function dr_log($data) { // @fixme this was in for debugging as $this->log_action is not functioning while in this class. $this->home is also not available so we cannot put the logs in the user's directory without hard-coding. // if support has flagged this account for logging, then we check if a file exists for today, create if not /* $log = $this->home . '/.a2opt/a2opt-' . date('mdy') . '.log'; touch($log); // append supplied data to the log file $handle = fopen($log, 'a') or die('Cannot open file: ' . $log); fwrite($handle, date('Y-m-d H:i:s') . ':::A2OptDrupal8:::' . $data . "\n"); */ return; } /* * Utility */ private function shell_exec($command, $y = false) { if ($this->is_cagefs) { return shell_exec("/bin/cagefs_enter.proxied $command"); } else { return shell_exec("$command"); } } private function get_site_name() { $system_site = $this->get_variable('system.site'); return $system_site['name']; } private function cache_clear($all = true) { if ($all) { $res = $this->shell_exec('drush cache-rebuild all'); } else { $res = $this->shell_exec('drush cache-rebuild'); } } private function is_module_enabled($name = '') { $this->dr_log('is_module_enabled ' . $name); if ($name == '') { return false; } chdir($this->path); $res = $this->shell_exec('drush pm-list --type=module --status=enabled 2>&1'); if (strpos($res, $name) !== false) { $this->dr_log('is_module_enabled ' . $name . ' true'); return true; } return false; } private function get_memcache_settings() { return [ '$settings[\'cache\'][\'default\'] = \'cache.backend.memcache\';', '$settings[\'memcache\'][\'servers\'] = [\'' . $memcached_socket . '\' => \'default\'];', '$settings[\'memcache\'][\'bins\'] = [\'default\' => \'default\'];', '$settings[\'memcache\'][\'key_hash_algorithm\'] = \'sha1\';', ]; } private function get_settings_file() { $settings_file = "{$this->path}/sites/default/settings.php"; if (file_exists($settings_file)) { return file_get_contents($settings_file); } return false; } private function save_settings_file($settings_data) { $settings_file = "{$this->path}/sites/default/settings.php"; if (file_exists($settings_file)) { $this->dr_log('save_settings_file: have settings'); //chmod to writeable chmod($settings_file, 0644); $this->dr_log('save_settings_file: made writable'); //fwrite to file $handle = fopen($settings_file, 'w'); if ($handle == false) { $this->dr_log('save_settings_file: Cannot open file: ' . $settings_file); return false; } $this->dr_log('save_settings_file: got handle'); fwrite($handle, $settings_data); $this->dr_log('save_settings_file: wrote file'); //chmod to readonly chmod($settings_file, 0444); $this->dr_log('save_settings_file: made ro'); return true; } return false; } /* * Check, enable, disable methods for optimizations */ public function is_core_file_cache_enabled() { $system_performance = $this->get_variable('system.performance'); if ($system_performance['cache']['page']['max_age'] > 0) { return true; } } public function enable_core_file_cache() { $system_performance = $this->get_variable('system.performance'); $system_performance['cache']['page']['max_age'] = 3600; $this->set_variable('system.performance', $system_performance); } public function disable_core_file_cache() { $system_performance = $this->get_variable('system.performance'); $system_performance['cache']['page']['max_age'] = 0; $this->set_variable('system.performance', $system_performance); } public function is_core_css_cache_enabled() { $system_performance = $this->get_variable('system.performance'); return $system_performance['css']['preprocess']; } public function enable_core_css_cache() { $system_performance = $this->get_variable('system.performance'); $system_performance['css']['preprocess'] = true; $this->set_variable('system.performance', $system_performance); } public function disable_core_css_cache() { $system_performance = $this->get_variable('system.performance'); $system_performance['css']['preprocess'] = false; $this->set_variable('system.performance', $system_performance); } public function is_core_js_cache_enabled() { $system_performance = $this->get_variable('system.performance'); return $system_performance['js']['preprocess']; } public function enable_core_js_cache() { $system_performance = $this->get_variable('system.performance'); $system_performance['js']['preprocess'] = true; $this->set_variable('system.performance', $system_performance); } public function disable_core_js_cache() { $system_performance = $this->get_variable('system.performance'); $system_performance['js']['preprocess'] = false; $this->set_variable('system.performance', $system_performance); } public function is_logging_minimized() { $dblog_settings = $this->get_variable('dblog.settings'); $limit = $dblog_settings['row_limit']; return !($limit === false || intval($limit) > 101); } public function enable_minimized_logging() { $dblog_settings = $this->get_variable('dblog.settings'); $dblog_settings['row_limit'] = 100; $this->set_variable('dblog.settings', $dblog_settings); } public function disable_minimized_logging() { $dblog_settings = $this->get_variable('dblog.settings'); $dblog_settings['row_limit'] = 1000; $this->set_variable('dblog.settings', $dblog_settings); } public function is_db_maintenance() { $db_maint = $this->get_variable('db_maintenance.settings'); $this->dr_log('is_db_maintenance'); if ($db_maint) { if ( ( // Check that at least some tables are set to be optimized // We default to all tables, but user can override to just some $db_maint['all_tables'] === 1 || ( is_array($db_maint['table_list_' . $this->db_name]) && count($db_maint['table_list_' . $this->db_name] > 0) ) ) && $db_maint['write_log'] === 0 && // Do not want to log OPTIMIZE queries $db_maint['cron_frequency'] == '2592000' && // Optimize monthly $this->is_module_enabled('db_maintenance') ) { $this->dr_log('is_db_maintenance true'); return true; } else { $this->dr_log('is_db_maintenance false'); $this->dr_log('is_db_maintenance write_log: ' . $db_maint['write_log'] . ' cron_frequency: ' . $db_maint['cron_frequency'] . ' is_module_enabled: ' . $this->is_module_enabled('db_maintenance')); return false; } } $this->dr_log('is_db_maintenance no variable'); return false; } public function enable_db_maintenance() { chdir($this->path); $res = $this->shell_exec('drush dl db_maintenance -y'); unset($res); $res = $this->shell_exec('drush en db_maintenance -y'); unset($res); $db_maint = $this->get_variable('db_maintenance.settings'); $db_maint['all_tables'] = 1; $db_maint['write_log'] = 0; $db_maint['cron_frequency'] = 2592000; $this->set_variable('db_maintenance.settings', $db_maint); $this->cache_clear(); } public function disable_db_maintenance() { chdir($this->path); $res = $this->shell_exec('drush pm-uninstall db_maintenance -y'); unset($res); $this->cache_clear(); return true; } public function is_memcached_enabled() { if ($this->is_module_enabled('memcache') == false) { $this->dr_log('is_memcached_enabled: module check failed'); return false; } $this->dr_log('is_memcached_enabled'); if (isset($this->instance->socket)) { $this->dr_log('is_memcached_enabled: Have instance->socket'); $this->memcached_socket = $this->instance->socket; } elseif (isset($socket) && $socket != '') { $this->dr_log('is_memcached_enabled: Have $socket'); $this->memcached_socket = $socket; } else { $this->dr_log('is_memcached_enabled: no instance or socket'); return false; } $settings_file = $this->get_settings_file(); $memcache_settings = $this->get_memcache_settings(); foreach ($memcache_settings as $setting) { if (strpos($settings_file, $setting) === false) { return false; } } return true; } public function enable_memcached($socket = '') {//Drupal $this->check_app_path(); $this->dr_log('enable_memcached'); chdir($this->path); $res = $this->shell_exec('drush dl memcache -y'); unset($res); $res = $this->shell_exec('drush en memcache -y'); unset($res); $this->cache_clear(); if (isset($this->instance->socket)) { $this->dr_log('enable_memcached: have instance->socket'); $this->memcached_socket = $this->instance->socket; } elseif (isset($socket) && $socket != '') { $this->dr_log('enable_memcached: have $socket'); $this->memcached_socket = $socket; } else { $this->dr_log('enable_memcached: no socket or instance'); die('no Memcached socket given'); } $settings_file = $this->get_settings_file(); $memcache_defaults = $this->get_memcache_settings(); $memcache_string = implode("\n", $memcache_defaults); $settings_file .= $memcache_string; if ($this->save_settings_file($settings_file)) { $this->app->swiftcache->memcached->enabled = true; } } public function disable_memcached() { $this->check_app_path(); $res = $this->shell_exec('drush pm-uninstall memcache -y'); $this->app->swiftcache->memcached->enabled = false; $settings_file = $this->get_settings_file(); $settings_file = preg_replace('/^.*?\$settings\[\'(mem)?cache\'].*?$/m', '', $settings_file); if ($this->save_settings_file($settings_file)) { return true; } return false; } public function get_optimizations() { $thisclass = $this; $optimizations = [ 'Core File Cache' => [ 'optimized' => $this->is_core_file_cache_enabled(), 'description' => "Cache pages for anonymous users. To learn more: visit the <a href=\"https://www.a2hosting.com/kb/installable-applications/optimization-and-configuration/drupal2/optimizing-drupal\" target='_blank'>Knowledge Base</a>", 'enable' => function () use (&$thisclass) { $thisclass->enable_core_file_cache(); }, 'disable' => function () use (&$thisclass) { $thisclass->disable_core_file_cache(); }, 'type' => 'core' ], 'Aggregate CSS' => [ 'optimized' => $this->is_core_css_cache_enabled(), 'description' => "Combine and compress CSS files. To learn more: visit the <a href=\"https://www.a2hosting.com/kb/installable-applications/optimization-and-configuration/drupal2/optimizing-drupal\" target='_blank'>Knowledge Base</a>", 'enable' => function () use (&$thisclass) { $thisclass->enable_core_css_cache(); }, 'disable' => function () use (&$thisclass) { $thisclass->disable_core_css_cache(); }, 'type' => 'core' ], 'Aggregate JavaScript' => [ 'optimized' => $this->is_core_js_cache_enabled(), 'description' => "Combine and compress JavaScript files. To learn more: visit the <a href=\"https://www.a2hosting.com/kb/installable-applications/optimization-and-configuration/drupal2/optimizing-drupal\" target='_blank'>Knowledge Base</a>", 'enable' => function () use (&$thisclass) { $thisclass->enable_core_js_cache(); }, 'disable' => function () use (&$thisclass) { $thisclass->disable_core_js_cache(); }, 'type' => 'core' ], 'Minimize Database Logging' => [ 'optimized' => $this->is_logging_minimized(), 'description' => "Log only the last 100 messages to the database. To learn more: visit the <a href=\"https://www.a2hosting.com/kb/installable-applications/optimization-and-configuration/drupal2/optimizing-drupal\" target='_blank'>Knowledge Base</a>", 'enable' => function () use (&$thisclass) { $thisclass->enable_minimized_logging(); }, 'disable' => function () use (&$thisclass) { $thisclass->disable_minimized_logging(); }, 'type' => 'core' ], 'Update Drupal Daily' => [ 'optimized' => $this->is_update_cron(), 'description' => 'Disable the Update Manager module and use a daily cron to automatically update drupal and its modules instead.', 'enable' => function () use (&$thisclass) { $thisclass->disable_update_manager(); $thisclass->enable_update_cron(); }, 'disable' => function () use (&$thisclass) { $thisclass->enable_update_manager(); $thisclass->disable_update_cron(); }, 'type' => 'cron' ], 'DB Maintenance Module' => [ 'optimized' => $this->is_db_maintenance(), 'description' => 'DB maintenance optimizes selected tables in the database during regular intervals.', 'enable' => function () use (&$thisclass) { $thisclass->enable_db_maintenance(); }, 'disable' => function () use (&$thisclass) { $thisclass->disable_db_maintenance(); }, 'type' => 'module' ], 'Memcached' => [ 'optimized' => $this->is_memcached_enabled(), 'description' => 'Caching of database query results in memory', 'controller' => 'Memcached', 'advanced' => true ], 'TurboCache' => [ 'optimized' => $this->is_litespeed_enabled(), 'description' => 'Static file caching by the web Server', 'controller' => 'TurboCache', 'advanced' => true ] ]; if ($this->dr_ver == 9) { unset($optimizations['DB Maintenance Module']); unset($optimizations['Memcached']); } return $optimizations; } public function get_percent_optimized() { $optimizations = $this->get_optimizations(); $count = 0; foreach ($optimizations as $name => $optimization) { if ($optimization['optimized']) { $count ++; } } return round(($count / (count($optimizations))) * 100); } public function set_optimization($name, $optimize) { $optimizations = $this->get_optimizations(); if (array_key_exists($name, $optimizations)) { if (isset($optimizations[$name]['type'])) { switch ($optimize) { case true: if (isset($optimizations[$name]['enable'])) { $optimizations[$name]['enable'](); } break; case false: if (isset($optimizations[$name]['disable'])) { $optimizations[$name]['disable'](); } break; } } } return $this->get_optimizations(); } public function optimize() { chdir($this->path); $this->enable_drush(); $optimizations = $this->get_optimizations(); foreach ($optimizations as $name => $optimization) { if (!$optimization['optimized']) { $this->set_optimization($name, true); } } } public function enable_drush() { if (!file_exists("{$this->path}/drush")) { copy('/opt/a2-optimized/drupal/drush', "{$this->path}/drush"); } if (!is_executable("{$this->path}/drush")) { chmod("{$this->path}/drush", 0770); } } public function disable_update_manager() { chdir($this->path); $res = $this->shell_exec('drush pm-uninstall update -y'); unset($res); $this->cache_clear(); return true; } public function enable_update_manager() { chdir($this->path); $res = $this->shell_exec('drush en update -y'); unset($res); $this->cache_clear(); return true; } public function disable_update_cron() { global $cpanel; if ($this->is_update_cron()) {//set the cron if missing if ($this->is_cpanel()) { $count = $this->get_update_cron_id(); //set the cron in cPanel $res = $cpanel->api2( 'Cron', 'remove_line', [ 'line' => $count, ] ); unset($res); } else { $cmd = str_replace('/', "\/", preg_quote(rtrim($this->app->path, '/') . '/drush up')); shell_exec("crontab -l | sed 's/[^\\n\\r]*{$cmd}[^\\n\\r]*//' | crontab"); } } return !$this->is_update_cron(); } public function enable_update_cron() { global $cpanel; if (!$this->is_update_cron()) {//set the cron if missing //set up random times to execute the cron once per week $hour = rand(0, 23); $minute = rand(0, 59); if ($this->is_cagefs()) { $command = "/usr/bin/cagefs_enter.proxied cd {$this->app->path}; {$this->app->path}/drush up >/dev/null 2>&1"; } else { $command = "cd {$this->app->path}; {$this->app->path}/drush up >/dev/null 2>&1"; } if ($this->is_cpanel()) { //set the cron in cPanel $res = $cpanel->api2( 'Cron', 'add_line', [ 'command' => $command, 'day' => '*', 'hour' => "{$hour}", 'minute' => "{$minute}", 'month' => '*', 'weekday' => '*', ] ); unset($res); } else { $cron = "{$minute} {$hour} * * * $command"; shell_exec("(crontab -l ; echo '{$cron}') | sort | uniq | crontab"); } } return $this->is_update_cron(); } public function get_update_cron_id() { global $cpanel; $path = trim("{$this->app->path}", '/'); if ($this->is_cpanel()) { $res = $cpanel->api2('Cron', 'listcron'); $res = $res['cpanelresult']['data']; foreach ($res as $i => $line) { if (isset($line['command'])) { $command = $line['command']; if (!(strpos($command, "{$path}/drush up") === false)) { return $line['count']; } } } } else { exec('crontab -l', $res); foreach ($res as $i => $line) { if ( !(strpos($line, "{$path}/drush up") === false)) { return $i; } } } return false; } public function is_update_cron() { if ($this->is_module_enabled('update') == false && $this->get_update_cron_id()) { return true; } } /* The previous regex in db_connect was choking when db credentials had a '*' in it (passwords) orig: if(preg_match("/(\\\$databases[^*]*\\);)/msiU", $settings, $matches )){ */ public function db_connect() { $this->dr_log('db_connect 8 : ' . $this->path); if (file_exists("{$this->path}/sites/default/settings.php")) { $settings = file_get_contents("{$this->path}/sites/default/settings.php"); $settings = str_replace('$databases = [];', '', $settings); $settings = str_replace('$databases = array();', '', $settings); if (preg_match('/^\s*(\$databases.+\)\;)$/Uims', $settings, $matches )) { $db_array = array_pop($matches); $this->parse_db($db_array); $this->dr_log('Connecting to the Drupal database host: ' . $this->db_host . ', user: ' . $this->db_user); try { $this->db_connection = new MySQLi($this->db_host, $this->db_user, $this->db_passwd, $this->db_name); if ($this->db_connection->connect_errno) { $this->error('Could not connect to the Drupal database: ' . $this->db_connection->connect_error); $this->db_connection = null; } } catch (Exception $e) { $this->error('Could not connect to the Drupal database'); $this->db_connection = null; } } else { $this->error('Invalid Drupal database settings'); $this->db_connection = null; } } else { $this->error('Invalid Drupal database settings'); $this->db_connection = null; } } public function parse_db($db_array) { if (preg_match('/[\'"]database[\'"].*[\'"]([0-9a-zA-Z\$_]*)[\'"].*,/', $db_array, $db_match)) { $this->db_name = array_pop($db_match); } if (preg_match('/[\'"]username[\'"].*=>.*[\'"](.*)[\'"],/', $db_array, $db_match)) { $this->db_user = array_pop($db_match); } if (preg_match('/[\'"]password[\'"].*=>.*[\'"](.*)[\'"],/', $db_array, $db_match)) { $this->db_passwd = array_pop($db_match); } if (preg_match('/[\'"]host[\'"].*=>.*[\'"](.*)[\'"],/', $db_array, $db_match)) { $this->db_host = array_pop($db_match); } if (preg_match('/[\'"]prefix[\'"].*=>.*[\'"]([0-9a-zA-Z\$_]*)[\'"],/', $db_array, $db_match)) { $this->db_prefix = array_pop($db_match); } if (preg_match('/[\'"]prefix[\'"].*=>.*[\'"]([0-9]*)[\'"],/', $db_array, $db_match)) { $this->db_port = array_pop($db_match); } if (empty($this->db_port)) { $this->db_port = '3306'; } } public function get_table($name) { try { $pdo = new A2OptPDO($this->db_host, $this->db_user, $this->db_passwd, $this->db_name); } catch (PDOException $e) { $this->error('Could not connect to the database.'); return false; } $var = "{$this->db_prefix}{$name}"; $table = ''; $stmt = $pdo->prepare('SHOW Tables LIKE ?'); $stmt->execute([$var]); if ($res = $stmt->fetchAll()) { $table = $res[0][0]; } else { $var = "%{$name}"; $stmt = $pdo->prepare('SHOW Tables LIKE ?'); $stmt->execute([$var]); if ($res = $stmt->fetchAll()) { $table = $res[0][0]; } } return $table; } public function get_variable($name = 'site_name') { $this->dr_log('Drupal get_variable ' . $name . ' for ' . $this->path); $value = false; try { $pdo = new A2OptPDO($this->db_host, $this->db_user, $this->db_passwd, $this->db_name); } catch (PDOException $e) { $this->error('Could not connect to the database.'); $this->dr_log('Drupal get_variable could not connect to ' . $this->path); return false; } $table_name = $this->get_table('config'); if (!$table_name) { $this->dr_log('Drupal get_variable no table ' . $this->path); return false; } $stmt = $pdo->prepare("select data from {$table_name} where name=?"); $stmt->execute([$name]); if ($res = $stmt->fetchAll()) { $result = unserialize($res[0]['data']); // This is not advisable in PHP7, readdress if cPanel moves to PHP7+ for it's runtime return $result; } return false; } public function set_variable($name = null, $value = null) { if (is_null($value) || is_null($name)) { return false; } $value = serialize($value); $table_name = $this->get_table('config'); if (!$table_name) { return false; } try { $pdo = new A2OptPDO($this->db_host, $this->db_user, $this->db_passwd, $this->db_name); } catch (PDOException $e) { $this->error('Could not connect to the database.'); return false; } $stmt = $pdo->prepare("update {$table_name} set data=? where name=?"); $stmt->execute([$value,$name]); if ($stmt->rowCount() == 1) { $this->cache_clear(); return true; } else { $stmt = $pdo->prepare("insert into {$table_name} set data=?, name=?"); $stmt->execute([$value,$name]); if ($stmt->rowCount() == 1) { $this->cache_clear(); return true; } } return false; } public function is_litespeed_enabled() { return $this->is_module_enabled('lite_speed_cache'); } public function get_turbocache_defaults() { return [ 'reject_urls' => [ 'admin', 'login', 'register' ], 'reject_cookies' => ['SESS'], 'time' => '15 Minutes', 'ttl' => 900, 'enabled' => true ]; } } Save