sgd_dashboard-1.0.0-beta1/src/Plugin/SgdCompanion/SgdCompanionPhpStatus.php
src/Plugin/SgdCompanion/SgdCompanionPhpStatus.php
<?php
namespace Drupal\sgd_dashboard\Plugin\SgdCompanion;
use Drupal\sgd_dashboard\SgdCompanionPluginBase;
/**
* Provides a SGD Companion plugin.
*
* @SgdCompanion(
* id = "sgd_companion_php_status",
* )
*/
class SgdCompanionPhpStatus extends SgdCompanionPluginBase {
/**
* {@inheritdoc}
*/
public function canProcessStatus($statusData) : bool {
// If the Site Guardian API Companion module 'PHP status' is installed on
// the site we should have data from it.
// Just checking for the existance of 'sgd_php_extensions' is enough.
if (array_key_exists('sgd_php_extensions', $statusData)) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* {@inheritdoc}
*/
public function saveStatus($websiteData, $statusData, $enabledProjects = NULL) : bool {
if ($this->canProcessStatus($statusData)) {
// Following values are provided by the PHP status companion module.
$phpData = [
'php_post_max_size' => $statusData['php_post_max_size'],
'php_upload_max_filesize' => $statusData['php_upload_max_filesize'],
'php_realpath_cache_size' => $statusData['php_realpath_cache_size'],
'php_realpath_cache_ttl' => $statusData['php_realpath_cache_ttl'],
'php_max_execution_time' => $statusData['php_max_execution_time'],
'php_display_errors' => $statusData['php_display_errors'],
'php_error_reporting' => $statusData['php_error_reporting'],
'php_log_errors' => $statusData['php_log_errors'],
'php_error_log' => $statusData['php_error_log'],
'php_short_open_tag' => $statusData['php_short_open_tag'],
'php_max_input_vars' => $statusData['php_max_input_vars'],
'sgd_php_extensions' => $statusData['sgd_php_extensions'],
];
// Add the PHP settings that are provided by the core API module to the
// saved data.
// This means they will also display on the PHP data page for each
// website.
$phpData += [
'php_version' => $statusData['php'],
'php_memory_limit' => $statusData['php_memory_limit'],
'php_apcu_caching' => $statusData['php_apcu_enabled'],
'php_opcache' => $statusData['php_opcache'],
];
// Serialize the data and save the field.
$websiteData->set('data_php_status', serialize($phpData));
}
else {
$websiteData->set('data_php_status', NULL);
}
return TRUE;
}
/**
* {@inheritdoc}
*/
public function getStatusDefaults() : array {
return [];
}
/**
* {@inheritdoc}
*/
public function getStatus($websiteData) : array | NULL {
if ($dataSerialized = $websiteData->get('data_php_status')->value) {
$data = unserialize($dataSerialized, ['allowed_classes' => FALSE]);
// Here is the opertunity to massage/transform any data we got from the
// website status.
// Do it here rather than when saving so we retain all info received.
$data['php_version']['title'] = $this->t('PHP version');
$data['php_version']['value'] = strtok($this->getValueOrDefault($data['php_version'] ?? NULL), '(');
}
else {
$data = NULL;
}
return $data;
}
/**
* {@inheritdoc}
*/
public function getBuildElements($websiteData) : array | NULL {
if ($data = $this->getStatus($websiteData)) {
$elements = [];
$validation = $this->validate($data);
foreach ($data as $key => $value) {
if ($key != 'sgd_php_extensions') {
$elements[$key] = [
'title' => $value['title'],
'value' => $value['value'],
'status' => $validation[$key] ?? NULL,
];
}
}
return $elements;
}
return NULL;
}
/**
* Validate data.
*
* Checks specific status items and for those where practicle returns a
* good/neutral/bad status that can be displayed on page or in a report.
*/
private function validate(&$statusData): array {
$validation = [];
// Validate PHP variables.
// Each validation is hard coded as it differs for each plugin.
foreach ($statusData as $key => $status) {
$phpVar = substr($key, 4);
switch ($key) {
case 'php_post_max_size':
// Get the 'real' value from the value saved which could be an int or
// in shorthand format.
$intValue = $this->getShortHandValue($status['value']);
if ($intValue < (10 * 1024 * 1024)) {
$validation[$key] = [
'class' => 'error',
'text' => $this->t('Issue'),
'message' => $this->t('post_max_size too low, < 10M. Review and set appropriately.'),
];
}
elseif ($intValue < (25 * 1024 * 1024) || $intValue > (50 * 1024 * 1024)) {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('post_max_size maybe too low, < 25M, or too high > 50M.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_upload_max_filesize':
// Get the 'real' value from the value saved which could be an int or
// in shorthand format.
$intValue = $this->getShortHandValue($status['value']);
if ($intValue < (2 * 1024 * 1024)) {
$validation[$key] = [
'class' => 'error',
'text' => $this->t('Issue'),
'message' => $this->t('upload_max_filesize to low, < 2M. Review and set appropriately.'),
];
}
elseif ($intValue < (5 * 1024 * 1024) || $intValue > (20 * 1024 * 1024)) {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('upload_max_filesize maybe too low, < 5M, or too high > 20M.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_realpath_cache_size':
// Get the 'real' value from the value saved which could be an int or
// in shorthand format.
$intValue = $this->getShortHandValue($status['value']);
if ($intValue < (64 * 1024)) {
$validation[$key] = [
'class' => 'error',
'text' => $this->t('Issue'),
'message' => $this->t('realpath_cache_size too low, < 64K. Review and set appropriately.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_realpath_cache_ttl':
$intValue = intval($status['value']);
if ($intValue < 3600) {
$validation[$key] = [
'class' => 'error',
'text' => $this->t('Issue'),
'message' => $this->t('realpath_cache_ttl too low, < 3600. Review and set appropriately.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_display_errors':
case 'php_display_startup_errors':
if (in_array(strtolower($status['value']), ['1', 'yes', 'on', 'true'])) {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('@phpvar is enabled. It is recommended it be off on production systems.', ['@phpvar' => $phpVar]),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
$statusData[$key]['value'] = 'False';
}
break;
case 'php_log_errors':
case 'php_html_errors':
if (!in_array(strtolower($status['value']), ['1', 'yes', 'on', 'true'])) {
$validation[$key] = [
'class' => 'error',
'text' => $this->t('Issue'),
'message' => $this->t('@phpvar is disabled. It is recommended it be turned on.', ['@phpvar' => $phpVar]),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_error_reporting':
if ($status['value'] != (E_ALL & ~E_STRICT & ~E_USER_DEPRECATED)) {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('error_reporting is not set as recommended for production systems. Recommended value is E_ALL & ~E_STRICT & ~E_USER_DEPRECATED.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_apcu_caching':
case 'php_opcache':
$enabled = strtolower(strtok($status['value'], ' ()'));
if ($enabled != 'enabled') {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('@phpvar is not enabled.', ['@phpvar' => $phpVar]),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_max_execution_time':
$intValue = intval($status['value']);
if ($intValue < 30) {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('max_execution_time too low, < 30 seconds. Review and set appropriately.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_max_input_vars':
$intValue = intval($status['value']);
if ($intValue < 2000) {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('max_input_vars too low, < 2000. Review and set appropriately.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_memory_limit':
// Get the 'real' value from the value saved which could be an int or
// in shorthand format.
$intValue = $this->getShortHandValue($status['value']);
if ($intValue < 32 * 1024 * 1024) {
$validation[$key] = [
'class' => 'error',
'text' => $this->t('Issue'),
'message' => $this->t('memory_limit too low, < 32M. Review and set appropriately.'),
];
}
elseif ($intValue > 100 * 1024 * 1024) {
$validation[$key] = [
'class' => 'warning',
'text' => $this->t('Alert'),
'message' => $this->t('memory_limit may be too high, > 100M. Review and set appropriately.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
case 'php_short_open_tag':
if (filter_var($status['value'], FILTER_VALIDATE_BOOLEAN)) {
$validation[$key] = [
'class' => 'error',
'text' => $this->t('Issue'),
'message' => $this->t('Use of short tags is discouraged and is likely to depreciated at some point.'),
];
}
else {
$validation[$key] = [
'class' => 'ok',
'text' => $this->t('OK'),
];
}
break;
}
}
return $validation;
}
/**
* Return a number from a PHP INI value that maybe in shorthand format.
*
* For example: 5M or 25K.
*/
private function getShortHandValue($val) : int {
switch (strtolower(substr(trim($val), -1))) {
case 'k':
return (int) $val * 1024;
case 'm':
return (int) $val * 1024 * 1024;
case 'g':
return (int) $val * 1024 * 1024 * 1024;
default:
return (int) $val;
}
}
}
