<?php

namespace Modules\Utils\AppReports;


class SqlManager extends \App\Config\XmlDynamicRecordsAbstract
{
	/**
	 * gets the files from temporary due to the modification of constructInit()
	 * @var string
	 */
	protected $fileName = 'appReports.xml';


	/**
	 * (non-PHPdoc)
	 * @see \App\Config\BaseAbstract::constructInit()
	 */
	protected function constructInit()
	{
		$CFG = \Engine\Loader_Cfg::getInstance()->getGlobal();

		// check if the file name is okay
		if (!$this->fileName) {
			throw new \Exception('You did not set the `fileName` attribute for the class `'.get_called_class().'`.');
		}

		// set the path to temporary
		$this->fileNameWithPath = APP_REAL_PATH . $CFG['paths']['temporary'] . $this->fileName;

		//\App\UserTrail::getInstance()->storeTrail(false);

	}


	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_list()
	 */
	protected function getAll_rawData_extractDataFromFile_list(\SimpleXMLElement $xml)
	{
		$return=[];

		foreach($xml->report  as $appReport) {
			$return[(String)$appReport->name] = [
				"name"				=>	(String)$appReport->name,
				"sql"				=>	(String)$appReport->sql,
				"monthTable"		=>	(String)$appReport->monthTable,
			];

		}
		return $return;

	}


	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_default()
	 */
	protected function getAll_rawData_extractDataFromFile_default(\SimpleXMLElement $xml)
	{
		return '';
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlDynamicRecordsAbstract::getAll_rawData_extractDataFromFile_mandatory()
	 */
	protected function getAll_rawData_extractDataFromFile_mandatory(\SimpleXMLElement $xml)
	{
		return false;
	}

	/**
	 *
	 * {@inheritDoc}
	 * @see \App\Config\XmlDynamicRecordsAbstract::itemCheck()
	 */
	protected function itemCheck($code, $itemDetails)
	{

		if (!$itemDetails) $this->addErrorMessage('The appReport data details was not defined.');

		if (!$code) $this->addErrorMessage('The app report name is not set.');

		if (!$itemDetails['name']) {
			$this->addErrorMessage('The name is not set.');
		}

		if (!$itemDetails['sql']) {
			$this->addErrorMessage('The sql is not set.');
		}

		if ($itemDetails['sql'] and strtoupper(substr(ltrim($itemDetails['sql']), 0, 6)) !== 'SELECT') {
			$this->addErrorMessage('The sql does not contain `SELECT`.');
		}

		if ($itemDetails['sql']) {
			$this->checkForbiddenPatterns($itemDetails['sql']);
		}

	}


	/**
	 * Rejects SQL containing dangerous constructs that bypass the SELECT-only restriction.
	 * Blocks: file read/write, time/CPU/lock-based DoS, system schema enumeration, stacked statements.
	 * @param string $sql
	 */
	private function checkForbiddenPatterns($sql)
	{
		$forbidden = [
			'/\bINTO\s+(OUTFILE|DUMPFILE)\b/i',                                           // arbitrary file write / webshell upload
			'/\bLOAD_FILE\s*\(/i',                                                        // server-side file read
			'/\bSLEEP\s*\(/i',                                                            // time-based DoS
			'/\bBENCHMARK\s*\(/i',                                                        // CPU-based DoS
			'/\bGET_LOCK\s*\(/i',                                                         // lock-based DoS
			'/\b(REPEAT|SPACE)\s*\(/i',                                                   // string allocation DoS
			'/\b(ExtractValue|UpdateXML)\s*\(/i',                                         // error-based data extraction
			'/\binformation_schema\b/i',                                                  // schema enumeration
			'/\bperformance_schema\b/i',                                                  // performance schema enumeration
			'/\bmysql\s*\.\s*\w/i',                                                       // mysql system schema (user, db, tables_priv, etc.)
			'/\bsys\s*\.\s*\w/i',                                                         // sys schema access
			'/;\s*(DROP|DELETE|UPDATE|INSERT|TRUNCATE|ALTER|CREATE|REPLACE|RENAME)\b/i',  // stacked statement bypass
		];

		foreach ($forbidden as $pattern) {
			if (preg_match($pattern, $sql)) {
				$this->addErrorMessage('The sql contains a forbidden operation.');
			}
		}
	}


	/**
	 * (non-PHPdoc)
	 * @see \App\Config\BaseAbstract::save_check()
	 */
	protected function save_check()
	{

	}


	/**
	 * (non-PHPdoc)
	 * @see \App\Config\XmlBasicAbstract::save_write_do_getXml()
	 */
	protected function save_write_do_getXml()
	{
		$xml=simplexml_load_string('<?xml version="1.0" encoding="UTF-8"?><reports />');
		foreach ($this->itemsList as $currReport) {

			$currentXML = $xml->addChild('report', '');
			$currentXML->name = $currReport['name'];
			$currentXML->sql = $currReport['sql'];
			$currentXML->monthTable = $currReport['monthTable'];

		}

		// return the xml content of the current object
		return $xml;

	}



}
