PageRenderTime 49ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/Debug/src/debug.php

https://github.com/Yannix/zetacomponents
PHP | 466 lines | 169 code | 36 blank | 261 comment | 11 complexity | 06cd962a540fdc461acc4a3ccaf68ff2 MD5 | raw file
  1. <?php
  2. /**
  3. * File containing the ezcDebug class.
  4. *
  5. * Licensed to the Apache Software Foundation (ASF) under one
  6. * or more contributor license agreements. See the NOTICE file
  7. * distributed with this work for additional information
  8. * regarding copyright ownership. The ASF licenses this file
  9. * to you under the Apache License, Version 2.0 (the
  10. * "License"); you may not use this file except in compliance
  11. * with the License. You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing,
  16. * software distributed under the License is distributed on an
  17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18. * KIND, either express or implied. See the License for the
  19. * specific language governing permissions and limitations
  20. * under the License.
  21. *
  22. * @package Debug
  23. * @version //autogentag//
  24. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  25. */
  26. /**
  27. * The ezcDebug class provides functionality to format and store debug messages and timers.
  28. *
  29. * The functionality of the Debug component is two folded:
  30. * - Debug log messages
  31. * - Timers
  32. *
  33. * The log messages are heavily based on the {@link EventLog} log messages. In fact
  34. * internally the EventLog is used with its own log writer. The {@link log()} method
  35. * is almost the same as from the EventLog. The next example demonstrates how to instantiate the
  36. * ezcDebug class and write some log messages:
  37. * <code>
  38. * $debug = ezcDebug::getInstance();
  39. * $debug->log( "Connecting with the paynet server", 2 );
  40. * // ...
  41. * $debug->log( "Connection failed, retrying in 5 seconds", 1 );
  42. * // ...
  43. * $debug->log( "Could not connect with the server", 0 );
  44. * </code>
  45. *
  46. * The second parameter of the log method is the verbosity. This is a number that
  47. * specifies the importance of the log message. That makes it easier to sort out messages of less importance.
  48. * In this example, we assumed the more important the message, the lower the
  49. * verbosity number.
  50. *
  51. * The ezcDebug timer is designed to allow the next two timing methods:
  52. * - Timers, the time between two points in the program.
  53. * - Accumulators, gets the relative time after the script started.
  54. *
  55. * The "Timers" are simply set with the methods {@link startTimer()} and {@link stopTimer()}. The next example
  56. * demonstrates the timing of a simple calculation:
  57. * <code>
  58. * $debug = ezcDebug::getInstance();
  59. * $debug->startTimer( "Simple calculation" );
  60. *
  61. * // Simple calculation
  62. * $result = 4 + 6;
  63. *
  64. * $debug->stopTimer( "Simple calculation" ); // Parameter can be omitted.
  65. * </code>
  66. *
  67. * To get timing points, accumulators, use the {@link switchTimer()} method. This is shown in the next example:
  68. * <code>
  69. * $debug = ezcDebug::getInstance();
  70. * $debug->startTimer( "My script" );
  71. * // ...
  72. * $debug->switchTimer( "Reading ini file" );
  73. * // ...
  74. * $debug->switchTimer( "Initializing template parser" );
  75. * // ...
  76. * $debug->switchTimer( "Parsing" );
  77. * // ...
  78. * $debug->stopTimer();
  79. * </code>
  80. *
  81. * @property ezcDebugOptions $options
  82. * Options to configure the behaviour of ezcDebug, including stack
  83. * trace behaviours.
  84. *
  85. * @package Debug
  86. * @version //autogentag//
  87. * @mainclass
  88. */
  89. class ezcDebug
  90. {
  91. /**
  92. * Properties.
  93. *
  94. * @var array(string=>mixed)
  95. */
  96. protected $properties = array();
  97. /**
  98. * Instance of the singleton ezcDebug object.
  99. *
  100. * Use the getInstance() method to retrieve the instance.
  101. *
  102. * @var ezcDebug
  103. */
  104. private static $instance = null;
  105. /**
  106. * The formatter that generates the debug output.
  107. *
  108. * @var ezcDebugFormatter
  109. */
  110. private $formatter = null;
  111. /**
  112. * A pointer to the logging system.
  113. *
  114. * @var ezcLog
  115. */
  116. private $log = null;
  117. /**
  118. * The timing object used to store timing information.
  119. *
  120. * @var ezcDebugTimer
  121. */
  122. private $timer = null;
  123. /**
  124. * The writer that holds debug output.
  125. *
  126. * @var ezcLogWriter
  127. */
  128. private $writer = null;
  129. /**
  130. * Constructs a new debug object and attaches it to the log object.
  131. *
  132. * This method is private because the getInstance() should be called.
  133. */
  134. private function __construct()
  135. {
  136. $this->options = new ezcDebugOptions();
  137. $original = ezcLog::getInstance();
  138. $this->log = clone( $original );
  139. $this->log->reset();
  140. $this->log->setMapper( new ezcLogFilterSet() );
  141. // Set the writer.
  142. $this->writer = new ezcDebugMemoryWriter();
  143. $filter = new ezcLogFilter();
  144. $filter->severity = ezcLog::DEBUG;
  145. $this->log->getMapper()->appendRule( new ezcLogFilterRule( $filter, $this->writer, true ) );
  146. $this->reset();
  147. }
  148. /**
  149. * Property get access.
  150. *
  151. * @throws ezcBasePropertyNotFoundException
  152. * If the given property could not be found.
  153. * @param string $propertyName
  154. * @ignore
  155. */
  156. public function __get( $propertyName )
  157. {
  158. if ( $this->__isset( $propertyName ) )
  159. {
  160. return $this->properties[$propertyName];
  161. }
  162. throw new ezcBasePropertyNotFoundException( $propertyName );
  163. }
  164. /**
  165. * Property set access.
  166. *
  167. * @throws ezcBasePropertyNotFoundException
  168. * @param string $propertyName
  169. * @param string $propertyValue
  170. * @ignore
  171. */
  172. public function __set( $propertyName, $propertyValue )
  173. {
  174. switch ( $propertyName )
  175. {
  176. case 'options':
  177. if ( !( $propertyValue instanceof ezcDebugOptions ) )
  178. {
  179. throw new ezcBaseValueException(
  180. $propertyName,
  181. $propertyValue,
  182. 'ezcDebugOptions'
  183. );
  184. }
  185. break;
  186. default:
  187. throw new ezcBasePropertyNotFoundException( $propertyName );
  188. }
  189. $this->properties[$propertyName] = $propertyValue;
  190. }
  191. /**
  192. * Property isset access.
  193. *
  194. * @param string $propertyName
  195. * @return bool
  196. * @ignore
  197. */
  198. public function __isset( $propertyName )
  199. {
  200. return array_key_exists( $propertyName, $this->properties );
  201. }
  202. /**
  203. * Resets the log messages and timer information.
  204. *
  205. * @return void
  206. */
  207. public function reset()
  208. {
  209. $this->writer->reset();
  210. $this->timer = new ezcDebugTimer();
  211. }
  212. /**
  213. * Returns the instance of this class.
  214. *
  215. * When the ezcDebug instance is created it is automatically added to the instance
  216. * of ezcLog.
  217. *
  218. * @return ezcDebug
  219. */
  220. public static function getInstance()
  221. {
  222. if ( is_null( self::$instance ))
  223. {
  224. self::$instance = new ezcDebug();
  225. ezcBaseInit::fetchConfig( 'ezcInitDebug', self::$instance );
  226. }
  227. return self::$instance;
  228. }
  229. /**
  230. * Returns the instance of the EventLog used in this class.
  231. *
  232. * The returned instance is not the same as retrieved via the
  233. * ezcLog::getInstance() method.
  234. *
  235. * @return ezcLog
  236. */
  237. public function getEventLog()
  238. {
  239. return $this->log;
  240. }
  241. /**
  242. * Sets the formatter $reporter for the output.
  243. *
  244. * If no formatter is set {@link ezcDebugHtmlReporter} will be used by default.
  245. *
  246. * @param ezcDebugOutputFormatter $formatter
  247. * @return void
  248. */
  249. public function setOutputFormatter( ezcDebugOutputFormatter $formatter )
  250. {
  251. $this->formatter = $formatter;
  252. }
  253. /**
  254. * Returns the formatted debug output.
  255. *
  256. * @return string
  257. */
  258. public function generateOutput()
  259. {
  260. if ( is_null( $this->formatter ) )
  261. $this->formatter = new ezcDebugHtmlFormatter();
  262. return $this->formatter->generateOutput( $this->writer->getStructure(), $this->timer->getTimeData() );
  263. }
  264. /**
  265. * Starts the timer with the identifier $name.
  266. *
  267. * Optionally, a timer group can be given with the $group parameter.
  268. *
  269. * @param string $name
  270. * @param string $group
  271. */
  272. public function startTimer( $name, $group = null )
  273. {
  274. $this->timer->startTimer( $name, $group );
  275. }
  276. /**
  277. * Stores the time from the running timer, and starts a new timer.
  278. *
  279. * Stores the time for $oldTimer (maybe omitted if only 1 timer is running)
  280. * and starts a new timer with $newName.
  281. *
  282. * @param string $newName
  283. * @param string|bool $oldName
  284. */
  285. public function switchTimer( $newName, $oldName = false )
  286. {
  287. $this->timer->switchTimer( $newName, $oldName );
  288. }
  289. /**
  290. * Stops the timer identified by $name.
  291. *
  292. * $name can be omitted (false) if only one timer is running.
  293. *
  294. * @param string|bool $name
  295. */
  296. public function stopTimer( $name = false )
  297. {
  298. $this->timer->stopTimer( $name );
  299. }
  300. /**
  301. * Writes the debug message $message with verbosity $verbosity.
  302. *
  303. * Arbitrary $extraInfo can be submitted. If $stackTrace is set to true, a
  304. * stack trace will be stored at the current program position.
  305. *
  306. * @param string $message
  307. * @param int $verbosity
  308. * @param array(string=>string) $extraInfo
  309. * @param bool $stackTrace
  310. */
  311. public function log( $message, $verbosity, array $extraInfo = array(), $stackTrace = false )
  312. {
  313. // Add the verbosity
  314. $extraInfo = array_merge( array( "verbosity" => $verbosity ), $extraInfo );
  315. if ( $this->options->stackTrace === true || $stackTrace === true )
  316. {
  317. $extraInfo['stackTrace'] = $this->getStackTrace();
  318. }
  319. $this->log->log( $message, ezcLog::DEBUG, $extraInfo );
  320. }
  321. /**
  322. * Returns a stack trace iterator for the current call.
  323. *
  324. * Returns a
  325. * - {@link ezcDebugXdebugStacktraceIterator} if Xdebug is available
  326. * - {@link ezcDebugPhpStacktraceIterator} otherwise
  327. * representing a stack trace of the current function environment.
  328. *
  329. * @return ezcDebugStacktraceIterator
  330. */
  331. private function getStackTrace()
  332. {
  333. if ( extension_loaded( 'xdebug' ) )
  334. {
  335. return new ezcDebugXdebugStacktraceIterator(
  336. xdebug_get_function_stack(),
  337. 2,
  338. $this->options
  339. );
  340. }
  341. else
  342. {
  343. return new ezcDebugPhpStacktraceIterator(
  344. debug_backtrace(),
  345. 2,
  346. $this->options
  347. );
  348. }
  349. }
  350. /**
  351. * Dispatches the message and error type to the correct debug or log
  352. * function.
  353. *
  354. * This function should be used as the set_error_handler from the
  355. * trigger_error function.
  356. *
  357. * Use for example the following code in your application:
  358. *
  359. * <code>
  360. * function debugHandler( $a, $b, $c, $d )
  361. * {
  362. * ezcDebug::debugHandler( $a, $b, $c, $d );
  363. * }
  364. *
  365. * set_error_handler( "debugHandler" );
  366. * </code>
  367. *
  368. * Use trigger_error() to log warning, error, etc:
  369. *
  370. * <code>
  371. * trigger_error( "[Paynet, templates] Cannot load template", E_USER_WARNING );
  372. * </code>
  373. *
  374. * See the PHP documentation of
  375. * {@link http://php.net/trigger_error trigger_error} for more information.
  376. *
  377. * @param int $errno
  378. * @param string $errstr
  379. * @param string $errfile
  380. * @param int $errline
  381. * @return void
  382. */
  383. public static function debugHandler( $errno, $errstr, $errfile, $errline )
  384. {
  385. $debug = ezcDebug::getInstance();
  386. $log = $debug->getEventLog();
  387. preg_match(
  388. '/^\s*(?:\[([^,\]]*)(?:,\s(.*))?\])?\s*(?:(\d+):)?\s*(.*)$/',
  389. $errstr,
  390. $matches
  391. );
  392. $message = ( $matches[4] === '' ? false : $matches[4] );
  393. $verbosity = ( $matches[3] === '' ? false : $matches[3] );
  394. if ( strlen( $matches[2] ) == 0 )
  395. {
  396. $category = ( $matches[1] === '' ? $log->category : $matches[1] );
  397. $source = $log->source;
  398. }
  399. else
  400. {
  401. $category = $matches[2];
  402. $source = $matches[1];
  403. }
  404. $severity = false;
  405. switch ( $errno )
  406. {
  407. case E_USER_NOTICE:
  408. $severity = ezcLog::NOTICE;
  409. break;
  410. case E_USER_WARNING:
  411. $severity = ezcLog::WARNING;
  412. break;
  413. case E_USER_ERROR:
  414. $severity = ezcLog::ERROR;
  415. break;
  416. }
  417. $debug->log(
  418. $message,
  419. $severity,
  420. array(
  421. 'source' => $source,
  422. 'category' => $category,
  423. 'verbosity' => $verbosity,
  424. 'file' => $errfile,
  425. 'line' => $errline
  426. )
  427. );
  428. }
  429. }
  430. ?>