人人商城

Client.php 46KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541
  1. <?php
  2. /*
  3. * This file is part of Raven.
  4. *
  5. * (c) Sentry Team
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * Raven PHP Client
  12. *
  13. * @package raven
  14. */
  15. class Raven_Client
  16. {
  17. const VERSION = '1.9.x-dev';
  18. const PROTOCOL = '6';
  19. const DEBUG = 'debug';
  20. const INFO = 'info';
  21. const WARN = 'warning';
  22. const WARNING = 'warning';
  23. const ERROR = 'error';
  24. const FATAL = 'fatal';
  25. const MESSAGE_LIMIT = 1024;
  26. public $breadcrumbs;
  27. /**
  28. * @var Raven_Context
  29. */
  30. public $context;
  31. public $extra_data;
  32. /**
  33. * @var array|null
  34. */
  35. public $severity_map;
  36. public $store_errors_for_bulk_send = false;
  37. protected $error_handler;
  38. protected $error_types;
  39. /**
  40. * @var Raven_Serializer
  41. */
  42. protected $serializer;
  43. /**
  44. * @var Raven_ReprSerializer
  45. */
  46. protected $reprSerializer;
  47. /**
  48. * @var string
  49. */
  50. protected $app_path;
  51. /**
  52. * @var string[]
  53. */
  54. protected $prefixes;
  55. /**
  56. * @var string[]|null
  57. */
  58. protected $excluded_app_paths;
  59. /**
  60. * @var Callable
  61. */
  62. protected $transport;
  63. public $logger;
  64. /**
  65. * @var string Full URL to Sentry
  66. */
  67. public $server;
  68. public $secret_key;
  69. public $public_key;
  70. public $project;
  71. public $auto_log_stacks;
  72. public $name;
  73. public $site;
  74. public $tags;
  75. public $release;
  76. public $environment;
  77. public $sample_rate;
  78. public $trace;
  79. public $timeout;
  80. public $message_limit;
  81. public $exclude;
  82. public $excluded_exceptions;
  83. public $http_proxy;
  84. protected $send_callback;
  85. public $curl_method;
  86. public $curl_path;
  87. public $curl_ipv4;
  88. public $ca_cert;
  89. public $verify_ssl;
  90. public $curl_ssl_version;
  91. public $trust_x_forwarded_proto;
  92. public $mb_detect_order;
  93. /**
  94. * @var Raven_Processor[]
  95. */
  96. public $processors;
  97. /**
  98. * @var string|int|null
  99. */
  100. public $_lasterror;
  101. /**
  102. * @var object|null
  103. */
  104. protected $_last_sentry_error;
  105. public $_last_event_id;
  106. public $_user;
  107. public $_pending_events;
  108. public $sdk;
  109. /**
  110. * @var Raven_CurlHandler
  111. */
  112. protected $_curl_handler;
  113. /**
  114. * @var resource|null
  115. */
  116. protected $_curl_instance;
  117. /**
  118. * @var bool
  119. */
  120. protected $_shutdown_function_has_been_set;
  121. /**
  122. * @var bool
  123. */
  124. public $useCompression;
  125. public function __construct($options_or_dsn = null, $options = array())
  126. {
  127. if (is_array($options_or_dsn)) {
  128. $options = array_merge($options_or_dsn, $options);
  129. }
  130. if (!is_array($options_or_dsn) && !empty($options_or_dsn)) {
  131. $dsn = $options_or_dsn;
  132. } elseif (!empty($_SERVER['SENTRY_DSN'])) {
  133. $dsn = @$_SERVER['SENTRY_DSN'];
  134. } elseif (!empty($options['dsn'])) {
  135. $dsn = $options['dsn'];
  136. } else {
  137. $dsn = null;
  138. }
  139. if (!empty($dsn)) {
  140. $options = array_merge($options, self::parseDSN($dsn));
  141. }
  142. $this->logger = Raven_Util::get($options, 'logger', 'php');
  143. $this->server = Raven_Util::get($options, 'server');
  144. $this->secret_key = Raven_Util::get($options, 'secret_key');
  145. $this->public_key = Raven_Util::get($options, 'public_key');
  146. $this->project = Raven_Util::get($options, 'project', 1);
  147. $this->auto_log_stacks = (bool) Raven_Util::get($options, 'auto_log_stacks', false);
  148. $this->name = Raven_Util::get($options, 'name', Raven_Compat::gethostname());
  149. $this->site = Raven_Util::get($options, 'site', self::_server_variable('SERVER_NAME'));
  150. $this->tags = Raven_Util::get($options, 'tags', array());
  151. $this->release = Raven_Util::get($options, 'release', null);
  152. $this->environment = Raven_Util::get($options, 'environment', null);
  153. $this->sample_rate = Raven_Util::get($options, 'sample_rate', 1);
  154. $this->trace = (bool) Raven_Util::get($options, 'trace', true);
  155. $this->timeout = Raven_Util::get($options, 'timeout', 2);
  156. $this->message_limit = Raven_Util::get($options, 'message_limit', self::MESSAGE_LIMIT);
  157. $this->exclude = Raven_Util::get($options, 'exclude', array());
  158. $this->excluded_exceptions = Raven_Util::get($options, 'excluded_exceptions', array());
  159. $this->severity_map = null;
  160. $this->http_proxy = Raven_Util::get($options, 'http_proxy');
  161. $this->extra_data = Raven_Util::get($options, 'extra', array());
  162. $this->send_callback = Raven_Util::get($options, 'send_callback', null);
  163. $this->curl_method = Raven_Util::get($options, 'curl_method', 'sync');
  164. $this->curl_path = Raven_Util::get($options, 'curl_path', 'curl');
  165. $this->curl_ipv4 = Raven_Util::get($options, 'curl_ipv4', true);
  166. $this->ca_cert = Raven_Util::get($options, 'ca_cert', static::get_default_ca_cert());
  167. $this->verify_ssl = Raven_Util::get($options, 'verify_ssl', true);
  168. $this->curl_ssl_version = Raven_Util::get($options, 'curl_ssl_version');
  169. $this->trust_x_forwarded_proto = Raven_Util::get($options, 'trust_x_forwarded_proto');
  170. $this->transport = Raven_Util::get($options, 'transport', null);
  171. $this->mb_detect_order = Raven_Util::get($options, 'mb_detect_order', null);
  172. $this->error_types = Raven_Util::get($options, 'error_types', null);
  173. // app path is used to determine if code is part of your application
  174. $this->setAppPath(Raven_Util::get($options, 'app_path', null));
  175. $this->setExcludedAppPaths(Raven_Util::get($options, 'excluded_app_paths', null));
  176. // a list of prefixes used to coerce absolute paths into relative
  177. $this->setPrefixes(Raven_Util::get($options, 'prefixes', static::getDefaultPrefixes()));
  178. $this->processors = $this->setProcessorsFromOptions($options);
  179. $this->_lasterror = null;
  180. $this->_last_sentry_error = null;
  181. $this->_curl_instance = null;
  182. $this->_last_event_id = null;
  183. $this->_user = null;
  184. $this->_pending_events = array();
  185. $this->context = new Raven_Context();
  186. $this->breadcrumbs = new Raven_Breadcrumbs();
  187. $this->_shutdown_function_has_been_set = false;
  188. $this->useCompression = function_exists('gzcompress');
  189. $this->sdk = Raven_Util::get($options, 'sdk', array(
  190. 'name' => 'sentry-php',
  191. 'version' => self::VERSION,
  192. ));
  193. $this->serializer = new Raven_Serializer($this->mb_detect_order);
  194. $this->reprSerializer = new Raven_ReprSerializer($this->mb_detect_order);
  195. if ($this->curl_method == 'async') {
  196. $this->_curl_handler = new Raven_CurlHandler($this->get_curl_options());
  197. }
  198. $this->transaction = new Raven_TransactionStack();
  199. if (static::is_http_request() && isset($_SERVER['PATH_INFO'])) {
  200. // @codeCoverageIgnoreStart
  201. $this->transaction->push($_SERVER['PATH_INFO']);
  202. // @codeCoverageIgnoreEnd
  203. }
  204. if (Raven_Util::get($options, 'install_default_breadcrumb_handlers', true)) {
  205. $this->registerDefaultBreadcrumbHandlers();
  206. }
  207. if (Raven_Util::get($options, 'install_shutdown_handler', true)) {
  208. $this->registerShutdownFunction();
  209. }
  210. $this->triggerAutoload();
  211. }
  212. public function __destruct()
  213. {
  214. // Force close curl resource
  215. $this->close_curl_resource();
  216. }
  217. /**
  218. * Destruct all objects contain link to this object
  219. *
  220. * This method can not delete shutdown handler
  221. */
  222. public function close_all_children_link()
  223. {
  224. $this->processors = array();
  225. }
  226. /**
  227. * Installs any available automated hooks (such as error_reporting).
  228. */
  229. public function install()
  230. {
  231. if ($this->error_handler) {
  232. throw new Raven_Exception(sprintf('%s->install() must only be called once', get_class($this)));
  233. }
  234. $this->error_handler = new Raven_ErrorHandler($this, false, $this->error_types);
  235. $this->error_handler->registerExceptionHandler();
  236. $this->error_handler->registerErrorHandler();
  237. $this->error_handler->registerShutdownFunction();
  238. if ($this->_curl_handler) {
  239. $this->_curl_handler->registerShutdownFunction();
  240. }
  241. return $this;
  242. }
  243. public function getRelease()
  244. {
  245. return $this->release;
  246. }
  247. public function setRelease($value)
  248. {
  249. $this->release = $value;
  250. return $this;
  251. }
  252. public function getEnvironment()
  253. {
  254. return $this->environment;
  255. }
  256. public function setEnvironment($value)
  257. {
  258. $this->environment = $value;
  259. return $this;
  260. }
  261. /**
  262. * Note: Prior to PHP 5.6, a stream opened with php://input can
  263. * only be read once;
  264. *
  265. * @see http://php.net/manual/en/wrappers.php.php
  266. */
  267. protected static function getInputStream()
  268. {
  269. if (PHP_VERSION_ID < 50600) {
  270. return null;
  271. }
  272. return file_get_contents('php://input');
  273. }
  274. private static function getDefaultPrefixes()
  275. {
  276. $value = get_include_path();
  277. return explode(PATH_SEPARATOR, $value);
  278. }
  279. private static function _convertPath($value)
  280. {
  281. $path = @realpath($value);
  282. if ($path === false) {
  283. $path = $value;
  284. }
  285. // we need app_path to have a trailing slash otherwise
  286. // base path detection becomes complex if the same
  287. // prefix is matched
  288. if ($path{0} === DIRECTORY_SEPARATOR && substr($path, -1) !== DIRECTORY_SEPARATOR) {
  289. $path .= DIRECTORY_SEPARATOR;
  290. }
  291. return $path;
  292. }
  293. public function getAppPath()
  294. {
  295. return $this->app_path;
  296. }
  297. public function setAppPath($value)
  298. {
  299. if ($value) {
  300. $this->app_path = static::_convertPath($value);
  301. } else {
  302. $this->app_path = null;
  303. }
  304. return $this;
  305. }
  306. public function getExcludedAppPaths()
  307. {
  308. return $this->excluded_app_paths;
  309. }
  310. public function setExcludedAppPaths($value)
  311. {
  312. if ($value) {
  313. $excluded_app_paths = array();
  314. // We should be able to exclude a php files
  315. foreach ((array) $value as $path) {
  316. $excluded_app_paths[] = substr($path, -4) !== '.php' ? self::_convertPath($path) : $path;
  317. }
  318. } else {
  319. $excluded_app_paths = null;
  320. }
  321. $this->excluded_app_paths = $excluded_app_paths;
  322. return $this;
  323. }
  324. public function getPrefixes()
  325. {
  326. return $this->prefixes;
  327. }
  328. /**
  329. * @param array $value
  330. * @return Raven_Client
  331. */
  332. public function setPrefixes($value)
  333. {
  334. $this->prefixes = $value ? array_map(array($this, '_convertPath'), $value) : $value;
  335. return $this;
  336. }
  337. public function getSendCallback()
  338. {
  339. return $this->send_callback;
  340. }
  341. public function setSendCallback($value)
  342. {
  343. $this->send_callback = $value;
  344. return $this;
  345. }
  346. public function getTransport()
  347. {
  348. return $this->transport;
  349. }
  350. public function getServerEndpoint($value = '')
  351. {
  352. return $this->server;
  353. }
  354. public static function getUserAgent()
  355. {
  356. return 'sentry-php/' . self::VERSION;
  357. }
  358. /**
  359. * Set a custom transport to override how Sentry events are sent upstream.
  360. *
  361. * The bound function will be called with ``$client`` and ``$data`` arguments
  362. * and is responsible for encoding the data, authenticating, and sending
  363. * the data to the upstream Sentry server.
  364. *
  365. * @param Callable $value Function to be called
  366. * @return Raven_Client
  367. */
  368. public function setTransport($value)
  369. {
  370. $this->transport = $value;
  371. return $this;
  372. }
  373. /**
  374. * @return string[]|Raven_Processor[]
  375. */
  376. public static function getDefaultProcessors()
  377. {
  378. return array(
  379. 'Raven_Processor_SanitizeDataProcessor',
  380. );
  381. }
  382. /**
  383. * Sets the Raven_Processor sub-classes to be used when data is processed before being
  384. * sent to Sentry.
  385. *
  386. * @param $options
  387. * @return Raven_Processor[]
  388. */
  389. public function setProcessorsFromOptions($options)
  390. {
  391. $processors = array();
  392. foreach (Raven_util::get($options, 'processors', static::getDefaultProcessors()) as $processor) {
  393. /**
  394. * @var Raven_Processor $new_processor
  395. * @var Raven_Processor|string $processor
  396. */
  397. $new_processor = new $processor($this);
  398. if (isset($options['processorOptions']) && is_array($options['processorOptions'])) {
  399. if (isset($options['processorOptions'][$processor])
  400. && method_exists($processor, 'setProcessorOptions')
  401. ) {
  402. $new_processor->setProcessorOptions($options['processorOptions'][$processor]);
  403. }
  404. }
  405. $processors[] = $new_processor;
  406. }
  407. return $processors;
  408. }
  409. /**
  410. * Parses a Raven-compatible DSN and returns an array of its values.
  411. *
  412. * @param string $dsn Raven compatible DSN
  413. * @return array parsed DSN
  414. *
  415. * @see http://raven.readthedocs.org/en/latest/config/#the-sentry-dsn
  416. */
  417. public static function parseDSN($dsn)
  418. {
  419. switch (strtolower($dsn)) {
  420. case '':
  421. case 'false':
  422. case '(false)':
  423. case 'empty':
  424. case '(empty)':
  425. case 'null':
  426. case '(null)':
  427. return array();
  428. }
  429. $url = parse_url($dsn);
  430. $scheme = (isset($url['scheme']) ? $url['scheme'] : '');
  431. if (!in_array($scheme, array('http', 'https'))) {
  432. throw new InvalidArgumentException(
  433. 'Unsupported Sentry DSN scheme: '.
  434. (!empty($scheme) ? $scheme : '<not set>')
  435. );
  436. }
  437. $netloc = (isset($url['host']) ? $url['host'] : null);
  438. $netloc .= (isset($url['port']) ? ':'.$url['port'] : null);
  439. $rawpath = (isset($url['path']) ? $url['path'] : null);
  440. if ($rawpath) {
  441. $pos = strrpos($rawpath, '/', 1);
  442. if ($pos !== false) {
  443. $path = substr($rawpath, 0, $pos);
  444. $project = substr($rawpath, $pos + 1);
  445. } else {
  446. $path = '';
  447. $project = substr($rawpath, 1);
  448. }
  449. } else {
  450. $project = null;
  451. $path = '';
  452. }
  453. $username = (isset($url['user']) ? $url['user'] : null);
  454. $password = (isset($url['pass']) ? $url['pass'] : null);
  455. if (empty($netloc) || empty($project) || empty($username) || empty($password)) {
  456. throw new InvalidArgumentException('Invalid Sentry DSN: ' . $dsn);
  457. }
  458. return array(
  459. 'server' => sprintf('%s://%s%s/api/%s/store/', $scheme, $netloc, $path, $project),
  460. 'project' => $project,
  461. 'public_key' => $username,
  462. 'secret_key' => $password,
  463. );
  464. }
  465. public function getLastError()
  466. {
  467. return $this->_lasterror;
  468. }
  469. /**
  470. * Given an identifier, returns a Sentry searchable string.
  471. *
  472. * @param mixed $ident
  473. * @return mixed
  474. * @codeCoverageIgnore
  475. */
  476. public function getIdent($ident)
  477. {
  478. // XXX: We don't calculate checksums yet, so we only have the ident.
  479. return $ident;
  480. }
  481. /**
  482. * @param string $message The message (primary description) for the event.
  483. * @param array $params params to use when formatting the message.
  484. * @param string $level Log level group
  485. * @param bool|array $stack
  486. * @param mixed $vars
  487. * @return string|null
  488. * @deprecated
  489. * @codeCoverageIgnore
  490. */
  491. public function message($message, $params = array(), $level = self::INFO,
  492. $stack = false, $vars = null)
  493. {
  494. return $this->captureMessage($message, $params, $level, $stack, $vars);
  495. }
  496. /**
  497. * @param Exception $exception
  498. * @return string|null
  499. * @deprecated
  500. * @codeCoverageIgnore
  501. */
  502. public function exception($exception)
  503. {
  504. return $this->captureException($exception);
  505. }
  506. /**
  507. * Log a message to sentry
  508. *
  509. * @param string $message The message (primary description) for the event.
  510. * @param array $params params to use when formatting the message.
  511. * @param array $data Additional attributes to pass with this event (see Sentry docs).
  512. * @param bool|array $stack
  513. * @param mixed $vars
  514. * @return string|null
  515. */
  516. public function captureMessage($message, $params = array(), $data = array(),
  517. $stack = false, $vars = null)
  518. {
  519. // Gracefully handle messages which contain formatting characters, but were not
  520. // intended to be used with formatting.
  521. if (!empty($params)) {
  522. $formatted_message = vsprintf($message, $params);
  523. } else {
  524. $formatted_message = $message;
  525. }
  526. if ($data === null) {
  527. $data = array();
  528. // support legacy method of passing in a level name as the third arg
  529. } elseif (!is_array($data)) {
  530. $data = array(
  531. 'level' => $data,
  532. );
  533. }
  534. $data['message'] = $formatted_message;
  535. $data['sentry.interfaces.Message'] = array(
  536. 'message' => $message,
  537. 'params' => $params,
  538. 'formatted' => $formatted_message,
  539. );
  540. return $this->capture($data, $stack, $vars);
  541. }
  542. /**
  543. * Log an exception to sentry
  544. *
  545. * @param \Throwable|\Exception $exception The Throwable/Exception object.
  546. * @param array $data Additional attributes to pass with this event (see Sentry docs).
  547. * @param mixed $logger
  548. * @param mixed $vars
  549. * @return string|null
  550. */
  551. public function captureException($exception, $data = null, $logger = null, $vars = null)
  552. {
  553. $has_chained_exceptions = PHP_VERSION_ID >= 50300;
  554. if (in_array(get_class($exception), $this->exclude)) {
  555. return null;
  556. }
  557. foreach ($this->excluded_exceptions as $exclude) {
  558. if ($exception instanceof $exclude) {
  559. return null;
  560. }
  561. }
  562. if ($data === null) {
  563. $data = array();
  564. }
  565. $exc = $exception;
  566. do {
  567. $exc_data = array(
  568. 'value' => $this->serializer->serialize($exc->getMessage()),
  569. 'type' => get_class($exc),
  570. );
  571. /**'exception'
  572. * Exception::getTrace doesn't store the point at where the exception
  573. * was thrown, so we have to stuff it in ourselves. Ugh.
  574. */
  575. $trace = $exc->getTrace();
  576. $frame_where_exception_thrown = array(
  577. 'file' => $exc->getFile(),
  578. 'line' => $exc->getLine(),
  579. );
  580. array_unshift($trace, $frame_where_exception_thrown);
  581. $exc_data['stacktrace'] = array(
  582. 'frames' => Raven_Stacktrace::get_stack_info(
  583. $trace, $this->trace, $vars, $this->message_limit, $this->prefixes,
  584. $this->app_path, $this->excluded_app_paths, $this->serializer, $this->reprSerializer
  585. ),
  586. );
  587. $exceptions[] = $exc_data;
  588. } while ($has_chained_exceptions && $exc = $exc->getPrevious());
  589. $data['exception'] = array(
  590. 'values' => array_reverse($exceptions),
  591. );
  592. if ($logger !== null) {
  593. $data['logger'] = $logger;
  594. }
  595. if (empty($data['level'])) {
  596. if (method_exists($exception, 'getSeverity')) {
  597. $data['level'] = $this->translateSeverity($exception->getSeverity());
  598. } else {
  599. $data['level'] = self::ERROR;
  600. }
  601. }
  602. return $this->capture($data, $trace, $vars);
  603. }
  604. /**
  605. * Capture the most recent error (obtained with ``error_get_last``).
  606. * @return string|null
  607. */
  608. public function captureLastError()
  609. {
  610. if (null === $error = error_get_last()) {
  611. return null;
  612. }
  613. $e = new ErrorException(
  614. @$error['message'], 0, @$error['type'],
  615. @$error['file'], @$error['line']
  616. );
  617. return $this->captureException($e);
  618. }
  619. /**
  620. * Log an query to sentry
  621. *
  622. * @param string|null $query
  623. * @param string $level
  624. * @param string $engine
  625. */
  626. public function captureQuery($query, $level = self::INFO, $engine = '')
  627. {
  628. $data = array(
  629. 'message' => $query,
  630. 'level' => $level,
  631. 'sentry.interfaces.Query' => array(
  632. 'query' => $query
  633. )
  634. );
  635. if ($engine !== '') {
  636. $data['sentry.interfaces.Query']['engine'] = $engine;
  637. }
  638. return $this->capture($data, false);
  639. }
  640. /**
  641. * Return the last captured event's ID or null if none available.
  642. */
  643. public function getLastEventID()
  644. {
  645. return $this->_last_event_id;
  646. }
  647. protected function registerDefaultBreadcrumbHandlers()
  648. {
  649. $handler = new Raven_Breadcrumbs_ErrorHandler($this);
  650. $handler->install();
  651. }
  652. protected function registerShutdownFunction()
  653. {
  654. if (!$this->_shutdown_function_has_been_set) {
  655. $this->_shutdown_function_has_been_set = true;
  656. register_shutdown_function(array($this, 'onShutdown'));
  657. }
  658. }
  659. /**
  660. * @return bool
  661. * @codeCoverageIgnore
  662. */
  663. protected static function is_http_request()
  664. {
  665. return isset($_SERVER['REQUEST_METHOD']) && PHP_SAPI !== 'cli';
  666. }
  667. protected function get_http_data()
  668. {
  669. $headers = array();
  670. foreach ($_SERVER as $key => $value) {
  671. if (0 === strpos($key, 'HTTP_')) {
  672. $header_key =
  673. str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
  674. $headers[$header_key] = $value;
  675. } elseif (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH')) && $value !== '') {
  676. $header_key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
  677. $headers[$header_key] = $value;
  678. }
  679. }
  680. $result = array(
  681. 'method' => self::_server_variable('REQUEST_METHOD'),
  682. 'url' => $this->get_current_url(),
  683. 'query_string' => self::_server_variable('QUERY_STRING'),
  684. );
  685. // dont set this as an empty array as PHP will treat it as a numeric array
  686. // instead of a mapping which goes against the defined Sentry spec
  687. if (!empty($_POST)) {
  688. $result['data'] = $_POST;
  689. } elseif (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'], 'application/json') === 0) {
  690. $raw_data = $this->getInputStream() ?: false;
  691. if ($raw_data !== false) {
  692. $result['data'] = (array) json_decode($raw_data, true) ?: null;
  693. }
  694. }
  695. if (!empty($_COOKIE)) {
  696. $result['cookies'] = $_COOKIE;
  697. }
  698. if (!empty($headers)) {
  699. $result['headers'] = $headers;
  700. }
  701. return array(
  702. 'request' => $result,
  703. );
  704. }
  705. protected function get_user_data()
  706. {
  707. $user = $this->context->user;
  708. if ($user === null) {
  709. if (!function_exists('session_id') || !session_id()) {
  710. return array();
  711. }
  712. $user = array(
  713. 'id' => session_id(),
  714. );
  715. if (!empty($_SERVER['REMOTE_ADDR'])) {
  716. $user['ip_address'] = $_SERVER['REMOTE_ADDR'];
  717. }
  718. if (!empty($_SESSION)) {
  719. $user['data'] = $_SESSION;
  720. }
  721. }
  722. return array(
  723. 'user' => $user,
  724. );
  725. }
  726. protected function get_extra_data()
  727. {
  728. return $this->extra_data;
  729. }
  730. public function get_default_data()
  731. {
  732. return array(
  733. 'server_name' => $this->name,
  734. 'project' => $this->project,
  735. 'site' => $this->site,
  736. 'logger' => $this->logger,
  737. 'tags' => $this->tags,
  738. 'platform' => 'php',
  739. 'sdk' => $this->sdk,
  740. 'culprit' => $this->transaction->peek(),
  741. );
  742. }
  743. public function capture($data, $stack = null, $vars = null)
  744. {
  745. if (!isset($data['timestamp'])) {
  746. $data['timestamp'] = gmdate('Y-m-d\TH:i:s\Z');
  747. }
  748. if (!isset($data['level'])) {
  749. $data['level'] = self::ERROR;
  750. }
  751. if (!isset($data['tags'])) {
  752. $data['tags'] = array();
  753. }
  754. if (!isset($data['extra'])) {
  755. $data['extra'] = array();
  756. }
  757. if (!isset($data['event_id'])) {
  758. $data['event_id'] = static::uuid4();
  759. }
  760. if (isset($data['message'])) {
  761. $data['message'] = substr($data['message'], 0, $this->message_limit);
  762. }
  763. $data = array_merge($this->get_default_data(), $data);
  764. if (static::is_http_request()) {
  765. $data = array_merge($this->get_http_data(), $data);
  766. }
  767. $data = array_merge($this->get_user_data(), $data);
  768. if ($this->release) {
  769. $data['release'] = $this->release;
  770. }
  771. if ($this->environment) {
  772. $data['environment'] = $this->environment;
  773. }
  774. $data['tags'] = array_merge(
  775. $this->tags,
  776. $this->context->tags,
  777. $data['tags']);
  778. $data['extra'] = array_merge(
  779. $this->get_extra_data(),
  780. $this->context->extra,
  781. $data['extra']);
  782. if (empty($data['extra'])) {
  783. unset($data['extra']);
  784. }
  785. if (empty($data['tags'])) {
  786. unset($data['tags']);
  787. }
  788. if (empty($data['user'])) {
  789. unset($data['user']);
  790. }
  791. if (empty($data['request'])) {
  792. unset($data['request']);
  793. }
  794. if (empty($data['site'])) {
  795. unset($data['site']);
  796. }
  797. $existing_runtime_context = isset($data['contexts']['runtime']) ? $data['contexts']['runtime'] : array();
  798. $runtime_context = array('version' => PHP_VERSION, 'name' => 'php');
  799. $data['contexts']['runtime'] = array_merge($runtime_context, $existing_runtime_context);
  800. if (!$this->breadcrumbs->is_empty()) {
  801. $data['breadcrumbs'] = $this->breadcrumbs->fetch();
  802. }
  803. if ((!$stack && $this->auto_log_stacks) || $stack === true) {
  804. $stack = debug_backtrace();
  805. // Drop last stack
  806. array_shift($stack);
  807. }
  808. if (! empty($stack) && ! isset($data['stacktrace']) && ! isset($data['exception'])) {
  809. $data['stacktrace'] = array(
  810. 'frames' => Raven_Stacktrace::get_stack_info(
  811. $stack, $this->trace, $vars, $this->message_limit, $this->prefixes,
  812. $this->app_path, $this->excluded_app_paths, $this->serializer, $this->reprSerializer
  813. ),
  814. );
  815. }
  816. $this->sanitize($data);
  817. $this->process($data);
  818. if (!$this->store_errors_for_bulk_send) {
  819. $this->send($data);
  820. } else {
  821. $this->_pending_events[] = $data;
  822. }
  823. $this->_last_event_id = $data['event_id'];
  824. return $data['event_id'];
  825. }
  826. public function sanitize(&$data)
  827. {
  828. // attempt to sanitize any user provided data
  829. if (!empty($data['request'])) {
  830. $data['request'] = $this->serializer->serialize($data['request'], 5);
  831. }
  832. if (!empty($data['user'])) {
  833. $data['user'] = $this->serializer->serialize($data['user'], 3);
  834. }
  835. if (!empty($data['extra'])) {
  836. $data['extra'] = $this->serializer->serialize($data['extra']);
  837. }
  838. if (!empty($data['tags'])) {
  839. foreach ($data['tags'] as $key => $value) {
  840. $data['tags'][$key] = @(string)$value;
  841. }
  842. }
  843. if (!empty($data['contexts'])) {
  844. $data['contexts'] = $this->serializer->serialize($data['contexts'], 5);
  845. }
  846. if (!empty($data['breadcrumbs'])) {
  847. $data['breadcrumbs'] = $this->serializer->serialize($data['breadcrumbs'], 5);
  848. }
  849. }
  850. /**
  851. * Process data through all defined Raven_Processor sub-classes
  852. *
  853. * @param array $data Associative array of data to log
  854. */
  855. public function process(&$data)
  856. {
  857. foreach ($this->processors as $processor) {
  858. $processor->process($data);
  859. }
  860. }
  861. public function sendUnsentErrors()
  862. {
  863. foreach ($this->_pending_events as $data) {
  864. $this->send($data);
  865. }
  866. $this->_pending_events = array();
  867. if ($this->store_errors_for_bulk_send) {
  868. //in case an error occurs after this is called, on shutdown, send any new errors.
  869. $this->store_errors_for_bulk_send = !defined('RAVEN_CLIENT_END_REACHED');
  870. }
  871. }
  872. /**
  873. * @param array $data
  874. * @return string|bool
  875. */
  876. public function encode(&$data)
  877. {
  878. $message = Raven_Compat::json_encode($data);
  879. if ($message === false) {
  880. if (function_exists('json_last_error_msg')) {
  881. $this->_lasterror = json_last_error_msg();
  882. } else {
  883. // @codeCoverageIgnoreStart
  884. $this->_lasterror = json_last_error();
  885. // @codeCoverageIgnoreEnd
  886. }
  887. return false;
  888. }
  889. if ($this->useCompression) {
  890. $message = gzcompress($message);
  891. }
  892. // PHP's builtin curl_* function are happy without this, but the exec method requires it
  893. $message = base64_encode($message);
  894. return $message;
  895. }
  896. /**
  897. * Wrapper to handle encoding and sending data to the Sentry API server.
  898. *
  899. * @param array $data Associative array of data to log
  900. */
  901. public function send(&$data)
  902. {
  903. if (is_callable($this->send_callback)
  904. && call_user_func_array($this->send_callback, array(&$data)) === false
  905. ) {
  906. // if send_callback returns false, end native send
  907. return;
  908. }
  909. if (!$this->server) {
  910. return;
  911. }
  912. if ($this->transport) {
  913. call_user_func($this->transport, $this, $data);
  914. return;
  915. }
  916. // should this event be sampled?
  917. if (rand(1, 100) / 100.0 > $this->sample_rate) {
  918. return;
  919. }
  920. $message = $this->encode($data);
  921. $headers = array(
  922. 'User-Agent' => static::getUserAgent(),
  923. 'X-Sentry-Auth' => $this->getAuthHeader(),
  924. 'Content-Type' => 'application/octet-stream'
  925. );
  926. $this->send_remote($this->server, $message, $headers);
  927. }
  928. /**
  929. * Send data to Sentry
  930. *
  931. * @param string $url Full URL to Sentry
  932. * @param array|string $data Associative array of data to log
  933. * @param array $headers Associative array of headers
  934. */
  935. protected function send_remote($url, $data, $headers = array())
  936. {
  937. $parts = parse_url($url);
  938. $parts['netloc'] = $parts['host'].(isset($parts['port']) ? ':'.$parts['port'] : null);
  939. $this->send_http($url, $data, $headers);
  940. }
  941. protected static function get_default_ca_cert()
  942. {
  943. return dirname(__FILE__) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'cacert.pem';
  944. }
  945. /**
  946. * @return array
  947. * @see http://stackoverflow.com/questions/9062798/php-curl-timeout-is-not-working/9063006#9063006
  948. */
  949. protected function get_curl_options()
  950. {
  951. $options = array(
  952. CURLOPT_VERBOSE => false,
  953. CURLOPT_SSL_VERIFYHOST => 2,
  954. CURLOPT_SSL_VERIFYPEER => $this->verify_ssl,
  955. CURLOPT_CAINFO => $this->ca_cert,
  956. CURLOPT_USERAGENT => 'sentry-php/' . self::VERSION,
  957. );
  958. if ($this->http_proxy) {
  959. $options[CURLOPT_PROXY] = $this->http_proxy;
  960. }
  961. if ($this->curl_ssl_version) {
  962. $options[CURLOPT_SSLVERSION] = $this->curl_ssl_version;
  963. }
  964. if ($this->curl_ipv4) {
  965. $options[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
  966. }
  967. if (defined('CURLOPT_TIMEOUT_MS')) {
  968. // MS is available in curl >= 7.16.2
  969. $timeout = max(1, ceil(1000 * $this->timeout));
  970. // some versions of PHP 5.3 don't have this defined correctly
  971. if (!defined('CURLOPT_CONNECTTIMEOUT_MS')) {
  972. //see stackoverflow link in the phpdoc
  973. define('CURLOPT_CONNECTTIMEOUT_MS', 156);
  974. }
  975. $options[CURLOPT_CONNECTTIMEOUT_MS] = $timeout;
  976. $options[CURLOPT_TIMEOUT_MS] = $timeout;
  977. } else {
  978. // fall back to the lower-precision timeout.
  979. $timeout = max(1, ceil($this->timeout));
  980. $options[CURLOPT_CONNECTTIMEOUT] = $timeout;
  981. $options[CURLOPT_TIMEOUT] = $timeout;
  982. }
  983. return $options;
  984. }
  985. /**
  986. * Send the message over http to the sentry url given
  987. *
  988. * @param string $url URL of the Sentry instance to log to
  989. * @param array|string $data Associative array of data to log
  990. * @param array $headers Associative array of headers
  991. */
  992. protected function send_http($url, $data, $headers = array())
  993. {
  994. if ($this->curl_method == 'async') {
  995. $this->_curl_handler->enqueue($url, $data, $headers);
  996. } elseif ($this->curl_method == 'exec') {
  997. $this->send_http_asynchronous_curl_exec($url, $data, $headers);
  998. } else {
  999. $this->send_http_synchronous($url, $data, $headers);
  1000. }
  1001. }
  1002. protected function buildCurlCommand($url, $data, $headers)
  1003. {
  1004. // TODO(dcramer): support ca_cert
  1005. $cmd = $this->curl_path.' -X POST ';
  1006. foreach ($headers as $key => $value) {
  1007. $cmd .= '-H ' . escapeshellarg($key.': '.$value). ' ';
  1008. }
  1009. $cmd .= '-d ' . escapeshellarg($data) . ' ';
  1010. $cmd .= escapeshellarg($url) . ' ';
  1011. $cmd .= '-m 5 '; // 5 second timeout for the whole process (connect + send)
  1012. if (!$this->verify_ssl) {
  1013. $cmd .= '-k ';
  1014. }
  1015. $cmd .= '> /dev/null 2>&1 &'; // ensure exec returns immediately while curl runs in the background
  1016. return $cmd;
  1017. }
  1018. /**
  1019. * Send the cURL to Sentry asynchronously. No errors will be returned from cURL
  1020. *
  1021. * @param string $url URL of the Sentry instance to log to
  1022. * @param array|string $data Associative array of data to log
  1023. * @param array $headers Associative array of headers
  1024. * @return bool
  1025. */
  1026. protected function send_http_asynchronous_curl_exec($url, $data, $headers)
  1027. {
  1028. exec($this->buildCurlCommand($url, $data, $headers));
  1029. return true; // The exec method is just fire and forget, so just assume it always works
  1030. }
  1031. /**
  1032. * Send a blocking cURL to Sentry and check for errors from cURL
  1033. *
  1034. * @param string $url URL of the Sentry instance to log to
  1035. * @param array|string $data Associative array of data to log
  1036. * @param array $headers Associative array of headers
  1037. * @return bool
  1038. */
  1039. protected function send_http_synchronous($url, $data, $headers)
  1040. {
  1041. $new_headers = array();
  1042. foreach ($headers as $key => $value) {
  1043. array_push($new_headers, $key .': '. $value);
  1044. }
  1045. // XXX(dcramer): Prevent 100-continue response form server (Fixes GH-216)
  1046. $new_headers[] = 'Expect:';
  1047. if (is_null($this->_curl_instance)) {
  1048. $this->_curl_instance = curl_init($url);
  1049. }
  1050. curl_setopt($this->_curl_instance, CURLOPT_POST, 1);
  1051. curl_setopt($this->_curl_instance, CURLOPT_HTTPHEADER, $new_headers);
  1052. curl_setopt($this->_curl_instance, CURLOPT_POSTFIELDS, $data);
  1053. curl_setopt($this->_curl_instance, CURLOPT_RETURNTRANSFER, true);
  1054. $options = $this->get_curl_options();
  1055. if (isset($options[CURLOPT_CAINFO])) {
  1056. $ca_cert = $options[CURLOPT_CAINFO];
  1057. unset($options[CURLOPT_CAINFO]);
  1058. } else {
  1059. $ca_cert = null;
  1060. }
  1061. curl_setopt_array($this->_curl_instance, $options);
  1062. $buffer = curl_exec($this->_curl_instance);
  1063. $errno = curl_errno($this->_curl_instance);
  1064. // CURLE_SSL_CACERT || CURLE_SSL_CACERT_BADFILE
  1065. if ((($errno == 60) || ($errno == 77)) && !is_null($ca_cert)) {
  1066. curl_setopt($this->_curl_instance, CURLOPT_CAINFO, $ca_cert);
  1067. $buffer = curl_exec($this->_curl_instance);
  1068. }
  1069. if ($errno != 0) {
  1070. $this->_lasterror = curl_error($this->_curl_instance);
  1071. $this->_last_sentry_error = null;
  1072. return false;
  1073. }
  1074. $code = curl_getinfo($this->_curl_instance, CURLINFO_HTTP_CODE);
  1075. $success = ($code == 200);
  1076. if ($success) {
  1077. $this->_lasterror = null;
  1078. $this->_last_sentry_error = null;
  1079. } else {
  1080. // It'd be nice just to raise an exception here, but it's not very PHP-like
  1081. $this->_lasterror = curl_error($this->_curl_instance);
  1082. $this->_last_sentry_error = @json_decode($buffer);
  1083. }
  1084. return $success;
  1085. }
  1086. /**
  1087. * Generate a Sentry authorization header string
  1088. *
  1089. * @param string $timestamp Timestamp when the event occurred
  1090. * @param string $client HTTP client name (not Raven_Client object)
  1091. * @param string $api_key Sentry API key
  1092. * @param string $secret_key Sentry API key
  1093. * @return string
  1094. */
  1095. protected static function get_auth_header($timestamp, $client, $api_key, $secret_key)
  1096. {
  1097. $header = array(
  1098. sprintf('sentry_timestamp=%F', $timestamp),
  1099. "sentry_client={$client}",
  1100. sprintf('sentry_version=%s', self::PROTOCOL),
  1101. );
  1102. if ($api_key) {
  1103. $header[] = "sentry_key={$api_key}";
  1104. }
  1105. if ($secret_key) {
  1106. $header[] = "sentry_secret={$secret_key}";
  1107. }
  1108. return sprintf('Sentry %s', implode(', ', $header));
  1109. }
  1110. public function getAuthHeader()
  1111. {
  1112. $timestamp = microtime(true);
  1113. return $this->get_auth_header(
  1114. $timestamp, static::getUserAgent(), $this->public_key, $this->secret_key
  1115. );
  1116. }
  1117. /**
  1118. * Generate an uuid4 value
  1119. *
  1120. * @return string
  1121. */
  1122. protected static function uuid4()
  1123. {
  1124. $uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
  1125. // 32 bits for "time_low"
  1126. mt_rand(0, 0xffff), mt_rand(0, 0xffff),
  1127. // 16 bits for "time_mid"
  1128. mt_rand(0, 0xffff),
  1129. // 16 bits for "time_hi_and_version",
  1130. // four most significant bits holds version number 4
  1131. mt_rand(0, 0x0fff) | 0x4000,
  1132. // 16 bits, 8 bits for "clk_seq_hi_res",
  1133. // 8 bits for "clk_seq_low",
  1134. // two most significant bits holds zero and one for variant DCE1.1
  1135. mt_rand(0, 0x3fff) | 0x8000,
  1136. // 48 bits for "node"
  1137. mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
  1138. );
  1139. return str_replace('-', '', $uuid);
  1140. }
  1141. /**
  1142. * Return the URL for the current request
  1143. *
  1144. * @return string|null
  1145. */
  1146. protected function get_current_url()
  1147. {
  1148. // When running from commandline the REQUEST_URI is missing.
  1149. if (!isset($_SERVER['REQUEST_URI'])) {
  1150. return null;
  1151. }
  1152. // HTTP_HOST is a client-supplied header that is optional in HTTP 1.0
  1153. $host = (!empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST']
  1154. : (!empty($_SERVER['LOCAL_ADDR']) ? $_SERVER['LOCAL_ADDR']
  1155. : (!empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '')));
  1156. $hasNonDefaultPort = !empty($_SERVER['SERVER_PORT']) && !in_array((int)$_SERVER['SERVER_PORT'], array(80, 443));
  1157. if ($hasNonDefaultPort && !preg_match('#:[0-9]*$#', $host)) {
  1158. $host .= ':' . $_SERVER['SERVER_PORT'];
  1159. }
  1160. $httpS = $this->isHttps() ? 's' : '';
  1161. return "http{$httpS}://{$host}{$_SERVER['REQUEST_URI']}";
  1162. }
  1163. /**
  1164. * Was the current request made over https?
  1165. *
  1166. * @return bool
  1167. */
  1168. protected function isHttps()
  1169. {
  1170. if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
  1171. return true;
  1172. }
  1173. if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
  1174. return true;
  1175. }
  1176. if (!empty($this->trust_x_forwarded_proto) &&
  1177. !empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
  1178. $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
  1179. return true;
  1180. }
  1181. return false;
  1182. }
  1183. /**
  1184. * Get the value of a key from $_SERVER
  1185. *
  1186. * @param string $key Key whose value you wish to obtain
  1187. * @return string Key's value
  1188. */
  1189. private static function _server_variable($key)
  1190. {
  1191. if (isset($_SERVER[$key])) {
  1192. return $_SERVER[$key];
  1193. }
  1194. return '';
  1195. }
  1196. /**
  1197. * Translate a PHP Error constant into a Sentry log level group
  1198. *
  1199. * @param string $severity PHP E_$x error constant
  1200. * @return string Sentry log level group
  1201. */
  1202. public function translateSeverity($severity)
  1203. {
  1204. if (is_array($this->severity_map) && isset($this->severity_map[$severity])) {
  1205. return $this->severity_map[$severity];
  1206. }
  1207. switch ($severity) {
  1208. case E_ERROR: return Raven_Client::ERROR;
  1209. case E_WARNING: return Raven_Client::WARN;
  1210. case E_PARSE: return Raven_Client::ERROR;
  1211. case E_NOTICE: return Raven_Client::INFO;
  1212. case E_CORE_ERROR: return Raven_Client::ERROR;
  1213. case E_CORE_WARNING: return Raven_Client::WARN;
  1214. case E_COMPILE_ERROR: return Raven_Client::ERROR;
  1215. case E_COMPILE_WARNING: return Raven_Client::WARN;
  1216. case E_USER_ERROR: return Raven_Client::ERROR;
  1217. case E_USER_WARNING: return Raven_Client::WARN;
  1218. case E_USER_NOTICE: return Raven_Client::INFO;
  1219. case E_STRICT: return Raven_Client::INFO;
  1220. case E_RECOVERABLE_ERROR: return Raven_Client::ERROR;
  1221. }
  1222. if (PHP_VERSION_ID >= 50300) {
  1223. switch ($severity) {
  1224. case E_DEPRECATED: return Raven_Client::WARN;
  1225. case E_USER_DEPRECATED: return Raven_Client::WARN;
  1226. }
  1227. }
  1228. return Raven_Client::ERROR;
  1229. }
  1230. /**
  1231. * Provide a map of PHP Error constants to Sentry logging groups to use instead
  1232. * of the defaults in translateSeverity()
  1233. *
  1234. * @param array $map
  1235. */
  1236. public function registerSeverityMap($map)
  1237. {
  1238. $this->severity_map = $map;
  1239. }
  1240. /**
  1241. * Convenience function for setting a user's ID and Email
  1242. *
  1243. * @deprecated
  1244. * @param string $id User's ID
  1245. * @param string|null $email User's email
  1246. * @param array $data Additional user data
  1247. * @codeCoverageIgnore
  1248. */
  1249. public function set_user_data($id, $email = null, $data = array())
  1250. {
  1251. $user = array('id' => $id);
  1252. if (isset($email)) {
  1253. $user['email'] = $email;
  1254. }
  1255. $this->user_context(array_merge($user, $data));
  1256. }
  1257. public function onShutdown()
  1258. {
  1259. if (!defined('RAVEN_CLIENT_END_REACHED')) {
  1260. define('RAVEN_CLIENT_END_REACHED', true);
  1261. }
  1262. $this->sendUnsentErrors();
  1263. if ($this->curl_method == 'async') {
  1264. $this->_curl_handler->join();
  1265. }
  1266. }
  1267. /**
  1268. * Sets user context.
  1269. *
  1270. * @param array $data Associative array of user data
  1271. * @param bool $merge Merge existing context with new context
  1272. */
  1273. public function user_context($data, $merge = true)
  1274. {
  1275. if ($merge && $this->context->user !== null) {
  1276. // bail if data is null
  1277. if (!$data) {
  1278. return;
  1279. }
  1280. $this->context->user = array_merge($this->context->user, $data);
  1281. } else {
  1282. $this->context->user = $data;
  1283. }
  1284. }
  1285. /**
  1286. * Appends tags context.
  1287. *
  1288. * @param array $data Associative array of tags
  1289. */
  1290. public function tags_context($data)
  1291. {
  1292. $this->context->tags = array_merge($this->context->tags, $data);
  1293. }
  1294. /**
  1295. * Appends additional context.
  1296. *
  1297. * @param array $data Associative array of extra data
  1298. */
  1299. public function extra_context($data)
  1300. {
  1301. $this->context->extra = array_merge($this->context->extra, $data);
  1302. }
  1303. /**
  1304. * @param array $processors
  1305. */
  1306. public function setProcessors(array $processors)
  1307. {
  1308. $this->processors = $processors;
  1309. }
  1310. /**
  1311. * @return object|null
  1312. */
  1313. public function getLastSentryError()
  1314. {
  1315. return $this->_last_sentry_error;
  1316. }
  1317. /**
  1318. * @return bool
  1319. */
  1320. public function getShutdownFunctionHasBeenSet()
  1321. {
  1322. return $this->_shutdown_function_has_been_set;
  1323. }
  1324. public function close_curl_resource()
  1325. {
  1326. if (!is_null($this->_curl_instance)) {
  1327. curl_close($this->_curl_instance);
  1328. $this->_curl_instance = null;
  1329. }
  1330. }
  1331. /**
  1332. * @param Raven_Serializer $serializer
  1333. */
  1334. public function setSerializer(Raven_Serializer $serializer)
  1335. {
  1336. $this->serializer = $serializer;
  1337. }
  1338. /**
  1339. * @param Raven_ReprSerializer $reprSerializer
  1340. */
  1341. public function setReprSerializer(Raven_ReprSerializer $reprSerializer)
  1342. {
  1343. $this->reprSerializer = $reprSerializer;
  1344. }
  1345. private function triggerAutoload()
  1346. {
  1347. // manually trigger autoloading, as it cannot be done during error handling in some edge cases due to PHP (see #60149)
  1348. if (! class_exists('Raven_Stacktrace')) {
  1349. spl_autoload_call('Raven_Stacktrace');
  1350. }
  1351. if (function_exists('mb_detect_encoding')) {
  1352. mb_detect_encoding('string');
  1353. }
  1354. if (function_exists('mb_convert_encoding')) {
  1355. mb_convert_encoding('string', 'UTF8');
  1356. }
  1357. }
  1358. }