From fbe0ee21003dfa6c629397a1005d6996f97012f8 Mon Sep 17 00:00:00 2001 From: "Bruno O. Notario" Date: Sat, 9 Apr 2022 16:53:01 -0300 Subject: [PATCH] New Debug class, Smarty templates fix, Render changes Added more complex exception log with format changes --- system/config/autoload.php | 33 +++++- system/engine/autoload.php | 6 +- system/engine/controller.php | 9 +- system/engine/debug.php | 191 +++++++++++++++++++++++++++++++++++ system/engine/exception.php | 38 ++++++- system/engine/render.php | 110 +++++++++++++++++++- 6 files changed, 375 insertions(+), 12 deletions(-) create mode 100644 system/engine/debug.php diff --git a/system/config/autoload.php b/system/config/autoload.php index 018877a..33d5aa8 100644 --- a/system/config/autoload.php +++ b/system/config/autoload.php @@ -41,12 +41,43 @@ final class Config { return isset($this->data[$key]); } + /** + * + * @param string $key + * @param mixed $value + * @return mixed + */ + static public function __callStatic ($key, $value){ + try { + return (defined($key)) ? constant($key) : self::setConstant($key, $value); + } catch (\Phacil\Framework\Exception $th) { + throw new \Phacil\Framework\Exception($th->getMessage()); + } + } + + /** + * + * @param string $key + * @param mixed $value + * @return mixed + */ + static private function setConstant($key, $value){ + if(is_array($value) && count($value) == 1){ + $value = end($value); + } + $php = version_compare(phpversion(), '7.0', '>='); + if(is_array($value) && !$php){ + throw new \Phacil\Framework\Exception('Array is not supported on constant value in PHP <7'); + } + return (define($key, $value)) ? $value : null; + } + /** * @param string $filename * @return void */ public function load($filename) { - $file = DIR_CONFIG . $filename . '.php'; + $file = self::DIR_CONFIG() . $filename . '.php'; if (file_exists($file)) { $cfg = array(); diff --git a/system/engine/autoload.php b/system/engine/autoload.php index 416c63c..7c925ba 100644 --- a/system/engine/autoload.php +++ b/system/engine/autoload.php @@ -22,8 +22,7 @@ spl_autoload_register(function ($class) { 'Request', 'Mail', 'Translate'. - 'Encryption', - 'Render' + 'Encryption' ]; if(in_array($class, $legacy)){ @@ -78,7 +77,8 @@ spl_autoload_register(function ($class) { 'traits\\action', 'interfaces\\databases', 'exception', - 'render' + 'render', + 'debug' ]; if($namespace[0] == "Phacil" && in_array($classNative, $allowed)){ diff --git a/system/engine/controller.php b/system/engine/controller.php index 7dfd53f..fdaeecc 100644 --- a/system/engine/controller.php +++ b/system/engine/controller.php @@ -242,6 +242,8 @@ abstract class Controller { $this->data[basename($child)] = $this->getChild($child); } + $tpl = new \Phacil\Framework\Render($this->registry); + $pegRout = explode("/", ($this->registry->routeOrig)?: $this->request->get['route']); $pegRoutWithoutLast = $pegRout; array_pop($pegRoutWithoutLast); @@ -252,8 +254,9 @@ abstract class Controller { $thema = ($this->config->get("config_template") != NULL) ? $this->config->get("config_template") : "default"; - $structure = []; - foreach($this->templateTypes as $extensionTemplate) { + + foreach($tpl->getTemplateTypes() as $extensionTemplate) { + $structure = []; $structure[] = $thema.'/'.$pegRout[0].'/'.$pegRout[1].((isset($pegRout[2])) ? '_'.$pegRout[2] : '').'.'.$extensionTemplate; $structure[] = 'default/'.$pegRout[0].'/'.$pegRout[1].((isset($pegRout[2])) ? '_'.$pegRout[2] : '').'.'.$extensionTemplate; @@ -293,7 +296,7 @@ abstract class Controller { $templateFileInfo = pathinfo($templatePath .$this->template); $templateType = $templateFileInfo['extension']; - $tpl = new \Phacil\Framework\Render($templateType, $templatePath, $this->template, $this->data, $this->twig); + $tpl->setTemplate($templateType, $templatePath, $this->template, $this->data, $this->twig); $this->registry->response->addHeader('Content-Type', $this->contentType); diff --git a/system/engine/debug.php b/system/engine/debug.php new file mode 100644 index 0000000..99c3744 --- /dev/null +++ b/system/engine/debug.php @@ -0,0 +1,191 @@ + + */ + +namespace Phacil\Framework; + +/** + * Debug methods + * @package Phacil\Framework + */ +class Debug +{ + /** + * @var int + */ + public static $argLength = 16; + + /** + * Root path + * + * @var string + */ + protected static $_filePath; + + /** + * Retrieve real root path with last directory separator + * + * @return string + */ + public static function getRootPath() + { + if (self::$_filePath === null) { + if (defined('DIR_APPLICATION')) { + self::$_filePath = DIR_APPLICATION; + } else { + self::$_filePath = dirname(__DIR__); + } + } + return self::$_filePath; + } + + /** + * Prints or returns a backtrace + * + * @param bool $return return or print + * @param bool $html output in HTML format + * @param bool $withArgs add short arguments of methods + * @return string|bool + */ + public static function backtrace($return = false, $html = true, $withArgs = true) + { + $trace = debug_backtrace(); + return self::trace($trace, $return, $html, $withArgs); + } + + /** + * Prints or return a trace + * + * @param array $trace trace array + * @param bool $return return or print + * @param bool $html output in HTML format + * @param bool $withArgs add short arguments of methods + * @return string|bool + */ + public static function trace(array $trace, $return = true, $html = false, $withArgs = true) + { + $out = ''; + if ($html) { + $out .= '
';
+		}
+
+		foreach ($trace as $i => $data) {
+			// skip self
+			if ($i == 0) {
+				continue;
+			}
+
+			// prepare method arguments
+			$args = [];
+			if (isset($data['args']) && $withArgs) {
+				foreach ($data['args'] as $arg) {
+					$args[] = self::_formatCalledArgument($arg);
+				}
+			}
+
+			// prepare method's name
+			if (isset($data['class']) && isset($data['function'])) {
+				if (isset($data['object']) && get_class($data['object']) != $data['class']) {
+					$className = get_class($data['object']) . '[' . $data['class'] . ']';
+				} else {
+					$className = $data['class'];
+				}
+				if (isset($data['object'])) {
+					$className .= sprintf('#%s#', spl_object_hash($data['object']));
+				}
+
+				$methodName = sprintf(
+					'%s%s%s(%s)',
+					$className,
+					isset($data['type']) ? $data['type'] : '->',
+					$data['function'],
+					join(', ', $args)
+				);
+			} elseif (isset($data['function'])) {
+				$methodName = sprintf('%s(%s)', $data['function'], join(', ', $args));
+			}
+
+			if (isset($data['file'])) {
+				$pos = strpos($data['file'], self::getRootPath());
+				if ($pos !== false) {
+					$data['file'] = str_replace(self::getRootPath(), "", $data['file']);
+				}
+				$fileName = sprintf('%s:%d', $data['file'], $data['line']);
+			} else {
+				$fileName = false;
+			}
+
+			if ($fileName) {
+				$out .= sprintf('#%d %s called at [%s]', $i, $methodName, $fileName);
+			} else {
+				$out .= sprintf('#%d %s', $i, $methodName);
+			}
+
+			$out .= "\n";
+		}
+
+		if ($html) {
+			$out .= '
'; + } + + if ($return) { + return $out; + } else { + echo $out; + return true; + } + } + + /** + * Format argument in called method + * + * @param mixed $arg + * @return string + */ + protected static function _formatCalledArgument($arg) + { + $out = ''; + if (is_object($arg)) { + $out .= sprintf("&%s#%s#", get_class($arg), spl_object_hash($arg)); + } elseif (is_resource($arg)) { + $out .= '#[' . get_resource_type($arg) . ']'; + } elseif (is_array($arg)) { + $isAssociative = false; + $args = []; + foreach ($arg as $k => $v) { + if (!is_numeric($k)) { + $isAssociative = true; + } + $args[$k] = self::_formatCalledArgument($v); + } + if ($isAssociative) { + $arr = []; + foreach ($args as $k => $v) { + $arr[] = self::_formatCalledArgument($k) . ' => ' . $v; + } + $out .= 'array(' . join(', ', $arr) . ')'; + } else { + $out .= 'array(' . join(', ', $args) . ')'; + } + } elseif ($arg === null) { + $out .= 'NULL'; + } elseif (is_numeric($arg) || is_float($arg)) { + $out .= $arg; + } elseif (is_string($arg)) { + if (strlen($arg) > self::$argLength) { + $arg = substr($arg, 0, self::$argLength) . "..."; + } + $arg = strtr($arg, ["\t" => '\t', "\r" => '\r', "\n" => '\n', "'" => '\\\'']); + $out .= "'" . $arg . "'"; + } elseif (is_bool($arg)) { + $out .= $arg === true ? 'true' : 'false'; + } + + return $out; + } +} \ No newline at end of file diff --git a/system/engine/exception.php b/system/engine/exception.php index 268e4a2..0402757 100644 --- a/system/engine/exception.php +++ b/system/engine/exception.php @@ -1,17 +1,24 @@ */ namespace Phacil\Framework; +/** + * Exception extended for log on destruct + * @since 2.0.0 + * @package Phacil\Framework + */ class Exception extends \Exception { + public $errorFormat = 'text'; /** * Save the exceptions in the exceptions log file @@ -19,8 +26,33 @@ class Exception extends \Exception */ public function __destruct() { + $debugging = (defined('DEBUG')) ? DEBUG : false; + $this->errorFormat = defined('DEBUG_FORMAT') ? constant('DEBUG_FORMAT') : $this->errorFormat; + $log = new \Phacil\Framework\Log("exception.log"); - $log->write($this->getMessage()); + + $errorStamp = [ + 'error' => $this->getMessage(), + 'line' => $this->getLine(), + 'file' => $this->getFile(), + 'trace' => ($debugging) ? (($this->errorFormat == 'json') ? $this->getTrace() : Debug::trace($this->getTrace())) : null + ]; + $log->write(($this->errorFormat == 'json') ? json_encode($errorStamp) : implode(PHP_EOL, array_map( + ['self','convertArray'], + $errorStamp, + array_keys($errorStamp) + ))); + } + + /** + * + * @param string|array $v + * @param string $k + * @return string + */ + static public function convertArray($v, $k) { + return sprintf("%s: %s", $k, (is_array($v) ? json_encode($v) : $v)); } } + diff --git a/system/engine/render.php b/system/engine/render.php index 7bb91a8..76b2192 100644 --- a/system/engine/render.php +++ b/system/engine/render.php @@ -9,23 +9,66 @@ namespace Phacil\Framework; use Phacil\Framework\Translate; + use Phacil\Framework\Registry; /** * * @package Phacil\Framework */ class Render { + /** + * + * @var mixed + */ private $data; + /** + * + * @var string + */ private $template; + /** + * + * @var string + */ private $templatePath; + /** + * + * @var string + */ private $templateType; + /** + * + * @var mixed + */ private $output; + /** + * + * @var mixed + */ private $extras; + + /** + * + * @var array + */ + protected $templateTypes = ["tpl", "twig", "mustache", "smarty", "phtml"]; + + /** + * + * @var Registry + */ + private $registry; + + /** + * + * @var \Phacil\Framework\Config + */ + private $config; /** * @@ -36,7 +79,31 @@ * @param mixed $extras * @return void */ - function __construct($templateType, $templatePath, $template, $data, $extras) { + function __construct(Registry $registry = null) { + if (!$registry) { + + /** + * @global \Phacil\Framework\startEngineExacTI $engine + */ + global $engine; + + $registry = &$engine->registry; + } + $this->registry = &$registry; + + $this->config =& $this->registry->config; + } + + /** + * + * @param mixed $templateType + * @param mixed $templatePath + * @param mixed $template + * @param mixed $data + * @param mixed $extras + * @return $this + */ + public function setTemplate($templateType, $templatePath, $template, $data, $extras) { $this->data = $data; $this->template = $template; @@ -46,6 +113,8 @@ $this->templateType = $templateType; $this->extras = $extras; + + return $this; } /** @@ -61,6 +130,36 @@ return $this->output; } + /** + * + * @return array + */ + public function getTemplateTypes(){ + return $this->templateTypes; + } + + /** + * + * @param array $templateTypes + * @return array + */ + public function setTemplateTypes(array $templateTypes){ + $this->templateTypes = $templateTypes; + + return $this->templateTypes; + } + + /** + * + * @param string $type + * @return array + */ + public function addTemplateType(string $type){ + $this->templateTypes[] = $type; + + return $this->templateTypes; + } + /** * * @return void @@ -190,7 +289,14 @@ $smarty->setCacheDir(DIR_CACHE . "Smarty/cache/"); - $smarty->registerPlugin("block", "translate", "translate"); + $smarty->registerPlugin("block", "translate", function ($text) { + if (class_exists('Phacil\Framework\Translate')) { + $trans = new Translate(); + return ($trans->translation($text)); + } else { + return $text; + } // do something translate here... + }); $smarty->assign($this->data);