PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php

https://gitlab.com/daniruizcamacho/pfcascensores
PHP | 333 lines | 170 code | 38 blank | 125 comment | 16 complexity | f623b3545df920c9bdd81f7f265f7371 MD5 | raw file
  1. <?php
  2. /**
  3. * Whoops - php errors for cool kids
  4. * @author Filipe Dobreira <http://github.com/filp>
  5. */
  6. namespace Whoops\Handler;
  7. use Whoops\Handler\Handler;
  8. use InvalidArgumentException;
  9. class PrettyPageHandler extends Handler
  10. {
  11. /**
  12. * @var string
  13. */
  14. private $resourcesPath;
  15. /**
  16. * @var array[]
  17. */
  18. private $extraTables = array();
  19. /**
  20. * @var string
  21. */
  22. private $pageTitle = 'Whoops! There was an error.';
  23. /**
  24. * A string identifier for a known IDE/text editor, or a closure
  25. * that resolves a string that can be used to open a given file
  26. * in an editor. If the string contains the special substrings
  27. * %file or %line, they will be replaced with the correct data.
  28. *
  29. * @example
  30. * "txmt://open?url=%file&line=%line"
  31. * @var mixed $editor
  32. */
  33. protected $editor;
  34. /**
  35. * A list of known editor strings
  36. * @var array
  37. */
  38. protected $editors = array(
  39. 'sublime' => 'subl://open?url=file://%file&line=%line',
  40. 'textmate' => 'txmt://open?url=file://%file&line=%line',
  41. 'emacs' => 'emacs://open?url=file://%file&line=%line',
  42. 'macvim' => 'mvim://open/?url=file://%file&line=%line'
  43. );
  44. /**
  45. * Constructor.
  46. */
  47. public function __construct()
  48. {
  49. if (ini_get('xdebug.file_link_format') || extension_loaded('xdebug')) {
  50. // Register editor using xdebug's file_link_format option.
  51. $this->editors['xdebug'] = function($file, $line) {
  52. return str_replace(array('%f', '%l'), array($file, $line), ini_get('xdebug.file_link_format'));
  53. };
  54. }
  55. }
  56. /**
  57. * @return int|null
  58. */
  59. public function handle()
  60. {
  61. // Check conditions for outputting HTML:
  62. // @todo: make this more robust
  63. if(php_sapi_name() === 'cli' && !isset($_ENV['whoops-test'])) {
  64. return Handler::DONE;
  65. }
  66. // Get the 'pretty-template.php' template file
  67. // @todo: this can be made more dynamic &&|| cleaned-up
  68. if(!($resources = $this->getResourcesPath())) {
  69. $resources = __DIR__ . '/../Resources';
  70. }
  71. $templateFile = "$resources/pretty-template.php";
  72. // @todo: Make this more reliable,
  73. // possibly by adding methods to append CSS & JS to the page
  74. $cssFile = "$resources/pretty-page.css";
  75. // Prepare the $v global variable that will pass relevant
  76. // information to the template
  77. $inspector = $this->getInspector();
  78. $frames = $inspector->getFrames();
  79. $v = (object) array(
  80. 'title' => $this->getPageTitle(),
  81. 'name' => explode('\\', $inspector->getExceptionName()),
  82. 'message' => $inspector->getException()->getMessage(),
  83. 'frames' => $frames,
  84. 'hasFrames' => !!count($frames),
  85. 'handler' => $this,
  86. 'handlers' => $this->getRun()->getHandlers(),
  87. 'pageStyle' => file_get_contents($cssFile),
  88. 'tables' => array(
  89. 'Server/Request Data' => $_SERVER,
  90. 'GET Data' => $_GET,
  91. 'POST Data' => $_POST,
  92. 'Files' => $_FILES,
  93. 'Cookies' => $_COOKIE,
  94. 'Session' => isset($_SESSION) ? $_SESSION: array(),
  95. 'Environment Variables' => $_ENV
  96. )
  97. );
  98. $extraTables = array_map(function($table) {
  99. return $table instanceof \Closure ? $table() : $table;
  100. }, $this->getDataTables());
  101. // Add extra entries list of data tables:
  102. $v->tables = array_merge($extraTables, $v->tables);
  103. call_user_func(function() use($templateFile, $v) {
  104. // $e -> cleanup output, optionally preserving URIs as anchors:
  105. $e = function($_, $allowLinks = false) {
  106. $escaped = htmlspecialchars($_, ENT_QUOTES, 'UTF-8');
  107. // convert URIs to clickable anchor elements:
  108. if($allowLinks) {
  109. $escaped = preg_replace(
  110. '@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@',
  111. "<a href=\"$1\" target=\"_blank\">$1</a>", $escaped
  112. );
  113. }
  114. return $escaped;
  115. };
  116. // $slug -> sluggify string (i.e: Hello world! -> hello-world)
  117. $slug = function($_) {
  118. $_ = str_replace(" ", "-", $_);
  119. $_ = preg_replace('/[^\w\d\-\_]/i', '', $_);
  120. return strtolower($_);
  121. };
  122. require $templateFile;
  123. });
  124. return Handler::QUIT;
  125. }
  126. /**
  127. * Adds an entry to the list of tables displayed in the template.
  128. * The expected data is a simple associative array. Any nested arrays
  129. * will be flattened with print_r
  130. * @param string $label
  131. * @param array $data
  132. */
  133. public function addDataTable($label, array $data)
  134. {
  135. $this->extraTables[$label] = $data;
  136. }
  137. /**
  138. * Lazily adds an entry to the list of tables displayed in the table.
  139. * The supplied callback argument will be called when the error is rendered,
  140. * it should produce a simple associative array. Any nested arrays will
  141. * be flattened with print_r.
  142. *
  143. * @throws InvalidArgumentException If $callback is not callable
  144. * @param string $label
  145. * @param callable $callback Callable returning an associative array
  146. */
  147. public function addDataTableCallback($label, /* callable */ $callback)
  148. {
  149. if (!is_callable($callback)) {
  150. throw new InvalidArgumentException('Expecting callback argument to be callable');
  151. }
  152. $this->extraTables[$label] = function() use ($callback) {
  153. try {
  154. $result = call_user_func($callback);
  155. // Only return the result if it can be iterated over by foreach().
  156. return is_array($result) || $result instanceof \Traversable ? $result : array();
  157. } catch (\Exception $e) {
  158. // Don't allow failure to break the rendering of the original exception.
  159. return array();
  160. }
  161. };
  162. }
  163. /**
  164. * Returns all the extra data tables registered with this handler.
  165. * Optionally accepts a 'label' parameter, to only return the data
  166. * table under that label.
  167. * @param string|null $label
  168. * @return array[]
  169. */
  170. public function getDataTables($label = null)
  171. {
  172. if($label !== null) {
  173. return isset($this->extraTables[$label]) ?
  174. $this->extraTables[$label] : array();
  175. }
  176. return $this->extraTables;
  177. }
  178. /**
  179. * Adds an editor resolver, identified by a string
  180. * name, and that may be a string path, or a callable
  181. * resolver. If the callable returns a string, it will
  182. * be set as the file reference's href attribute.
  183. *
  184. * @example
  185. * $run->addEditor('macvim', "mvim://open?url=file://%file&line=%line")
  186. * @example
  187. * $run->addEditor('remove-it', function($file, $line) {
  188. * unlink($file);
  189. * return "http://stackoverflow.com";
  190. * });
  191. * @param string $identifier
  192. * @param string $resolver
  193. */
  194. public function addEditor($identifier, $resolver)
  195. {
  196. $this->editors[$identifier] = $resolver;
  197. }
  198. /**
  199. * Set the editor to use to open referenced files, by a string
  200. * identifier, or a callable that will be executed for every
  201. * file reference, with a $file and $line argument, and should
  202. * return a string.
  203. *
  204. * @example
  205. * $run->setEditor(function($file, $line) { return "file:///{$file}"; });
  206. * @example
  207. * $run->setEditor('sublime');
  208. *
  209. * @throws InvalidArgumentException If invalid argument identifier provided
  210. * @param string|callable $editor
  211. */
  212. public function setEditor($editor)
  213. {
  214. if(!is_callable($editor) && !isset($this->editors[$editor])) {
  215. throw new InvalidArgumentException(
  216. "Unknown editor identifier: $editor. Known editors:" .
  217. implode(",", array_keys($this->editors))
  218. );
  219. }
  220. $this->editor = $editor;
  221. }
  222. /**
  223. * Given a string file path, and an integer file line,
  224. * executes the editor resolver and returns, if available,
  225. * a string that may be used as the href property for that
  226. * file reference.
  227. *
  228. * @throws InvalidArgumentException If editor resolver does not return a string
  229. * @param string $filePath
  230. * @param int $line
  231. * @return string|bool
  232. */
  233. public function getEditorHref($filePath, $line)
  234. {
  235. if($this->editor === null) {
  236. return false;
  237. }
  238. $editor = $this->editor;
  239. if(is_string($editor)) {
  240. $editor = $this->editors[$editor];
  241. }
  242. if(is_callable($editor)) {
  243. $editor = call_user_func($editor, $filePath, $line);
  244. }
  245. // Check that the editor is a string, and replace the
  246. // %line and %file placeholders:
  247. if(!is_string($editor)) {
  248. throw new InvalidArgumentException(
  249. __METHOD__ . " should always resolve to a string; got something else instead"
  250. );
  251. }
  252. $editor = str_replace("%line", rawurlencode($line), $editor);
  253. $editor = str_replace("%file", rawurlencode($filePath), $editor);
  254. return $editor;
  255. }
  256. /**
  257. * @var string
  258. */
  259. public function setPageTitle($title)
  260. {
  261. $this->pageTitle = (string) $title;
  262. }
  263. /**
  264. * @return string
  265. */
  266. public function getPageTitle()
  267. {
  268. return $this->pageTitle;
  269. }
  270. /**
  271. * @return string
  272. */
  273. public function getResourcesPath()
  274. {
  275. return $this->resourcesPath;
  276. }
  277. /**
  278. * @throws InvalidArgumentException If argument is not a valid directory
  279. * @param string $resourcesPath
  280. */
  281. public function setResourcesPath($resourcesPath)
  282. {
  283. if(!is_dir($resourcesPath)) {
  284. throw new InvalidArgumentException(
  285. "$resourcesPath is not a valid directory"
  286. );
  287. }
  288. $this->resourcesPath = $resourcesPath;
  289. }
  290. }