mangadex/scripts/header.req.php

378 lines
13 KiB
PHP
Raw Normal View History

2021-03-14 17:31:55 -04:00
<?php
// Sentry error handling (must be as early as possible to catch any init exceptions=
if (defined('SENTRY_DSN') && SENTRY_DSN && class_exists('Raven_Client')) {
2021-03-19 16:06:32 -04:00
$sentry = new Raven_Client(SENTRY_DSN, [
'sample_rate' => SENTRY_SAMPLE_RATE,
'curl_method' => SENTRY_CURL_METHOD,
'timeout' => SENTRY_TIMEOUT
]);
try {
$sentry->install();
} catch (\Raven_Exception $e) {
// This should land in the logfiles at least but not block script execution
trigger_error('Failed to install Sentry client: '.$e->getMessage(), E_USER_WARNING);
}
2021-03-14 17:31:55 -04:00
}
//database stuff
$host = DB_HOST;
$db = DB_NAME;
$charset = 'utf8mb4';
$dsn_master = "mysql:host=$host;dbname=$db;charset=$charset";
$dsn_slaves = [];
foreach (DB_READ_HOSTS ?? [] AS $slave_host) {
2021-03-19 16:06:32 -04:00
$slave_db = DB_READ_NAME;
$slave_port = 3306;
if (strpos($slave_host, ':') !== false) {
[$slave_host, $slave_port] = explode(':', $slave_host, 2);
}
$dsn_slaves[] = "mysql:host=$slave_host;port=$slave_port;dbname=$slave_db;charset=$charset";
2021-03-14 17:31:55 -04:00
}
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
2021-03-19 16:06:32 -04:00
PDO::ATTR_PERSISTENT => defined('DB_PERSISTENT') ? (bool)DB_PERSISTENT : false,
2021-03-14 17:31:55 -04:00
];
class SQL extends PDO {
2021-03-19 16:06:32 -04:00
private $debug = [];
private $time_array = [];
/** @var \PDO */
private $slave_sql;
private $credentials = [];
private $isConnected = false;
public function __construct(string $dsn_master, array $dsn_slaves, $username = null, $passwd = null, $options = null)
{
$this->credentials = [
'dsn_master' => $dsn_master,
'dsn_slaves' => $dsn_slaves,
'username' => $username,
'passwd' => $passwd,
'options' => $options,
];
}
private function ensureConnected(): void
{
if (!$this->isConnected) {
$dsn_master = $this->credentials['dsn_master'];
$dsn_slaves = $this->credentials['dsn_slaves'];
$username = $this->credentials['username'];
$passwd = $this->credentials['passwd'];
$options = $this->credentials['options'];
$this->credentials = [];
// Establish connection with master
parent::__construct($dsn_master, $username, $passwd, $options);
$this->isConnected = true;
// Randomize pick order
shuffle($dsn_slaves);
while (!empty($dsn_slaves)) {
$dsn_slave = array_pop($dsn_slaves);
$error = 'Slave failed with unknown reason';
try {
$this->slave_sql = new \PDO($dsn_slave, DB_READ_USER, DB_READ_PASSWORD, $options);
// Try a ping
if (false === $this->slave_sql->query('SELECT 1')) {
throw new \RuntimeException('Ping on slave failed!');
}
// Connection successful
return;
} catch (\PDOException $e) {
$error = sprintf('Slave failed with error code %s', $e->getCode());
} catch (\Throwable $e) {
$error = sprintf('Unexpected exception: %s', $e->getMessage());
}
// A slave failed, report warning to sentry
trigger_error($error, E_USER_WARNING);
}
// Fall back to master
$this->slave_sql = $this;
}
}
public function query_read($name, $query, $fetch, $pdo_mode, $expiry = 0) {
global $memcached;
$name = str_replace(' ', '_', $name);
if ($expiry < 0) {
$memcached->delete($name);
}
$start = microtime(true);
$cache = $memcached->get($name);
$from_cache = 'Y';
if ($cache === FALSE) {
$this->ensureConnected();
if ($fetch === 'fetchAll') {
$cache = $this->slave_sql->query($query)->fetchAll($pdo_mode);
}
elseif ($fetch === 'fetchColumn') {
$cache = $this->slave_sql->query($query)->fetchColumn();
}
else {
$cache = $this->slave_sql->query($query)->fetch($pdo_mode);
}
if ($expiry >= 0) {
$memcached->set($name, $cache, $expiry);
}
$from_cache = 'N';
}
$time_taken = round((microtime(true) - $start) * 1000, 2);
$this->time_array[] = $time_taken;
$this->debug[] = [$name, nl2br(trim($query)), $fetch, $from_cache, $time_taken];
return $cache;
}
public function prep($name, $query, $bind, $fetch, $pdo_mode = '', $expiry = 0, $force_master = false) {
global $memcached;
$name = str_replace(' ', '_', $name);
if ($expiry < 0) {
$memcached->delete($name);
}
$start = microtime(true);
$cache = $memcached->get($name);
$from_cache = 'Y';
if ($cache === FALSE) {
$this->ensureConnected();
$stmt = $force_master ? $this->prepare($query) : $this->slave_sql->prepare($query);
$stmt->execute($bind);
switch ($fetch) {
case 'fetch':
$cache = $stmt->fetch($pdo_mode);
break;
case 'fetchColumn':
$cache = $stmt->fetchColumn();
break;
default:
$cache = $stmt->fetchAll($pdo_mode);
break;
}
if ($expiry >= 0) {
2021-03-14 17:31:55 -04:00
$memcached->set($name, $cache, $expiry);
2021-03-19 16:06:32 -04:00
}
$from_cache = 'N';
}
$time_taken = round((microtime(true) - $start) * 1000, 2);
$this->time_array[] = $time_taken;
$query = preg_replace(array_fill(0, count($bind), '/\?/'), array_fill(0, count($bind), "<span style='color: red'>~</span>"), $query, 1);
$query = preg_replace(array_fill(0, count($bind), '/~/'), $bind, $query, 1);
$this->debug[] = [$name, nl2br(trim($query)), $fetch, $from_cache, $time_taken];
return $cache;
}
public function modify($name, $query, $bind) {
$name = str_replace(' ', '_', $name);
$start = microtime(true);
$from_cache = '/';
2021-03-14 17:31:55 -04:00
2021-03-19 16:06:32 -04:00
$this->ensureConnected();
$stmt = $this->prepare($query);
$stmt->execute($bind);
$time_taken = round((microtime(true) - $start) * 1000, 2);
$this->time_array[] = $time_taken;
$query = preg_replace(array_fill(0, count($bind), '/\?/'), array_fill(0, count($bind), "<span style='color: red'>~</span>"), $query, 1);
$query = preg_replace(array_fill(0, count($bind), '/~/'), $bind, $query, 1);
$this->debug[] = [$name, nl2br(trim($query)), 'modify', $from_cache, $time_taken];
return $this->lastInsertId();
2021-03-14 17:31:55 -04:00
}
2021-03-19 16:06:32 -04:00
public function debug() {
global $memcached;
// ======== Add sql table
$return = "
<table style='margin-top: 50px;' class='table table-condensed table-striped'>
<tr>
<th><button id='toggle-sql-table'><i class='fa fa-eye'></i></button></th>
<th>N</th>
<th>Q</th>
<th>M</th>
<th>C</th>
<th>T</th>
</tr>";
foreach ($this->debug as $key => $array) {
++$key;
$return .= "<tr style='display:none'>";
$return .= "<td>$key</td>";
foreach ($array as $value) {
$return .= "<td>$value</td>";
}
$return .= "</tr>";
}
$total_time = array_sum($this->time_array);
$return .= "
<tr style='display:none'>
<th>#</th>
<th>Name</th>
<th>Query</th>
<th>Mode</th>
<th>Cache</th>
<th>$total_time</th>
</tr>
</table>";
if (defined('CAPTURE_CACHE_STATS') && CAPTURE_CACHE_STATS) {
2021-03-14 17:31:55 -04:00
// ======== Add cache table
$cacheDebug = $memcached->toArray();
$return .= "
2021-03-19 16:06:32 -04:00
<table style='margin-top: 50px;' class='table table-condensed table-striped'>
<tr>
<th><button id='toggle-cache-table'><i class='fa fa-eye'></i></button></th>
<th>Method</th>
<th>Time (s)</th>
<th>Key</th>
<th>Result</th>
<th>Call Stack</th>
</tr>";
2021-03-14 17:31:55 -04:00
$return .= "
<tr style='display:none'>
<td colspan='6'>Hits: {$cacheDebug['stats']['hit']}, Misses: {$cacheDebug['stats']['miss']}, Sets: {$cacheDebug['stats']['set']}, Deletes: {$cacheDebug['stats']['delete']}</td>
</tr>";
foreach ($cacheDebug['log'] as $key => $cacheLogRow) {
$return .= "
<tr style='display:none'>
<td>$key</td>
<td>$cacheLogRow[method]</td>
<td>$cacheLogRow[time]</td>
<td>$cacheLogRow[key]</td>
<td>".$memcached->getResultString($cacheLogRow['result'])."</td>
<td><ul><li>".implode("</li><li>",$cacheLogRow['call_stack'])."</li></ul></td>
</tr>";
}
$total_time = array_sum($this->time_array);
$return .= "
2021-03-19 16:06:32 -04:00
<tr style='display:none'>
<th>#</th>
<th>Method</th>
<th>Time (s)</th>
<th>Key</th>
<th>Result</th>
<th>$cacheDebug[time]</th>
</tr>
</table>";
2021-03-14 17:31:55 -04:00
}
2021-03-19 16:06:32 -04:00
$return .= "
2021-03-14 17:31:55 -04:00
<script>
document.addEventListener('DOMContentLoaded', function() {
$('#toggle-sql-table').click(function(ev){
console.log($(this).parent().parent().parent().find('tr'));
$(this).parent().parent().parent().find('tr').each(function(i,e){
if (i == 0) return;
$(e).toggle();
});
});
$('#toggle-cache-table').click(function(ev){
console.log($(this).parent().parent().parent().find('tr'));
$(this).parent().parent().parent().find('tr').each(function(i,e){
if (i == 0) return;
$(e).toggle();
});
});
});
</script>";
2021-03-19 16:06:32 -04:00
return $return;
}
2021-03-14 17:31:55 -04:00
}
try {
$sql = new SQL($dsn_master, $dsn_slaves, DB_USER, DB_PASSWORD, $opt);
} catch (\PDOException $e) {
print file_get_contents(ABSPATH . '/dberror.html');
// Send to sentry
trigger_error('DB is down: '.$e->getMessage(), E_USER_ERROR);
die();
}
//cache
require_once ABSPATH . '/scripts/classes/cache.class.req.php';
if (defined('CAPTURE_CACHE_STATS') && CAPTURE_CACHE_STATS) {
$memcached = new Cache();
} else {
2021-03-19 16:06:32 -04:00
$memcached = new Synced_Memcached();
2021-03-14 17:31:55 -04:00
}
$memcached->addServer(MEMCACHED_HOST, 11211);
//including files
require_once (ABSPATH . '/scripts/functions.req.php');
foreach (read_dir('scripts/classes') as $file) {
$file = str_replace(['..', '/', '\\', '`', '´', '"', "'"], '', $file);
2021-03-19 16:06:32 -04:00
require_once (ABSPATH . "/scripts/classes/$file");
2021-03-14 17:31:55 -04:00
} //require every file in classes
require_once (ABSPATH . '/scripts/display.req.php');
//set vars
$ip = substr($_SERVER['HTTP_CF_CONNECTING_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? 'cli', 0, 45);
if (\strpos($ip, ',') !== false) {
$_tmp = explode(',', $ip);
$ip = reset($_tmp);
unset($_tmp);
}
define('_IP', $ip);
$ipService = new \Mangadex\Model\IpLocator();
$mdAtHomeClient = new Mangadex\Model\MdexAtHomeClient();
$hentai_toggle = max(0,min(2, $_COOKIE['mangadex_h_toggle'] ?? 0));
$theme_cookie = (int)($_COOKIE['mangadex_theme'] ?? 1);
$display_lang_cookie = (int)($_COOKIE['mangadex_display_lang'] ?? 0);
$filter_langs_cookie = $_COOKIE['mangadex_filter_langs'] ?? '';
$title_mode_cookie = (int)($_COOKIE['mangadex_title_mode'] ?? 0);
$_GET['page'] = $_GET['page'] ?? '';
$timestamp = time();
//require other stuff
require_once (ABSPATH . '/scripts/JBBCode/Parser.php');
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());