%PDF- %PDF-
| Direktori : /www/varak.net/dmarc.varak.net/classes/Database/Common/ |
| Current File : /www/varak.net/dmarc.varak.net/classes/Database/Common/CommonReportLogMapper.php |
<?php
/**
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
* Copyright (C) 2022-2025 Aleksey Andreev (liuch)
*
* Available at:
* https://github.com/liuch/dmarc-srg
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*
* =========================
*
* This file contains the CommonReportLogMapper class
*
* @category API
* @package DmarcSrg
* @author Aleksey Andreev (liuch)
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU/GPLv3
*/
namespace Liuch\DmarcSrg\Database\Common;
use Liuch\DmarcSrg\DateTime;
use Liuch\DmarcSrg\Database\ReportLogMapperInterface;
use Liuch\DmarcSrg\Exception\DatabaseFatalException;
use Liuch\DmarcSrg\Exception\DatabaseNotFoundException;
/**
* Universal implementation of ReportLogMapper class
*/
class CommonReportLogMapper implements ReportLogMapperInterface
{
/** @var \Liuch\DmarcSrg\Database\DatabaseConnector */
private $connector = null;
/**
* The constructor
*
* @param \Liuch\DmarcSrg\Database\DatabaseConnector $connector DatabaseConnector instance of the current database
*/
public function __construct(object $connector)
{
$this->connector = $connector;
}
/**
* Fetches data of report log item from the database by id
*
* @param array $data Log data
*
* @return void
*/
public function fetch(array &$data): void
{
try {
$st = $this->connector->dbh()->prepare(
'SELECT domain, external_id, event_time, filename, source, success, message FROM '
. $this->connector->tablePrefix('reportlog') . ' WHERE id = ?'
);
$st->bindValue(1, $data['id'], \PDO::PARAM_INT);
$st->execute();
if (!($row = $st->fetch(\PDO::FETCH_NUM))) {
throw new DatabaseNotFoundException();
}
$data['domain'] = $row[0];
$data['report_id'] = $row[1];
$data['event_time'] = new DateTime($row[2]);
$data['filename'] = $row[3];
$data['source'] = intval($row[4]);
$data['success'] = boolval($row[5]);
$data['message'] = $row[6];
$st->closeCursor();
} catch (\PDOException $e) {
throw new DatabaseFatalException('Failed to get the log item', -1, $e);
}
}
/**
* Saves data of report log item to the database
*
* @return void
*/
public function save(array &$data): void
{
$db = $this->connector->dbh();
try {
$id = $data['id'];
if (is_null($id)) {
$st = $db->prepare(
'INSERT INTO ' . $this->connector->tablePrefix('reportlog')
. ' (domain, external_id, event_time, filename, source, success, message)'
. ' VALUES (?, ?, ?, ?, ?, ?, ?)'
);
} else {
$st = $db->prepare(
'UPDATE ' . $this->connector->tablePrefix('reportlog')
. ' SET domain = ?, external_id = ?, event_time = ?, filename = ?,'
. ' source = ?, success = ?, message = ? WHERE id = ?'
);
$st->bindValue(8, $id, \PDO::PARAM_INT);
}
$ts = $data['event_time'] ?? (new DateTime());
$st->bindValue(1, $data['domain'], \PDO::PARAM_STR);
$st->bindValue(2, $data['report_id'], \PDO::PARAM_STR);
$st->bindValue(3, $ts->format('Y-m-d H:i:s'), \PDO::PARAM_STR);
$st->bindValue(4, $data['filename'], \PDO::PARAM_STR);
$st->bindValue(5, $data['source'], \PDO::PARAM_INT);
$st->bindValue(6, $data['success'], \PDO::PARAM_BOOL);
$st->bindValue(7, $data['message'], \PDO::PARAM_STR);
$st->execute();
if (is_null($id)) {
$data['id'] = intval($db->lastInsertId());
}
$st->closeCursor();
$data['event_time'] = $ts;
} catch (\PDOException $e) {
throw new DatabaseFatalException('Failed to save a report log item');
}
}
/**
* Returns a list of report log items with given criteria
*
* @param array $filter Key-value array:
* 'from_time' => DateTime
* 'till_time' => DateTime
* @param array $order Key-value array with order options:
* 'direction' => string, 'ascent' or 'descent'
* @param array $limit Key-value array:
* 'offset' => int
* 'count' => int
*
* @return array
*/
public function list(array &$filter, array &$order, array &$limit): array
{
$list = [];
try {
$st = $this->connector->dbh()->prepare(
'SELECT id, domain, event_time, source, success, message FROM '
. $this->connector->tablePrefix('reportlog')
. $this->sqlCondition($filter)
. $this->sqlOrder($order)
. $this->sqlLimit($limit)
);
$this->sqlBindValues($st, $filter, $limit);
$st->execute();
while ($row = $st->fetch(\PDO::FETCH_NUM)) {
$list[] = [
'id' => intval($row[0]),
'domain' => $row[1],
'event_time' => new DateTime($row[2]),
'source' => intval($row[3]),
'success' => boolval($row[4]),
'message' => $row[5]
];
}
$st->closeCursor();
} catch (\PDOException $e) {
throw new DatabaseFatalException('Failed to get the logs', -1, $e);
}
return $list;
}
/**
* Returns the number of report log items matching the specified filter and limits
*
* @param array $filter Key-value array with filtering parameters
* @param array $limit Key-value array with limits
*
* @return int
*/
public function count(array &$filter, array &$limit): int
{
$cnt = 0;
try {
$st = $this->connector->dbh()->prepare(
'SELECT COUNT(*) FROM ' . $this->connector->tablePrefix('reportlog')
. $this->sqlCondition($filter)
. $this->sqlLimit($limit)
);
$this->sqlBindValues($st, $filter, $limit);
$st->execute();
$cnt = intval($st->fetch(\PDO::FETCH_NUM)[0]);
$st->closeCursor();
} catch (\PDOException $e) {
throw new DatabaseFatalException('Failed to get the log data', -1, $e);
}
return $cnt;
}
/**
* Deletes report log items from the database
*
* @param array $filter Key-value array with filtering parameters
* @param array $order Key-value array with order options:
* 'direction' => string, 'ascent' or 'descent'
* @param array $limit Key-value array with limits
*
* @return void
*/
public function delete(array &$filter, array &$order, array &$limit): void
{
try {
$st = $this->connector->dbh()->prepare(
'DELETE FROM ' . $this->connector->tablePrefix('reportlog')
. $this->sqlCondition($filter)
. $this->sqlOrder($order)
. $this->sqlLimit($limit)
);
$this->sqlBindValues($st, $filter, $limit);
$st->execute();
$st->closeCursor();
} catch (\PDOException $e) {
throw new DatabaseFatalException('Failed to remove the log data', -1, $e);
}
}
/**
* Returns a string with an SQL condition 'WHERE ...'
*
* @param array $filter Key-value with filtering paremeters
*
* @return string
*/
private function sqlCondition(array &$filter): string
{
$res = [];
if (isset($filter['from_time'])) {
$res[] = 'event_time >= ?';
}
if (isset($filter['till_time'])) {
$res[] = 'event_time < ?';
}
if (isset($filter['success'])) {
$res[] = 'success = ?';
}
if (isset($filter['source'])) {
$res[] = 'source = ?';
}
return count($res) > 0 ? (' WHERE ' . implode(' AND ', $res)) : '';
}
/**
* Returns 'ORDER BY ...' part of the SQL query
*
* @param array $order Key-value array with ordering options
*
* @return string
*/
private function sqlOrder(array &$order): string
{
return ' ORDER BY event_time ' . ($order['direction'] === 'descent' ? 'DESC' : 'ASC');
}
/**
* Returns 'LIMIT ...' part of the SQL string
*
* @param array $limit Key-value array with keys 'offset' and 'count'
*
* @return string
*/
private function sqlLimit(array &$limit): string
{
$res = '';
if ($limit['count'] > 0) {
$res = ' LIMIT ?';
if ($limit['offset'] > 0) {
$res .= ', ?';
}
}
return $res;
}
/**
* Binds the values of the filter and the limit to SQL query
*
* @param \PDOStatement $st Prepared SOL statement to bind to
* @param array $filter Key-value array with filter data
* @param array $limit Key-value array with limit data
*
* @return void
*/
private function sqlBindValues($st, array &$filter, array &$limit): void
{
$pos = 0;
if (isset($filter['from_time'])) {
$st->bindValue(++$pos, $filter['from_time']->format('Y-m-d H:i:s'), \PDO::PARAM_STR);
}
if (isset($filter['till_time'])) {
$st->bindValue(++$pos, $filter['till_time']->format('Y-m-d H:i:s'), \PDO::PARAM_STR);
}
if (isset($filter['success'])) {
$st->bindValue(++$pos, $filter['success'], \PDO::PARAM_BOOL);
}
if (isset($filter['source'])) {
$st->bindValue(++$pos, $filter['source'], \PDO::PARAM_INT);
}
if ($limit['count'] > 0) {
if ($limit['offset'] > 0) {
$st->bindValue(++$pos, $limit['offset'], \PDO::PARAM_INT);
}
$st->bindValue(++$pos, $limit['count'], \PDO::PARAM_INT);
}
}
}