PageRenderTime 71ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/shopp/core/model/Error.php

https://bitbucket.org/sanders_nick/legacy-media
PHP | 624 lines | 289 code | 75 blank | 260 comment | 60 complexity | 3b16afcc60bc7e71238eab3d25b376f0 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.1, GPL-2.0, BSD-3-Clause, GPL-3.0
  1. <?php
  2. /**
  3. * Error.php
  4. * Error management system for Shopp
  5. *
  6. * @author Jonathan Davis
  7. * @version 1.1
  8. * @copyright Ingenesis Limited, 28 March, 2008
  9. * @license GNU GPL version 3 (or later) {@see license.txt}
  10. * @package shopp
  11. * @subpackage errors
  12. **/
  13. define('SHOPP_ERR',1); // Shopper visible general Shopp/shopping errors
  14. define('SHOPP_TRXN_ERR',2); // Transaction errors (third-party service errors)
  15. define('SHOPP_AUTH_ERR',4); // Authorization errors (login, credential problems)
  16. define('SHOPP_COMM_ERR',8); // Communication errors (connectivity)
  17. define('SHOPP_STOCK_ERR',16); // Inventory-related warnings (low stock, out-of-stock)
  18. define('SHOPP_ADDON_ERR',32); // Shopp module errors (bad descriptors, core version requriements)
  19. define('SHOPP_ADMIN_ERR',64); // Admin errors (for logging)
  20. define('SHOPP_DB_ERR',128); // DB errors (for logging)
  21. define('SHOPP_PHP_ERR',256); // PHP errors (for logging)
  22. define('SHOPP_ALL_ERR',1024); // All errors (for logging)
  23. define('SHOPP_DEBUG_ERR',2048); // Debug-only (for logging)
  24. /**
  25. * ShoppErrors class
  26. *
  27. * The error message management class that allows other
  28. * systems to subscribe to notifications when errors are
  29. * triggered.
  30. *
  31. * @author Jonathan Davis
  32. * @since 1.0
  33. * @package shopp
  34. * @subpackage errors
  35. **/
  36. class ShoppErrors {
  37. private static $instance;
  38. var $errors = array(); // Error message registry
  39. var $notifications; // Notification subscription registry
  40. var $reporting = SHOPP_ALL_ERR; // level of reporting
  41. /**
  42. * Setup error system and PHP error capture
  43. *
  44. * @author Jonathan Davis
  45. * @since 1.0
  46. *
  47. * @return void
  48. **/
  49. function __construct ($level = SHOPP_ALL_ERR) {
  50. $error_logging = shopp_setting('error_logging');
  51. if ( $error_logging ) $level = $error_logging;
  52. if (defined('WP_DEBUG') && WP_DEBUG) $this->reporting = SHOPP_DEBUG_ERR;
  53. if ($level > $this->reporting) $this->reporting = $level;
  54. $this->notifications = new CallbackSubscription();
  55. $types = E_ALL;// ^ E_NOTICE;
  56. if (defined('WP_DEBUG') && WP_DEBUG) $types = E_ALL;
  57. // Handle PHP errors
  58. if ($this->reporting >= SHOPP_PHP_ERR)
  59. set_error_handler(array($this,'php'),$types);
  60. add_action('init', array(&$this, 'init'), 5);
  61. }
  62. function init () {
  63. ShoppingObject::store('errors', $this->errors);
  64. foreach( $this->errors as $index => $error ) {
  65. if ( $error->remove ) unset($this->errors[$index]);
  66. }
  67. }
  68. public static function instance () {
  69. if ( ! self::$instance instanceof self )
  70. self::$instance = new self();
  71. return self::$instance;
  72. }
  73. function set_loglevel () {
  74. $this->reporting = shopp_setting('error_logging');
  75. }
  76. /**
  77. * Adds a ShoppError to the registry
  78. *
  79. * @author Jonathan Davis
  80. * @since 1.1
  81. *
  82. * @param ShoppError $ShoppError The ShoppError object to add
  83. * @return void
  84. **/
  85. function add ($ShoppError) {
  86. if (isset($ShoppError->code)) $this->errors[$ShoppError->code] = $ShoppError;
  87. else $this->errors[] = $ShoppError;
  88. $this->notifications->send($ShoppError);
  89. }
  90. /**
  91. * Gets all errors up to a specified error level
  92. *
  93. * @author Jonathan Davis
  94. * @since 1.1
  95. *
  96. * @param int $level The maximum error level
  97. * @return array A list of errors
  98. **/
  99. function get ($level=SHOPP_DEBUG_ERR) {
  100. $errors = array();
  101. foreach ($this->errors as &$error)
  102. if ($error->level <= $level) $errors[] = &$error;
  103. return $errors;
  104. }
  105. /**
  106. * Gets all errors of a specific error level
  107. *
  108. * @author Jonathan Davis
  109. * @since 1.1
  110. *
  111. * @param int $level The level of errors to retrieve
  112. * @return array A list of errors
  113. **/
  114. function level ($level=SHOPP_ALL_ERR) {
  115. $errors = array();
  116. foreach ($this->errors as &$error)
  117. if ($error->level == $level) $errors[] = &$error;
  118. return $errors;
  119. }
  120. /**
  121. * Gets an error message with a specific error code
  122. *
  123. * @author Jonathan Davis
  124. * @since 1.1
  125. *
  126. * @param string $code The name of the error code
  127. * @return ShoppError The error object
  128. **/
  129. function code ($code) {
  130. if (!empty($code) && isset($this->errors[$code]))
  131. return $this->errors[$code];
  132. }
  133. /**
  134. * Gets all errors from a specified source (object)
  135. *
  136. * @author Jonathan Davis
  137. * @since 1.1
  138. *
  139. * @param string $source The source object of errors
  140. * @return array A list of errors
  141. **/
  142. function source ($source) {
  143. if (empty($source)) return array();
  144. $errors = array();
  145. foreach ($this->errors as &$error)
  146. if ($error->source == $source) $errors[] = &$error;
  147. return $errors;
  148. }
  149. /**
  150. * Determines if any errors exist up to the specified error level
  151. *
  152. * @author Jonathan Davis
  153. * @since 1.0
  154. *
  155. * @param int $level The maximum level to look for errors in
  156. * @return void Description...
  157. **/
  158. function exist ($level=SHOPP_DEBUG_ERR) {
  159. $errors = array();
  160. foreach ($this->errors as &$error)
  161. if ($error->level <= $level) $errors[] = &$error;
  162. return (count($errors) > 0);
  163. }
  164. /**
  165. * Removes an error from the registry
  166. *
  167. * @author Jonathan Davis
  168. * @since 1.1
  169. *
  170. * @param ShoppError $error The ShoppError object to remove
  171. * @return boolean True when removed, false if removal failed
  172. **/
  173. function remove ($error) {
  174. if (!isset($this->errors[$error->code])) return false;
  175. $this->errors[$error->code]->remove = true;
  176. return true;
  177. }
  178. /**
  179. * Removes all errors from the error registry
  180. *
  181. * @author Jonathan Davis
  182. * @since 1.0
  183. *
  184. * @return void
  185. **/
  186. function reset () {
  187. $this->errors = array();
  188. }
  189. /**
  190. * Reports PHP generated errors to the Shopp error system
  191. *
  192. * @author Jonathan Davis
  193. * @since 1.0
  194. *
  195. * @param int $number The error type
  196. * @param string $message The PHP error message
  197. * @param string $file The file the error occurred in
  198. * @param int $line The line number the error occurred at in the file
  199. * @return boolean
  200. **/
  201. function php ($number, $message, $file, $line) {
  202. if (strpos($file,SHOPP_PATH) === false) return true;
  203. $debug = '';
  204. if (SHOPP_DEBUG) $debug = sprintf(" [%s, line %d]", basename($file),$line);
  205. new ShoppError($message.$debug,'php_error',SHOPP_PHP_ERR,
  206. array('file'=>$file,'line'=>$line,'phperror'=>$number));
  207. if ($number == E_USER_ERROR) return false; // Always show fatal errors
  208. return true;
  209. }
  210. } //end ShoppErrors
  211. /**
  212. * ShoppError class
  213. *
  214. * Triggers an error that is handled by the Shopp error system.
  215. *
  216. * @author Jonathan Davis
  217. * @since 1.0
  218. * @package shopp
  219. * @subpackage errors
  220. **/
  221. class ShoppError {
  222. var $code;
  223. var $source;
  224. var $messages;
  225. var $level;
  226. var $data = array();
  227. var $remove = false;
  228. /**
  229. * Creates and registers a new error
  230. *
  231. * @author Jonathan Davis
  232. * @since 1.1
  233. *
  234. * @return void Description...
  235. **/
  236. function __construct ($message='',$code='',$level=SHOPP_ERR,$data='') {
  237. $Errors = ShoppErrors();
  238. if (!is_a($Errors,'ShoppErrors')) return;
  239. if ($level > $Errors->reporting) return;
  240. if (empty($message)) return;
  241. $php = array(
  242. 1 => 'ERROR',
  243. 2 => 'WARNING',
  244. 4 => 'PARSE ERROR',
  245. 8 => 'NOTICE',
  246. 16 => 'CORE ERROR',
  247. 32 => 'CORE WARNING',
  248. 64 => 'COMPILE ERROR',
  249. 128 => 'COMPILE WARNING',
  250. 256 => 'Fatal error',
  251. 512 => 'Warning',
  252. 1024 => 'Notice',
  253. 2048 => 'STRICT NOTICE',
  254. 4096 => 'RECOVERABLE ERROR'
  255. );
  256. $debug = debug_backtrace();
  257. $this->code = $code;
  258. $this->messages[] = $message;
  259. $this->level = $level;
  260. $this->data = $data;
  261. $this->debug = $debug[1];
  262. // Handle template errors
  263. if (isset($this->debug['class']) && $this->debug['class'] == "ShoppErrors")
  264. $this->debug = $debug[2];
  265. if (isset($data['file'])) $this->debug['file'] = $data['file'];
  266. if (isset($data['line'])) $this->debug['line'] = $data['line'];
  267. unset($this->debug['object'],$this->debug['args']);
  268. $this->source = "Shopp";
  269. if (isset($this->debug['class'])) $this->source = $this->debug['class'];
  270. if (isset($this->data['phperror']) && isset($php[$this->data['phperror']]))
  271. $this->source = "PHP ".$php[$this->data['phperror']];
  272. $Errors = ShoppErrors();
  273. if (!empty($Errors)) $Errors->add($this);
  274. }
  275. /**
  276. * Prevent excess data from being stored in the session
  277. *
  278. * @author Jonathan Davis
  279. * @since 1.1
  280. *
  281. * @return void
  282. **/
  283. function __sleep () {
  284. return array('remove','code','source','messages','level');
  285. }
  286. /**
  287. * Tests if the error message is blank
  288. *
  289. * @author Jonathan Davis
  290. * @since 1.1
  291. *
  292. * @return boolean True for blank error messages
  293. **/
  294. function blank () {
  295. return (join('',$this->messages) == "");
  296. }
  297. /**
  298. * Displays messages registered to a specific error code
  299. *
  300. * @author Jonathan Davis
  301. * @since 1.1
  302. *
  303. * @param boolean $remove (optional) (Default true) Removes the error after retrieving it
  304. * @param boolean $source Prefix the error with the source object where the error was triggered
  305. * @param string $delimiter The delimeter used to join multiple error messages
  306. * @return string A collection of concatenated error messages
  307. **/
  308. function message ($remove=false,$source=false,$delimiter="\n") {
  309. $string = "";
  310. // Show source if debug is on, or not a general error message
  311. if (((defined('WP_DEBUG') && WP_DEBUG) || $this->level > SHOPP_ERR) &&
  312. !empty($this->source) && $source) $string .= "$this->source: ";
  313. $string .= join($delimiter,$this->messages);
  314. if ($remove) {
  315. $Errors = ShoppErrors();
  316. if (!empty($Errors->errors)) $Errors->remove($this);
  317. }
  318. return $string;
  319. }
  320. }
  321. /**
  322. * ShoppErrorLogging class
  323. *
  324. * Subscribes to error notifications in order to log any errors
  325. * generated.
  326. *
  327. * @author Jonathan Davis
  328. * @since 1.0
  329. * @package shopp
  330. * @subpackage errors
  331. **/
  332. class ShoppErrorLogging {
  333. private static $instance;
  334. var $dir;
  335. var $file = "shopp_debug.log";
  336. var $logfile;
  337. var $log;
  338. var $loglevel = 0;
  339. /**
  340. * Setup for error logging
  341. *
  342. * @author Jonathan Davis
  343. * @since 1.0
  344. *
  345. * @return void
  346. **/
  347. function __construct ($loglevel=0) {
  348. $loglevelsetting = shopp_setting('error_logging');
  349. $this->loglevel = $loglevelsetting ? $loglevelsetting : $loglevel;
  350. $this->dir = defined('SHOPP_LOG_PATH') ? SHOPP_LOG_PATH : sys_get_temp_dir();
  351. $this->dir = sanitize_path($this->dir); // Windows path sanitiation
  352. $siteurl = parse_url(get_bloginfo('siteurl'));
  353. $sub = (!empty($path)?"_".sanitize_title_with_dashes($path):'');
  354. $this->logfile = trailingslashit($this->dir).$siteurl['host'].$sub."_".$this->file;
  355. $Errors = &ShoppErrors();
  356. $Errors->notifications->subscribe($this,'log');
  357. }
  358. public static function instance() {
  359. if ( ! self::$instance )
  360. self::$instance = new self();
  361. return self::$instance;
  362. }
  363. function set_loglevel() {
  364. $this->loglevel = shopp_setting('error_logging');
  365. }
  366. /**
  367. * Logs an error to the error log file
  368. *
  369. * @author Jonathan Davis
  370. * @since 1.0
  371. *
  372. * @param ShoppError $error The error object to log
  373. * @return void
  374. **/
  375. function log ($error) {
  376. if ($error->level > $this->loglevel) return;
  377. $debug = "";
  378. if (isset($error->debug['file'])) {
  379. $debug = sprintf("[%s, line %d]", basename($error->debug['file']),$error->debug['line']);
  380. $debug = " ".apply_filters('shopp_error_message_debugdata',$debug,$error->debug);
  381. }
  382. $message = date("Y-m-d H:i:s",time())." - ".$error->message(false,true)."$debug\n";
  383. error_log($message,3,$this->logfile);
  384. error_log($error->message(false,true));
  385. }
  386. /**
  387. * Empties the error log file
  388. *
  389. * @author Jonathan Davis
  390. * @since 1.0
  391. *
  392. * @return void
  393. **/
  394. function reset () {
  395. $this->log = fopen($this->logfile,'w');
  396. fwrite($this->log,'');
  397. fclose($this->log);
  398. }
  399. /**
  400. * Gets the end of the log file
  401. *
  402. * @author Jonathan Davis
  403. * @since 1.0
  404. * @version 1.2
  405. *
  406. * @param int $lines The number of lines to get from the end of the log file
  407. * @param int $buffer Buffer size in bytes to handle each iteration
  408. * @return array List of log file lines
  409. **/
  410. function tail($lines=100,$buffer=4096) {
  411. if (!file_exists($this->logfile)) return;
  412. $f = fopen($this->logfile, 'rb');
  413. // Start at the end
  414. fseek($f, -1, SEEK_END);
  415. // Take into acount files that don't end with a blank line
  416. if("\n" != fread($f, 1)) $lines--;
  417. // Start reading
  418. $output = $chunk = '';
  419. while(ftell($f) > 0 && $lines >= 0) {
  420. $seek = min(ftell($f), $buffer); // Figure out how far to go back
  421. fseek($f, -$seek, SEEK_CUR); // Jump back from the current position
  422. $output = ($chunk = fread($f, $seek)).$output; // Read a buffer chunk and prepend it to our output
  423. fseek($f, -strlen($chunk), SEEK_CUR);// Jump back to where we started reading
  424. $lines -= substr_count($chunk, "\n"); // Decrease our line counter
  425. }
  426. fclose($f);
  427. // Handle over-reading because of buffer size
  428. while ($lines++ < 0)
  429. $output = substr($output, strpos($output, "\n") + 1);
  430. return explode("\n",trim($output));
  431. }
  432. }
  433. /**
  434. * ShoppErrorNotification class
  435. *
  436. * Sends error notification emails when errors are triggered
  437. * to specified recipients
  438. *
  439. * @author Jonathan Davis
  440. * @since 1.0
  441. * @package shopp
  442. * @subpackage errors
  443. **/
  444. class ShoppErrorNotification {
  445. private static $instance;
  446. var $recipients; // Recipient addresses to send to
  447. var $types=0; // Error types to send
  448. /**
  449. * Relays triggered errors to email messages
  450. *
  451. * @author Jonathan Davis
  452. * @since 1.0
  453. *
  454. * @param string $recipients List of email addresses
  455. * @param array $types The types of errors to report
  456. * @return void
  457. **/
  458. function __construct ($recipients='',$types=array()) {
  459. $recipients = shopp_setting('merchant_email');
  460. $types = shopp_setting('error_notifications');
  461. if (empty($recipients)) return;
  462. $this->recipients = $recipients;
  463. foreach ((array)$types as $type) $this->types += $type;
  464. $Errors = &ShoppErrors();
  465. $Errors->notifications->subscribe($this,'notify');
  466. }
  467. public static function instance() {
  468. if ( ! self::$instance )
  469. self::$instance = new self();
  470. return self::$instance;
  471. }
  472. function set_notifications () {
  473. $recipients = shopp_setting('merchant_email');
  474. $types = shopp_setting('error_notifications');
  475. if (empty($recipients)) return;
  476. $this->recipients = $recipients;
  477. foreach ((array)$types as $type) $this->types += $type;
  478. $Errors = ShoppErrors();
  479. $Errors->notifications->subscribe($this,'notify');
  480. }
  481. /**
  482. * Generates and sends an email of an error to the recipient list
  483. *
  484. * @author Jonathan Davis
  485. * @since 1.0
  486. *
  487. * @param ShoppError $error The error object
  488. * @return void
  489. **/
  490. function notify ($error) {
  491. if (!($error->level & $this->types)) return;
  492. $url = parse_url(get_bloginfo('url'));
  493. $_ = array();
  494. $_[] = 'From: "'.get_bloginfo('sitename').'" <shopp@'.$url['host'].'>';
  495. $_[] = 'To: '.$this->recipients;
  496. $_[] = 'Subject: '.__('Shopp Notification','Shopp');
  497. $_[] = '';
  498. $_[] = __('This is an automated notification message generated by the Shopp installation at '.get_bloginfo('url').'.','Shopp');
  499. $_[] = '';
  500. $_[] = $error->message();
  501. $_[] = '';
  502. if (isset($error->debug['file']) && defined('WP_DEBUG'))
  503. $_[] = 'DEBUG: '.basename($error->debug['file']).', line '.$error->debug['line'].'';
  504. shopp_email(join("\n",$_));
  505. }
  506. }
  507. class CallbackSubscription {
  508. var $subscribers = array();
  509. function subscribe ($target,$method) {
  510. if (!isset($this->subscribers[get_class($target)]))
  511. $this->subscribers[get_class($target)] = array(&$target,$method);
  512. }
  513. function send () {
  514. $args = func_get_args();
  515. foreach ($this->subscribers as $callback) {
  516. call_user_func_array($callback,$args);
  517. }
  518. }
  519. }
  520. /**
  521. * Helper to access the error system
  522. *
  523. * @author Jonathan Davis
  524. * @since 1.0
  525. *
  526. * @return void Description...
  527. **/
  528. function ShoppErrors () {
  529. return ShoppErrors::instance();
  530. }
  531. function ShoppErrorLogging () {
  532. return ShoppErrorLogging::instance();
  533. }
  534. function ShoppErrorNotification () {
  535. return ShoppErrorNotification::instance();
  536. }
  537. /**
  538. * Detects ShoppError objects
  539. *
  540. * @author Jonathan Davis
  541. * @since 1.0
  542. *
  543. * @param object $e The object to test
  544. * @return boolean True if the object is a ShoppError
  545. **/
  546. function is_shopperror ($e) {
  547. return (get_class($e) == "ShoppError");
  548. }
  549. ?>