1<?php 2/** 3 * Abstract class for the date format transformations plugins 4 */ 5 6declare(strict_types=1); 7 8namespace PhpMyAdmin\Plugins\Transformations\Abs; 9 10use PhpMyAdmin\Plugins\TransformationsPlugin; 11use PhpMyAdmin\Sanitize; 12use PhpMyAdmin\Util; 13use stdClass; 14use function checkdate; 15use function gmdate; 16use function htmlspecialchars; 17use function mb_strlen; 18use function mb_strtolower; 19use function mb_substr; 20use function mktime; 21use function preg_match; 22use function strtotime; 23 24/** 25 * Provides common methods for all of the date format transformations plugins. 26 */ 27abstract class DateFormatTransformationsPlugin extends TransformationsPlugin 28{ 29 /** 30 * Gets the transformation description of the specific plugin 31 * 32 * @return string 33 */ 34 public static function getInfo() 35 { 36 return __( 37 'Displays a TIME, TIMESTAMP, DATETIME or numeric unix timestamp' 38 . ' column as formatted date. The first option is the offset (in' 39 . ' hours) which will be added to the timestamp (Default: 0). Use' 40 . ' second option to specify a different date/time format string.' 41 . ' Third option determines whether you want to see local date or' 42 . ' UTC one (use "local" or "utc" strings) for that. According to' 43 . ' that, date format has different value - for "local" see the' 44 . ' documentation for PHP\'s strftime() function and for "utc" it' 45 . ' is done using gmdate() function.' 46 ); 47 } 48 49 /** 50 * Does the actual work of each specific transformations plugin. 51 * 52 * @param string $buffer text to be transformed 53 * @param array $options transformation options 54 * @param stdClass|null $meta meta information 55 * 56 * @return string 57 */ 58 public function applyTransformation($buffer, array $options = [], ?stdClass $meta = null) 59 { 60 $buffer = (string) $buffer; 61 // possibly use a global transform and feed it with special options 62 $cfg = $GLOBALS['cfg']; 63 $options = $this->getOptions($options, $cfg['DefaultTransformations']['DateFormat']); 64 65 // further operations on $buffer using the $options[] array. 66 $options[2] = mb_strtolower($options[2]); 67 68 if (empty($options[1])) { 69 if ($options[2] === 'local') { 70 $options[1] = __('%B %d, %Y at %I:%M %p'); 71 } else { 72 $options[1] = 'Y-m-d H:i:s'; 73 } 74 } 75 76 $timestamp = -1; 77 78 // INT columns will be treated as UNIX timestamps 79 // and need to be detected before the verification for 80 // MySQL TIMESTAMP 81 if ($meta->type === 'int') { 82 $timestamp = $buffer; 83 84 // Detect TIMESTAMP(6 | 8 | 10 | 12 | 14) 85 // TIMESTAMP (2 | 4) not supported here. 86 // (Note: prior to MySQL 4.1, TIMESTAMP has a display size 87 // for example TIMESTAMP(8) means YYYYMMDD) 88 } else { 89 if (preg_match('/^(\d{2}){3,7}$/', $buffer)) { 90 if (mb_strlen($buffer) == 14 || mb_strlen($buffer) == 8) { 91 $offset = 4; 92 } else { 93 $offset = 2; 94 } 95 96 $aDate = []; 97 $aDate['year'] = (int) mb_substr($buffer, 0, $offset); 98 $aDate['month'] = (int) mb_substr($buffer, $offset, 2); 99 $aDate['day'] = (int) mb_substr($buffer, $offset + 2, 2); 100 $aDate['hour'] = (int) mb_substr($buffer, $offset + 4, 2); 101 $aDate['minute'] = (int) mb_substr($buffer, $offset + 6, 2); 102 $aDate['second'] = (int) mb_substr($buffer, $offset + 8, 2); 103 104 if (checkdate($aDate['month'], $aDate['day'], $aDate['year'])) { 105 $timestamp = mktime( 106 $aDate['hour'], 107 $aDate['minute'], 108 $aDate['second'], 109 $aDate['month'], 110 $aDate['day'], 111 $aDate['year'] 112 ); 113 } 114 // If all fails, assume one of the dozens of valid strtime() syntaxes 115 // (https://www.gnu.org/manual/tar-1.12/html_chapter/tar_7.html) 116 } else { 117 if (preg_match('/^[0-9]\d{1,9}$/', $buffer)) { 118 $timestamp = (int) $buffer; 119 } else { 120 $timestamp = strtotime($buffer); 121 } 122 } 123 } 124 125 // If all above failed, maybe it's a Unix timestamp already? 126 if ($timestamp < 0 && preg_match('/^[1-9]\d{1,9}$/', $buffer)) { 127 $timestamp = $buffer; 128 } 129 130 // Reformat a valid timestamp 131 if ($timestamp >= 0) { 132 $timestamp -= (int) $options[0] * 60 * 60; 133 $source = $buffer; 134 if ($options[2] === 'local') { 135 $text = Util::localisedDate( 136 $timestamp, 137 $options[1] 138 ); 139 } elseif ($options[2] === 'utc') { 140 $text = gmdate($options[1], $timestamp); 141 } else { 142 $text = 'INVALID DATE TYPE'; 143 } 144 145 return '<dfn onclick="alert(\'' . Sanitize::jsFormat($source, false) . '\');" title="' 146 . htmlspecialchars((string) $source) . '">' . htmlspecialchars((string) $text) . '</dfn>'; 147 } 148 149 return htmlspecialchars((string) $buffer); 150 } 151 152 /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ 153 154 /** 155 * Gets the transformation name of the specific plugin 156 * 157 * @return string 158 */ 159 public static function getName() 160 { 161 return 'Date Format'; 162 } 163} 164