Bugsnag_Notifier - Version 1.0.0

Version Notes

The first release!

Download this release

Release Info

Developer MAG002804728
Extension Bugsnag_Notifier
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

app/code/community/Bugsnag/Notifier/Block/Adminhtml/System/Config/Form/Button.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Notifier_Block_Adminhtml_System_Config_Form_Button extends Mage_Adminhtml_Block_System_Config_Form_Field
4
+ {
5
+ protected function _construct()
6
+ {
7
+ parent::_construct();
8
+ $this->setTemplate('bugsnag/system/config/button.phtml');
9
+ }
10
+
11
+ protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
12
+ {
13
+ return $this->_toHtml();
14
+ }
15
+
16
+ public function getAjaxCheckUrl()
17
+ {
18
+ return Mage::helper('adminhtml')->getUrl('adminhtml/adminhtml_bugsnag/check');
19
+ }
20
+
21
+ public function getButtonHtml()
22
+ {
23
+ $button = $this
24
+ ->getLayout()
25
+ ->createBlock('adminhtml/widget_button')
26
+ ->setData(
27
+ array(
28
+ 'type' => 'button',
29
+ 'id' => 'Bugsnag_Notifier',
30
+ 'label' => $this->helper('adminhtml')->__('Fire Test Event'),
31
+ 'onclick' => 'javascript:fireTestEvent(); return false;'
32
+ )
33
+ );
34
+
35
+ return $button->toHtml();
36
+ }
37
+ }
app/code/community/Bugsnag/Notifier/Model/Observer.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Notifier_Model_Observer
4
+ {
5
+ private static $DEFAULT_NOTIFY_SEVERITIES = "fatal,error";
6
+
7
+ private static $NOTIFIER = array(
8
+ "name" => "Bugsnag Magento (Official)",
9
+ "version" => "1.0.0",
10
+ "url" => "https://bugsnag.com/notifiers/magento"
11
+ );
12
+
13
+ private $client;
14
+ private $apiKey;
15
+ private $notifySeverities;
16
+ private $filterFields;
17
+
18
+ public static function fireTestEvent($apiKey) {
19
+ if (strlen($apiKey) != 32) {
20
+ throw new Exception("Invalid length of the API key");
21
+ }
22
+
23
+ $client = new Bugsnag_Client($apiKey);
24
+ $client->notifyError(
25
+ "BugsnagTest",
26
+ "Testing bugsnag",
27
+ array("notifier" => self::$NOTIFIER)
28
+ );
29
+ }
30
+
31
+ public function initBugsnag()
32
+ {
33
+ if (file_exists(Mage::getBaseDir('lib') . '/bugsnag-php/Autoload.php')) {
34
+ require_once(Mage::getBaseDir('lib') . '/bugsnag-php/Autoload.php');
35
+ } else {
36
+ error_log("Bugsnag Error: Couldn't activate Bugsnag Error Monitoring due to missing Bugsnag PHP library!");
37
+ return;
38
+ }
39
+
40
+ $this->apiKey = Mage::getStoreConfig("dev/Bugsnag_Notifier/apiKey");
41
+ $this->notifySeverities = Mage::getStoreConfig("dev/Bugsnag_Notifier/severites");
42
+ $this->filterFields = Mage::getStoreConfig("dev/Bugsnag_Notifier/filterFiels");
43
+
44
+ // Activate the bugsnag client
45
+ if (!empty($this->apiKey)) {
46
+ $this->client = new Bugsnag_Client($this->apiKey);
47
+
48
+ $this->client->setReleaseStage($this->releaseStage())
49
+ ->setErrorReportingLevel($this->errorReportingLevel())
50
+ ->setFilters($this->filterFields());
51
+
52
+ $this->client->setNotifier(self::$NOTIFIER);
53
+
54
+ if (Mage::getSingleton('customer/session')->isLoggedIn()) {
55
+ $this->addUserTab();
56
+ }
57
+
58
+ set_error_handler(array($this->client, "errorHandler"));
59
+ set_exception_handler(array($this->client, "exceptionHandler"));
60
+ }
61
+ }
62
+
63
+ private function addUserTab()
64
+ {
65
+ $customer = Mage::getSingleton('customer/session')->getCustomer();
66
+ $this->client->setUser(array(
67
+ 'id' => $customer->getId(),
68
+ 'email' => $customer->getEmail(),
69
+ 'created_at' => $customer->getCreatedAt(),
70
+ 'first_name' => $customer->getFirstname(),
71
+ 'last_name' => $customer->getLastname()
72
+ ));
73
+ }
74
+
75
+ private function releaseStage()
76
+ {
77
+ return Mage::getIsDeveloperMode() ? "development" : "production";
78
+ }
79
+
80
+ private function errorReportingLevel()
81
+ {
82
+ if (empty($this->notifySeverities)) {
83
+ $notifySeverities = "fatal,error";
84
+ } else {
85
+ $notifySeverities = $this->notifySeverities;
86
+ }
87
+
88
+ $level = 0;
89
+ $severities = explode(",", $notifySeverities);
90
+
91
+ foreach($severities as $severity) {
92
+ $level |= Bugsnag_ErrorTypes::getLevelsForSeverity($severity);
93
+ }
94
+
95
+ return $level;
96
+ }
97
+
98
+ private function filterFields()
99
+ {
100
+ return array_map('trim', explode("\n", $this->filterFields));
101
+ }
102
+
103
+ }
app/code/community/Bugsnag/Notifier/Model/Severity.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Notifier_Model_Severity
4
+ {
5
+ public function toOptionArray()
6
+ {
7
+ return array(
8
+ array('value' => 'fatal,error', 'label'=>Mage::helper('adminhtml')->__('Crashes & errors')),
9
+ array('value' => 'fatal,error,warning', 'label'=>Mage::helper('adminhtml')->__('Crashes, errors & warnings')),
10
+ array('value' => 'fatal,error,warning,info', 'label'=>Mage::helper('adminhtml')->__('Crashes, errors, warnings & info messages'))
11
+ );
12
+ }
13
+ }
app/code/community/Bugsnag/Notifier/controllers/Adminhtml/BugsnagController.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Notifier_Adminhtml_BugsnagController extends Mage_Adminhtml_Controller_Action
4
+ {
5
+ public function checkAction()
6
+ {
7
+ Bugsnag_Notifier_Model_Observer::fireTestEvent($_POST["apiKey"]);
8
+ $successCode = 1;
9
+ Mage::app()->getResponse()->setBody($successCode);
10
+ }
11
+ }
app/code/community/Bugsnag/Notifier/etc/config.xml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <modules>
4
+ <Bugsnag_Notifier>
5
+ <version>1.0.0</version>
6
+ </Bugsnag_Notifier>
7
+ </modules>
8
+ <global>
9
+ <models>
10
+ <Bugsnag_Notifier>
11
+ <class>Bugsang_Notifier_Model</class>
12
+ </Bugsnag_Notifier>
13
+ </models>
14
+ <events>
15
+ <controller_action_predispatch>
16
+ <observers>
17
+ <bugsnag_controller_action_predispatch>
18
+ <type>singleton</type>
19
+ <class>Bugsnag_Notifier_Model_Observer</class>
20
+ <method>initBugsnag</method>
21
+ </bugsnag_controller_action_predispatch>
22
+ </observers>
23
+ </controller_action_predispatch>
24
+ </events>
25
+ </global>
26
+ <admin>
27
+ <routers>
28
+ <adminhtml>
29
+ <args>
30
+ <modules>
31
+ <Bungsnag_Notifier after="Mage_Adminhtml">Bugsnag_Notifier</Bungsnag_Notifier>
32
+ </modules>
33
+ </args>
34
+ </adminhtml>
35
+ </routers>
36
+ </admin>
37
+ </config>
app/code/community/Bugsnag/Notifier/etc/system.xml ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <sections>
4
+ <dev>
5
+ <groups>
6
+ <Bugsnag_Notifier>
7
+ <label>Bugsnag</label>
8
+ <sort_order>0</sort_order>
9
+ <show_in_default>1</show_in_default>
10
+ <show_in_website>1</show_in_website>
11
+ <show_in_store>1</show_in_store>
12
+ <fields>
13
+ <apiKey>
14
+ <label>Bugsnag API Key</label>
15
+ <frontend_type>text</frontend_type>
16
+ <sort_order>20</sort_order>
17
+ <show_in_default>1</show_in_default>
18
+ <show_in_website>1</show_in_website>
19
+ <show_in_store>1</show_in_store>
20
+ </apiKey>
21
+ <severities>
22
+ <label>Notify Bugsnag about</label>
23
+ <frontend_type>select</frontend_type>
24
+ <source_model>Bugsnag_Notifier_Model_Severity</source_model>
25
+ <sort_order>40</sort_order>
26
+ <show_in_default>1</show_in_default>
27
+ <show_in_website>1</show_in_website>
28
+ <show_in_store>1</show_in_store>
29
+ </severities>
30
+ <filterFields>
31
+ <label>
32
+ Bugsnag Field Filter. The information to remove from Bugsnag
33
+ reports, one per line. Use this if you want to ensure you don't
34
+ send sensitive data such as passwords, and credit card numbers
35
+ to our servers.
36
+ </label>
37
+ <frontend_type>textarea</frontend_type>
38
+ <sort_order>50</sort_order>
39
+ <show_in_default>1</show_in_default>
40
+ <show_in_website>1</show_in_website>
41
+ <show_in_store>1</show_in_store>
42
+ </filterFields>
43
+ <test_bugsnag>
44
+ <label>Fire a test event to ensure that the API key is correct</label>
45
+ <frontend_type>button</frontend_type>
46
+ <frontend_model>Bugsnag_Notifier_Block_Adminhtml_System_Config_Form_Button</frontend_model>
47
+ <sort_order>60</sort_order>
48
+ <show_in_default>1</show_in_default>
49
+ <show_in_website>1</show_in_website>
50
+ <show_in_store>1</show_in_store>
51
+ </test_bugsnag>
52
+ </fields>
53
+ </Bugsnag_Notifier>
54
+ </groups>
55
+ </dev>
56
+ </sections>
57
+ </config>
app/design/adminhtml/default/default/template/bugsnag/system/config/button.phtml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/javascript">
2
+ //<![CDATA[
3
+ function fireTestEvent() {
4
+ new Ajax.Request('<?php echo $this->getAjaxCheckUrl() ?>', {
5
+ parameters: {
6
+ apiKey: $('dev_Bugsnag_Notifier_apiKey').value,
7
+ severities: $('dev_Bugsnag_Notifier_severities').value,
8
+ filterFiels: $('dev_Bugsnag_Notifier_filterFields').value
9
+ },
10
+ onSuccess: function(transport) {
11
+ alert('Sent notification. Visit https://bugsnag.com/ to see it in your dashboard');
12
+ },
13
+ onFailure: function(transport) {
14
+ alert("Couldn't send a notification. Double check your API key");
15
+ }
16
+ });
17
+ }
18
+ //]]>
19
+ </script>
20
+
21
+ <?php echo $this->getButtonHtml() ?>
app/etc/modules/Bugsnag_Notifier.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <Bugsnag_Notifier>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </Bugsnag_Notifier>
8
+ </modules>
9
+ </config>
lib/bugsnag-php/Autoload.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ # We used to have an autoloader, but it caused problems in some
4
+ # environments. So now we manually load the entire library upfront.
5
+ #
6
+ # The file is still called Autoload so that existing integration
7
+ # instructions continue to work.
8
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Client.php";
9
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Configuration.php";
10
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Diagnostics.php";
11
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Error.php";
12
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."ErrorTypes.php";
13
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Notification.php";
14
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Request.php";
15
+ require_once dirname(__FILE__).DIRECTORY_SEPARATOR."Stacktrace.php";
lib/bugsnag-php/Client.php ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Client
4
+ {
5
+ private $config;
6
+ private $notification;
7
+
8
+ /**
9
+ * Initialize Bugsnag
10
+ *
11
+ * @param String $apiKey your Bugsnag API key
12
+ */
13
+ public function __construct($apiKey)
14
+ {
15
+ // Check API key has been passed
16
+ if (!is_string($apiKey)) {
17
+ throw new Exception('Bugsnag Error: Invalid API key');
18
+ }
19
+
20
+ // Create a configuration object
21
+ $this->config = new Bugsnag_Configuration();
22
+ $this->config->apiKey = $apiKey;
23
+
24
+ // Build a Diagnostics object
25
+ $this->diagnostics = new Bugsnag_Diagnostics($this->config);
26
+
27
+ // Register a shutdown function to check for fatal errors
28
+ // and flush any buffered errors
29
+ register_shutdown_function(array($this, 'shutdownHandler'));
30
+ }
31
+
32
+ /**
33
+ * Set your release stage, eg "production" or "development"
34
+ *
35
+ * @param String $releaseStage the app's current release stage
36
+ */
37
+ public function setReleaseStage($releaseStage)
38
+ {
39
+ $this->config->releaseStage = $releaseStage;
40
+
41
+ return $this;
42
+ }
43
+
44
+ /**
45
+ * Set your app's semantic version, eg "1.2.3"
46
+ *
47
+ * @param String $appVersion the app's version
48
+ */
49
+ public function setAppVersion($appVersion)
50
+ {
51
+ $this->config->appVersion = $appVersion;
52
+
53
+ return $this;
54
+ }
55
+
56
+ /**
57
+ * Set which release stages should be allowed to notify Bugsnag
58
+ * eg array("production", "development")
59
+ *
60
+ * @param Array $notifyReleaseStages array of release stages to notify for
61
+ */
62
+ public function setNotifyReleaseStages(array $notifyReleaseStages)
63
+ {
64
+ $this->config->notifyReleaseStages = $notifyReleaseStages;
65
+
66
+ return $this;
67
+ }
68
+
69
+ /**
70
+ * Set which Bugsnag endpoint to send errors to.
71
+ *
72
+ * @param String $endpoint endpoint URL
73
+ */
74
+ public function setEndpoint($endpoint)
75
+ {
76
+ $this->config->endpoint = $endpoint;
77
+
78
+ return $this;
79
+ }
80
+
81
+ /**
82
+ * Set whether or not to use SSL when notifying bugsnag
83
+ *
84
+ * @param Boolean $useSSL whether to use SSL
85
+ */
86
+ public function setUseSSL($useSSL)
87
+ {
88
+ $this->config->useSSL = $useSSL;
89
+
90
+ return $this;
91
+ }
92
+
93
+ /**
94
+ * Set the desired timeout for cURL connection when notifying bugsnag
95
+ *
96
+ * @param Integer $timeout the desired timeout in seconds
97
+ */
98
+ public function setTimeout($timeout)
99
+ {
100
+ $this->config->timeout = $timeout;
101
+
102
+ return $this;
103
+ }
104
+
105
+ /**
106
+ * Set the absolute path to the root of your application.
107
+ * We use this to help with error grouping and to highlight "in project"
108
+ * stacktrace lines.
109
+ *
110
+ * @param String $projectRoot the root path for your application
111
+ */
112
+ public function setProjectRoot($projectRoot)
113
+ {
114
+ $this->config->setProjectRoot($projectRoot);
115
+
116
+ return $this;
117
+ }
118
+
119
+ /**
120
+ * Set the path that should be stripped from the beginning of
121
+ * any stacktrace file line. This helps to normalise filenames
122
+ * for grouping and reduces the noise in stack traces.
123
+ *
124
+ * @param String $stripPath the path to strip from filenames
125
+ */
126
+ public function setStripPath($stripPath)
127
+ {
128
+ $this->config->setStripPath($stripPath);
129
+
130
+ return $this;
131
+ }
132
+
133
+ /**
134
+ * Set the a regular expression for matching filenames in stacktrace lines
135
+ * that are part of your application.
136
+ *
137
+ * @param String $projectRootRegex regex matching paths belong to your project
138
+ */
139
+ public function setProjectRootRegex($projectRootRegex)
140
+ {
141
+ $this->config->projectRootRegex = $projectRootRegex;
142
+
143
+ return $this;
144
+ }
145
+
146
+ /**
147
+ * Set the strings to filter out from metaData arrays before sending then
148
+ * to Bugsnag. Eg. array("password", "credit_card")
149
+ *
150
+ * @param Array $filters an array of metaData filters
151
+ */
152
+ public function setFilters(array $filters)
153
+ {
154
+ $this->config->filters = $filters;
155
+
156
+ return $this;
157
+ }
158
+
159
+ /**
160
+ * Set information about the current user of your app, including
161
+ * id, name and email.
162
+ *
163
+ * @param Array $user an array of user information. Eg:
164
+ * array(
165
+ * 'name' => 'Bob Hoskins',
166
+ * 'email' => 'bob@hoskins.com'
167
+ * )
168
+ */
169
+ public function setUser(array $user)
170
+ {
171
+ $this->config->user = $user;
172
+
173
+ return $this;
174
+ }
175
+
176
+ /**
177
+ * @deprecated deprecated since version 2.1
178
+ */
179
+ public function setUserId($userId)
180
+ {
181
+ if (!is_array($this->config->user)) {
182
+ $this->config->user = array();
183
+ }
184
+
185
+ $this->config->user['id'] = $userId;
186
+
187
+ return $this;
188
+ }
189
+
190
+ /**
191
+ * Set a context representing the current type of request, or location in code.
192
+ *
193
+ * @param String $context the current context
194
+ */
195
+ public function setContext($context)
196
+ {
197
+ $this->config->context = $context;
198
+
199
+ return $this;
200
+ }
201
+
202
+ /**
203
+ * Set the type of application executing the code. This is usually used to
204
+ * represent if you are running plain PHP code "php", via a framework,
205
+ * eg "laravel", or executing through delayed worker code, eg "resque".
206
+ *
207
+ * @param String $type the current type
208
+ */
209
+ public function setType($type)
210
+ {
211
+ $this->config->type = $type;
212
+
213
+ return $this;
214
+ }
215
+
216
+ /**
217
+ * Set custom metadata to send to Bugsnag with every error. You can use
218
+ * this to add custom tabs of data to each error on your Bugsnag dashboard
219
+ *
220
+ * @param Array $metaData an array of arrays of custom data. Eg:
221
+ * array(
222
+ * "user" => array(
223
+ * "name" => "James",
224
+ * "email" => "james@example.com"
225
+ * )
226
+ * )
227
+ */
228
+ public function setMetaData(array $metaData)
229
+ {
230
+ $this->config->metaData = $metaData;
231
+
232
+ return $this;
233
+ }
234
+
235
+ /**
236
+ * Set proxy configuration
237
+ *
238
+ * @param Array $proxySettings an array with proxy settings. Eg:
239
+ * array(
240
+ * 'host' => "bugsnag.com",
241
+ * 'port' => 42,
242
+ * 'user' => "username"
243
+ * 'password' => "password123"
244
+ * )
245
+ */
246
+ public function setProxySettings(array $proxySettings)
247
+ {
248
+ $this->config->proxySettings = $proxySettings;
249
+
250
+ return $this;
251
+ }
252
+
253
+ /**
254
+ * Set a custom function to call before notifying Bugsnag of an error.
255
+ * You can use this to call your own error handling functions, or to add
256
+ * custom tabs of data to each error on your Bugsnag dashboard.
257
+ *
258
+ * // Adding meta-data example
259
+ * function before_bugsnag_notify($error) {
260
+ * $error->addMetaData(array(
261
+ * "user" => array(
262
+ * "name" => "James"
263
+ * )
264
+ * ));
265
+ * }
266
+ * $bugsnag->setBeforeNotifyFunction("before_bugsnag_notify");
267
+ *
268
+ */
269
+ public function setBeforeNotifyFunction($beforeNotifyFunction)
270
+ {
271
+ $this->config->beforeNotifyFunction = $beforeNotifyFunction;
272
+
273
+ return $this;
274
+ }
275
+
276
+ /**
277
+ * Set Bugsnag's error reporting level.
278
+ * If this is not set, we'll use your current PHP error_reporting value
279
+ * from your ini file or error_reporting(...) calls.
280
+ *
281
+ * @param Integer $errorReportingLevel the error reporting level integer
282
+ * exactly as you would pass to PHP's error_reporting
283
+ */
284
+ public function setErrorReportingLevel($errorReportingLevel)
285
+ {
286
+ $this->config->errorReportingLevel = $errorReportingLevel;
287
+
288
+ return $this;
289
+ }
290
+
291
+ /**
292
+ * Sets whether Bugsnag should be automatically notified of unhandled
293
+ * exceptions and errors.
294
+ *
295
+ * @param Boolean $autoNotify whether to auto notify or not
296
+ */
297
+ public function setAutoNotify($autoNotify)
298
+ {
299
+ $this->config->autoNotify = $autoNotify;
300
+
301
+ return $this;
302
+ }
303
+
304
+ /**
305
+ * Sets whether errors should be batched together and send at the end of
306
+ * each request.
307
+ *
308
+ * @param Boolean $batchSending whether to batch together errors
309
+ */
310
+ public function setBatchSending($batchSending)
311
+ {
312
+ $this->config->batchSending = $batchSending;
313
+
314
+ return $this;
315
+ }
316
+
317
+ /**
318
+ * Sets the notifier to report as to Bugsnag. This should only be
319
+ * set by other notifier libraries.
320
+ *
321
+ * @param Array $notifier an array of name, version, url.
322
+ */
323
+ public function setNotifier($notifier)
324
+ {
325
+ $this->config->notifier = $notifier;
326
+
327
+ return $this;
328
+ }
329
+
330
+ /**
331
+ * Notify Bugsnag of a non-fatal/handled exception
332
+ *
333
+ * @param Exception $exception the exception to notify Bugsnag about
334
+ * @param Array $metaData optional metaData to send with this error
335
+ * @param String $severity optional severity of this error (fatal/error/warning/info)
336
+ */
337
+ public function notifyException(Exception $exception, array $metaData=null, $severity=null)
338
+ {
339
+ $error = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
340
+ $error->setSeverity($severity);
341
+
342
+ $this->notify($error, $metaData);
343
+ }
344
+
345
+ /**
346
+ * Notify Bugsnag of a non-fatal/handled error
347
+ *
348
+ * @param String $errorName the name of the error, a short (1 word) string
349
+ * @param String $errorMessage the error message
350
+ * @param Array $metaData optional metaData to send with this error
351
+ * @param String $severity optional severity of this error (fatal/error/warning/info)
352
+ */
353
+ public function notifyError($name, $message, array $metaData=null, $severity=null)
354
+ {
355
+ $error = Bugsnag_Error::fromNamedError($this->config, $this->diagnostics, $name, $message);
356
+ $error->setSeverity($severity);
357
+
358
+ $this->notify($error, $metaData);
359
+ }
360
+
361
+ // Exception handler callback, should only be called internally by PHP's set_exception_handler
362
+ public function exceptionHandler($exception)
363
+ {
364
+ $error = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
365
+ $error->setSeverity("error");
366
+
367
+ if (!$error->shouldIgnore() && $this->config->autoNotify) {
368
+ $this->notify($error);
369
+ }
370
+ }
371
+
372
+ // Exception handler callback, should only be called internally by PHP's set_error_handler
373
+ public function errorHandler($errno, $errstr, $errfile='', $errline=0)
374
+ {
375
+ $error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $errno, $errstr, $errfile, $errline);
376
+
377
+ if (!$error->shouldIgnore() && $this->config->autoNotify) {
378
+ $this->notify($error);
379
+ }
380
+ }
381
+
382
+ // Shutdown handler callback, called when the PHP process has finished running
383
+ // Should only be called internally by PHP's register_shutdown_function
384
+ public function shutdownHandler()
385
+ {
386
+ // Get last error
387
+ $lastError = error_get_last();
388
+
389
+ // Check if a fatal error caused this shutdown
390
+ if (!is_null($lastError) && Bugsnag_ErrorTypes::isFatal($lastError['type'])) {
391
+ $error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $lastError['type'], $lastError['message'], $lastError['file'], $lastError['line'], true);
392
+ $error->setSeverity("error");
393
+
394
+ if (!$error->shouldIgnore() && $this->config->autoNotify) {
395
+ $this->notify($error);
396
+ }
397
+ }
398
+
399
+ // Flush any buffered errors
400
+ if ($this->notification) {
401
+ $this->notification->deliver();
402
+ $this->notification = null;
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Batches up errors into notifications for later sending
408
+ *
409
+ * @param Bugsnag_Error $error the error to batch up
410
+ * @param array $metaData optional meta data to send with the error
411
+ */
412
+ public function notify(Bugsnag_Error $error, $metaData = array())
413
+ {
414
+ // Queue or send the error
415
+ if ($this->sendErrorsOnShutdown()) {
416
+ // Create a batch notification unless we already have one
417
+ if (is_null($this->notification)) {
418
+ $this->notification = new Bugsnag_Notification($this->config);
419
+ }
420
+
421
+ // Add this error to the notification
422
+ $this->notification->addError($error, $metaData);
423
+ } else {
424
+ // Create and deliver notification immediatelt
425
+ $notif = new Bugsnag_Notification($this->config);
426
+ $notif->addError($error, $metaData);
427
+ $notif->deliver();
428
+ }
429
+ }
430
+
431
+ // Should we send errors immediately or on shutdown
432
+ private function sendErrorsOnShutdown()
433
+ {
434
+ return $this->config->batchSending && Bugsnag_Request::isRequest();
435
+ }
436
+ }
lib/bugsnag-php/Configuration.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Configuration
4
+ {
5
+ public static $DEFAULT_TIMEOUT = 10;
6
+
7
+ public $apiKey;
8
+ public $autoNotify = true;
9
+ public $batchSending = true;
10
+ public $useSSL = true;
11
+ public $endpoint = 'notify.bugsnag.com';
12
+ public $notifyReleaseStages;
13
+ public $filters = array('password');
14
+ public $projectRoot;
15
+ public $projectRootRegex;
16
+ public $proxySettings = array();
17
+ public $notifier = array(
18
+ 'name' => 'Bugsnag PHP (Official)',
19
+ 'version' => '2.3.0',
20
+ 'url' => 'https://bugsnag.com'
21
+ );
22
+ public $stripPath;
23
+ public $stripPathRegex;
24
+
25
+ public $context;
26
+ public $type;
27
+ public $user;
28
+ public $releaseStage = 'production';
29
+ public $appVersion;
30
+ public $hostname;
31
+
32
+ public $metaData;
33
+ public $beforeNotifyFunction;
34
+ public $errorReportingLevel;
35
+
36
+ public function __construct()
37
+ {
38
+ $this->timeout = Bugsnag_Configuration::$DEFAULT_TIMEOUT;
39
+ }
40
+
41
+ public function getNotifyEndpoint()
42
+ {
43
+ return $this->getProtocol()."://".$this->endpoint;
44
+ }
45
+
46
+ public function shouldNotify()
47
+ {
48
+ return is_null($this->notifyReleaseStages) || (is_array($this->notifyReleaseStages) && in_array($this->releaseStage, $this->notifyReleaseStages));
49
+ }
50
+
51
+ public function setProjectRoot($projectRoot)
52
+ {
53
+ $this->projectRoot = $projectRoot;
54
+ $this->projectRootRegex = '/'.preg_quote($projectRoot, '/')."[\\/]?/i";
55
+ if (is_null($this->stripPath)) {
56
+ $this->setStripPath($projectRoot);
57
+ }
58
+ }
59
+
60
+ public function setStripPath($stripPath)
61
+ {
62
+ $this->stripPath = $stripPath;
63
+ $this->stripPathRegex = '/'.preg_quote($stripPath, '/')."[\\/]?/i";
64
+ }
65
+
66
+ public function get($prop, $default=NULL)
67
+ {
68
+ $configured = $this->$prop;
69
+
70
+ if (is_array($configured) && is_array($default)) {
71
+ return array_merge($default, $configured);
72
+ } else {
73
+ return $configured ? $configured : $default;
74
+ }
75
+ }
76
+
77
+ private function getProtocol()
78
+ {
79
+ return $this->useSSL ? "https" : "http";
80
+ }
81
+ }
lib/bugsnag-php/Diagnostics.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Diagnostics
4
+ {
5
+ private $config;
6
+
7
+ public function __construct(Bugsnag_Configuration $config)
8
+ {
9
+ $this->config = $config;
10
+ }
11
+
12
+ public function getAppData()
13
+ {
14
+ $appData = array();
15
+
16
+ if (!is_null($this->config->appVersion)) {
17
+ $appData['version'] = $this->config->appVersion;
18
+ }
19
+
20
+ if (!is_null($this->config->releaseStage)) {
21
+ $appData['releaseStage'] = $this->config->releaseStage;
22
+ }
23
+
24
+ if (!is_null($this->config->type)) {
25
+ $appData['type'] = $this->config->type;
26
+ }
27
+
28
+ return $appData;
29
+ }
30
+
31
+ public function getDeviceData()
32
+ {
33
+ return array(
34
+ 'hostname' => $this->config->get('hostname', php_uname('n'))
35
+ );
36
+ }
37
+
38
+ public function getContext()
39
+ {
40
+ return $this->config->get('context', Bugsnag_Request::getContext());
41
+ }
42
+
43
+ public function getUser()
44
+ {
45
+ $defaultUser = array();
46
+ $userId = Bugsnag_Request::getUserId();
47
+
48
+ if (!is_null($userId)) {
49
+ $defaultUser['id'] = $userId;
50
+ }
51
+
52
+ return $this->config->get('user', $defaultUser);
53
+ }
54
+ }
lib/bugsnag-php/Error.php ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Error
4
+ {
5
+ private static $VALID_SEVERITIES = array(
6
+ 'error',
7
+ 'warning',
8
+ 'info'
9
+ );
10
+
11
+ public $name;
12
+ public $payloadVersion = "2";
13
+ public $message;
14
+ public $severity = "warning";
15
+ public $stacktrace;
16
+ public $metaData = array();
17
+ public $config;
18
+ public $diagnostics;
19
+ public $code;
20
+ public $previous;
21
+
22
+ // Static error creation methods, to ensure that Error object is always complete
23
+ public static function fromPHPError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $code, $message, $file, $line, $fatal=false)
24
+ {
25
+ $error = new Bugsnag_Error($config, $diagnostics);
26
+ $error->setPHPError($code, $message, $file, $line, $fatal);
27
+
28
+ return $error;
29
+ }
30
+
31
+ public static function fromPHPException(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, Exception $exception)
32
+ {
33
+ $error = new Bugsnag_Error($config, $diagnostics);
34
+ $error->setPHPException($exception);
35
+
36
+ return $error;
37
+ }
38
+
39
+ public static function fromNamedError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $name, $message=NULL)
40
+ {
41
+ $error = new Bugsnag_Error($config, $diagnostics);
42
+ $error->setName($name)
43
+ ->setMessage($message)
44
+ ->setStacktrace(Bugsnag_Stacktrace::generate($config));
45
+
46
+ return $error;
47
+ }
48
+
49
+ // Private constructor (for use only by the static methods above)
50
+ private function __construct(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics)
51
+ {
52
+ $this->config = $config;
53
+ $this->diagnostics = $diagnostics;
54
+ }
55
+
56
+ public function setName($name)
57
+ {
58
+ $this->name = $name;
59
+
60
+ return $this;
61
+ }
62
+
63
+
64
+ public function setMessage($message)
65
+ {
66
+ $this->message = $message;
67
+
68
+ return $this;
69
+ }
70
+
71
+ public function setStacktrace($stacktrace)
72
+ {
73
+ $this->stacktrace = $stacktrace;
74
+
75
+ return $this;
76
+ }
77
+
78
+ public function setCode($code)
79
+ {
80
+ $this->code = $code;
81
+
82
+ return $this;
83
+ }
84
+
85
+ public function setSeverity($severity)
86
+ {
87
+ if (!is_null($severity)) {
88
+ if (in_array($severity, Bugsnag_Error::$VALID_SEVERITIES)) {
89
+ $this->severity = $severity;
90
+ } else {
91
+ error_log('Bugsnag Warning: Tried to set error severity to '. $severity .' which is not allowed.');
92
+ }
93
+ }
94
+
95
+ return $this;
96
+ }
97
+
98
+ public function setPHPException(Exception $exception)
99
+ {
100
+ $this->setName(get_class($exception))
101
+ ->setMessage($exception->getMessage())
102
+ ->setStacktrace(Bugsnag_Stacktrace::fromBacktrace($this->config, $exception->getTrace(), $exception->getFile(), $exception->getLine()));
103
+
104
+ if (method_exists($exception, 'getPrevious')) {
105
+ $this->setPrevious($exception->getPrevious());
106
+ }
107
+
108
+ return $this;
109
+ }
110
+
111
+ public function setPHPError($code, $message, $file, $line, $fatal=false)
112
+ {
113
+ if ($fatal) {
114
+ // Generating stacktrace for PHP fatal errors is not possible,
115
+ // since this code executes when the PHP process shuts down,
116
+ // rather than at the time of the crash.
117
+ //
118
+ // In these situations, we generate a "stacktrace" containing only
119
+ // the line and file number where the crash occurred.
120
+ $stacktrace = Bugsnag_Stacktrace::fromFrame($this->config, $file, $line);
121
+ } else {
122
+ $stacktrace = Bugsnag_Stacktrace::generate($this->config);
123
+ }
124
+
125
+ $this->setName(Bugsnag_ErrorTypes::getName($code))
126
+ ->setMessage($message)
127
+ ->setSeverity(Bugsnag_ErrorTypes::getSeverity($code))
128
+ ->setStacktrace($stacktrace)
129
+ ->setCode($code);
130
+
131
+ return $this;
132
+ }
133
+
134
+ public function setMetaData($metaData)
135
+ {
136
+ if (is_array($metaData)) {
137
+ $this->metaData = array_merge_recursive($this->metaData, $metaData);
138
+ }
139
+
140
+ return $this;
141
+ }
142
+
143
+ public function setPrevious($exception)
144
+ {
145
+ if ($exception) {
146
+ $this->previous = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
147
+ }
148
+
149
+ return $this;
150
+ }
151
+
152
+ public function shouldIgnore()
153
+ {
154
+ // Check if we should ignore errors of this type
155
+ if (isset($this->code)) {
156
+ if (isset($this->config->errorReportingLevel)) {
157
+ return !($this->config->errorReportingLevel & $this->code);
158
+ } else {
159
+ return !(error_reporting() & $this->code);
160
+ }
161
+ }
162
+
163
+ return false;
164
+ }
165
+
166
+ public function toArray()
167
+ {
168
+ return array(
169
+ 'app' => $this->diagnostics->getAppData(),
170
+ 'device' => $this->diagnostics->getDeviceData(),
171
+ 'user' => $this->diagnostics->getUser(),
172
+ 'context' => $this->diagnostics->getContext(),
173
+ 'payloadVersion' => $this->payloadVersion,
174
+ 'severity' => $this->severity,
175
+ 'exceptions' => $this->exceptionArray(),
176
+ 'metaData' => $this->cleanupObj($this->metaData)
177
+ );
178
+ }
179
+
180
+ public function exceptionArray()
181
+ {
182
+ if ($this->previous) {
183
+ $exceptionArray = $this->previous->exceptionArray();
184
+ } else {
185
+ $exceptionArray = array();
186
+ }
187
+
188
+ $exceptionArray[] = array(
189
+ 'errorClass' => $this->name,
190
+ 'message' => $this->message,
191
+ 'stacktrace' => $this->stacktrace->toArray()
192
+ );
193
+
194
+ return $exceptionArray;
195
+ }
196
+
197
+ private function cleanupObj($obj)
198
+ {
199
+ if (empty($obj)) {
200
+ return NULL;
201
+ }
202
+
203
+ if (is_array($obj)) {
204
+ $cleanArray = array();
205
+ foreach ($obj as $key => $value) {
206
+ // Apply filters if required
207
+ if (is_array($this->config->filters)) {
208
+ // Check if this key should be filtered
209
+ $shouldFilter = false;
210
+ foreach ($this->config->filters as $filter) {
211
+ if (strpos($key, $filter) !== false) {
212
+ $shouldFilter = true;
213
+ break;
214
+ }
215
+ }
216
+
217
+ // Apply filters
218
+ if ($shouldFilter) {
219
+ $cleanArray[$key] = '[FILTERED]';
220
+ } else {
221
+ $cleanArray[$key] = $this->cleanupObj($value);
222
+ }
223
+ }
224
+ }
225
+
226
+ return $cleanArray;
227
+ } else if (is_string($obj)) {
228
+ // UTF8-encode if not already encoded
229
+ if (!mb_detect_encoding($obj, 'UTF-8', true)) {
230
+ return utf8_encode($obj);
231
+ } else {
232
+ return $obj;
233
+ }
234
+ } else if (is_object($obj)) {
235
+ // json_encode -> json_decode trick turns an object into an array
236
+ return $this->cleanupObj(json_decode(json_encode($obj), true));
237
+ } else {
238
+ return $obj;
239
+ }
240
+ }
241
+ }
lib/bugsnag-php/ErrorTypes.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_ErrorTypes
4
+ {
5
+ private static $ERROR_TYPES = array(
6
+ E_ERROR => array(
7
+ 'name' => 'PHP Fatal Error',
8
+ 'severity' => 'error'
9
+ ),
10
+
11
+ E_WARNING => array(
12
+ 'name' => 'PHP Warning',
13
+ 'severity' => 'warning'
14
+ ),
15
+
16
+ E_PARSE => array(
17
+ 'name' => 'PHP Parse Error',
18
+ 'severity' => 'error'
19
+ ),
20
+
21
+ E_NOTICE => array(
22
+ 'name' => 'PHP Notice',
23
+ 'severity' => 'info'
24
+ ),
25
+
26
+ E_CORE_ERROR => array(
27
+ 'name' => 'PHP Core Error',
28
+ 'severity' => 'error'
29
+ ),
30
+
31
+ E_CORE_WARNING => array(
32
+ 'name' => 'PHP Core Warning',
33
+ 'severity' => 'warning'
34
+ ),
35
+
36
+ E_COMPILE_ERROR => array(
37
+ 'name' => 'PHP Compile Error',
38
+ 'severity' => 'error'
39
+ ),
40
+
41
+ E_COMPILE_WARNING => array(
42
+ 'name' => 'PHP Compile Warning',
43
+ 'severity' => 'warning'
44
+ ),
45
+
46
+ E_USER_ERROR => array(
47
+ 'name' => 'User Error',
48
+ 'severity' => 'error'
49
+ ),
50
+
51
+ E_USER_WARNING => array(
52
+ 'name' => 'User Warning',
53
+ 'severity' => 'warning'
54
+ ),
55
+
56
+ E_USER_NOTICE => array(
57
+ 'name' => 'User Notice',
58
+ 'severity' => 'info'
59
+ ),
60
+
61
+ E_STRICT => array(
62
+ 'name' => 'PHP Strict',
63
+ 'severity' => 'info'
64
+ ),
65
+
66
+ E_RECOVERABLE_ERROR => array(
67
+ 'name' => 'PHP Recoverable Error',
68
+ 'severity' => 'error'
69
+ ),
70
+
71
+ // E_DEPRECATED (Since PHP 5.3.0)
72
+ 8192 => array(
73
+ 'name' => 'PHP Deprecated',
74
+ 'severity' => 'info'
75
+ ),
76
+
77
+ // E_USER_DEPRECATED (Since PHP 5.3.0)
78
+ 16384 => array(
79
+ 'name' => 'User Deprecated',
80
+ 'severity' => 'info'
81
+ )
82
+ );
83
+
84
+ public static function isFatal($code)
85
+ {
86
+ return self::getSeverity($code) == 'error';
87
+ }
88
+
89
+ public static function getName($code)
90
+ {
91
+ if (array_key_exists($code, self::$ERROR_TYPES)) {
92
+ return self::$ERROR_TYPES[$code]['name'];
93
+ } else {
94
+ return "Unknown";
95
+ }
96
+ }
97
+
98
+ public static function getSeverity($code)
99
+ {
100
+ if (array_key_exists($code, self::$ERROR_TYPES)) {
101
+ return self::$ERROR_TYPES[$code]['severity'];
102
+ } else {
103
+ return "error";
104
+ }
105
+ }
106
+
107
+ public static function getLevelsForSeverity($severity)
108
+ {
109
+ $levels = 0;
110
+ foreach (Bugsnag_ErrorTypes::$ERROR_TYPES as $level => $info) {
111
+ if ($info['severity'] == $severity) {
112
+ $levels |= $level;
113
+ }
114
+ }
115
+
116
+ return $levels;
117
+ }
118
+ }
lib/bugsnag-php/Notification.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Notification
4
+ {
5
+ private static $CONTENT_TYPE_HEADER = 'Content-type: application/json';
6
+
7
+ private $config;
8
+ private $errorQueue = array();
9
+
10
+ public function __construct(Bugsnag_Configuration $config)
11
+ {
12
+ $this->config = $config;
13
+ }
14
+
15
+ public function addError($error, $passedMetaData=array())
16
+ {
17
+ // Check if this error should be sent to Bugsnag
18
+ if (!$this->config->shouldNotify()) {
19
+ return FALSE;
20
+ }
21
+
22
+ // Add global meta-data to error
23
+ $error->setMetaData($this->config->metaData);
24
+
25
+ // Add request meta-data to error
26
+ if (Bugsnag_Request::isRequest()) {
27
+ $error->setMetaData(Bugsnag_Request::getRequestMetaData());
28
+ }
29
+
30
+ // Add environment meta-data to error
31
+ if (!empty($_ENV)) {
32
+ $error->setMetaData(array("Environment" => $_ENV));
33
+ }
34
+
35
+ // Add user-specified meta-data to error
36
+ $error->setMetaData($passedMetaData);
37
+
38
+ // Run beforeNotify function (can cause more meta-data to be merged)
39
+ if (isset($this->config->beforeNotifyFunction) && is_callable($this->config->beforeNotifyFunction)) {
40
+ $beforeNotifyReturn = call_user_func($this->config->beforeNotifyFunction, $error);
41
+ }
42
+
43
+ // Skip this error if the beforeNotify function returned FALSE
44
+ if (!isset($beforeNotifyReturn) || $beforeNotifyReturn !== FALSE) {
45
+ $this->errorQueue[] = $error;
46
+
47
+ return TRUE;
48
+ } else {
49
+ return FALSE;
50
+ }
51
+ }
52
+
53
+ public function toArray()
54
+ {
55
+ $events = array();
56
+ foreach ($this->errorQueue as $error) {
57
+ $errorArray = $error->toArray();
58
+
59
+ if (!is_null($errorArray)) {
60
+ $events[] = $errorArray;
61
+ }
62
+ }
63
+
64
+ return array(
65
+ 'apiKey' => $this->config->apiKey,
66
+ 'notifier' => $this->config->notifier,
67
+ 'events' => $events
68
+ );
69
+ }
70
+
71
+ public function deliver()
72
+ {
73
+ if (!empty($this->errorQueue)) {
74
+ // Post the request to bugsnag
75
+ $this->postJSON($this->config->getNotifyEndpoint(), $this->toArray());
76
+
77
+ // Clear the error queue
78
+ $this->errorQueue = array();
79
+ }
80
+ }
81
+
82
+ public function postJSON($url, $data)
83
+ {
84
+ $body = json_encode($data);
85
+
86
+ // Prefer cURL if it is installed, otherwise fall back to fopen()
87
+ // cURL supports both timeouts and proxies
88
+ if (function_exists('curl_version')) {
89
+ $this->postWithCurl($url, $body);
90
+ } else {
91
+ $this->postWithFopen($url, $body);
92
+ }
93
+ }
94
+
95
+ private function postWithCurl($url, $body)
96
+ {
97
+ $http = curl_init($url);
98
+
99
+ // Default curl settings
100
+ curl_setopt($http, CURLOPT_HEADER, false);
101
+ curl_setopt($http, CURLOPT_RETURNTRANSFER, true);
102
+ curl_setopt($http, CURLOPT_POST, true);
103
+ curl_setopt($http, CURLOPT_HTTPHEADER, array(Bugsnag_Notification::$CONTENT_TYPE_HEADER));
104
+ curl_setopt($http, CURLOPT_POSTFIELDS, $body);
105
+ curl_setopt($http, CURLOPT_CONNECTTIMEOUT, $this->config->timeout);
106
+ curl_setopt($http, CURLOPT_SSL_VERIFYPEER, false);
107
+ curl_setopt($http, CURLOPT_VERBOSE, false);
108
+
109
+ // Apply proxy settings (if present)
110
+ if (count($this->config->proxySettings)) {
111
+ if (isset($this->config->proxySettings['host'])) {
112
+ curl_setopt($http, CURLOPT_PROXY, $this->config->proxySettings['host']);
113
+ }
114
+ if (isset($this->config->proxySettings['port'])) {
115
+ curl_setopt($http, CURLOPT_PROXYPORT, $this->config->proxySettings['port']);
116
+ }
117
+ if (isset($this->config->proxySettings['user'])) {
118
+ $userPassword = $this->config->proxySettings['user'] . ':';
119
+ $userPassword .= isset($this->config->proxySettings['password'])? $this->config->proxySettings['password'] : '';
120
+ curl_setopt($http, CURLOPT_PROXYUSERPWD, $userPassword);
121
+ }
122
+ }
123
+
124
+ // Execute the request and fetch the response
125
+ $responseBody = curl_exec($http);
126
+ $statusCode = curl_getinfo($http, CURLINFO_HTTP_CODE);
127
+
128
+ if ($statusCode > 200) {
129
+ error_log('Bugsnag Warning: Couldn\'t notify ('.$responseBody.')');
130
+ }
131
+
132
+ if (curl_errno($http)) {
133
+ error_log('Bugsnag Warning: Couldn\'t notify (' . curl_error($http).')');
134
+ }
135
+
136
+ curl_close($http);
137
+ }
138
+
139
+ private function postWithFopen($url, $body)
140
+ {
141
+ // Warn about lack of proxy support if we are using fopen()
142
+ if (count($this->config->proxySettings)) {
143
+ error_log('Bugsnag Warning: Can\'t use proxy settings unless cURL is installed');
144
+ }
145
+
146
+ // Warn about lack of timeout support if we are using fopen()
147
+ if ($this->config->timeout != Bugsnag_Configuration::$DEFAULT_TIMEOUT) {
148
+ error_log('Bugsnag Warning: Can\'t change timeout settings unless cURL is installed');
149
+ }
150
+
151
+ // Create the request context
152
+ $context = stream_context_create(array(
153
+ 'http' => array(
154
+ 'method' => 'POST',
155
+ 'header' => Bugsnag_Notification::$CONTENT_TYPE_HEADER.'\r\n',
156
+ 'content' => $body
157
+ ),
158
+ 'ssl' => array(
159
+ 'verify_peer' => false,
160
+ )
161
+ ));
162
+
163
+ // Execute the request and fetch the response
164
+ if ($stream = fopen($url, 'rb', false, $context)) {
165
+ $response = stream_get_contents($stream);
166
+
167
+ if (!$response) {
168
+ error_log('Bugsnag Warning: Couldn\'t notify (no response)');
169
+ }
170
+ } else {
171
+ error_log('Bugsnag Warning: Couldn\'t notify (fopen failed)');
172
+ }
173
+ }
174
+ }
lib/bugsnag-php/Request.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Request
4
+ {
5
+ public static function isRequest()
6
+ {
7
+ return isset($_SERVER['REQUEST_METHOD']);
8
+ }
9
+
10
+ public static function getRequestMetaData()
11
+ {
12
+ $requestData = array();
13
+
14
+ // Request Tab
15
+ $requestData['request'] = array();
16
+ $requestData['request']['url'] = self::getCurrentUrl();
17
+ if (isset($_SERVER['REQUEST_METHOD'])) {
18
+ $requestData['request']['httpMethod'] = $_SERVER['REQUEST_METHOD'];
19
+ }
20
+
21
+ if (!empty($_POST)) {
22
+ $requestData['request']['params'] = $_POST;
23
+ } else {
24
+ if (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'], 'application/json') === 0) {
25
+ $requestData['request']['params'] = json_decode(file_get_contents('php://input'));
26
+ }
27
+ }
28
+
29
+ $requestData['request']['ip'] = self::getRequestIp();
30
+ if (isset($_SERVER['HTTP_USER_AGENT'])) {
31
+ $requestData['request']['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
32
+ }
33
+
34
+ if (function_exists("getallheaders")) {
35
+ $headers = getallheaders();
36
+ if (!empty($headers)) {
37
+ $requestData['request']['headers'] = $headers;
38
+ }
39
+ }
40
+
41
+ // Session Tab
42
+ if (!empty($_SESSION)) {
43
+ $requestData['session'] = $_SESSION;
44
+ }
45
+
46
+ // Cookies Tab
47
+ if (!empty($_COOKIE)) {
48
+ $requestData['cookies'] = $_COOKIE;
49
+ }
50
+
51
+ return $requestData;
52
+ }
53
+
54
+ public static function getContext()
55
+ {
56
+ if (self::isRequest() && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER["REQUEST_URI"])) {
57
+ return $_SERVER['REQUEST_METHOD'] . ' ' . strtok($_SERVER["REQUEST_URI"], '?');
58
+ } else {
59
+ return null;
60
+ }
61
+ }
62
+
63
+ public static function getUserId()
64
+ {
65
+ if (self::isRequest()) {
66
+ return self::getRequestIp();
67
+ } else {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ public static function getCurrentUrl()
73
+ {
74
+ $schema = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://';
75
+
76
+ return $schema.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
77
+ }
78
+
79
+ public static function getRequestIp()
80
+ {
81
+ return isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
82
+ }
83
+ }
lib/bugsnag-php/Stacktrace.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Bugsnag_Stacktrace
4
+ {
5
+ public $frames = array();
6
+ private $config;
7
+
8
+ public static function generate($config)
9
+ {
10
+ // Reduce memory usage by omitting args and objects from backtrace
11
+ if (version_compare(PHP_VERSION, '5.3.6') >= 0) {
12
+ $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS & ~DEBUG_BACKTRACE_PROVIDE_OBJECT);
13
+ } elseif (version_compare(PHP_VERSION, '5.2.5') >= 0) {
14
+ $backtrace = debug_backtrace(FALSE);
15
+ } else {
16
+ $backtrace = debug_backtrace();
17
+ }
18
+
19
+ return self::fromBacktrace($config, $backtrace, "[generator]", 0);
20
+ }
21
+
22
+ public static function fromFrame($config, $file, $line)
23
+ {
24
+ $stacktrace = new Bugsnag_Stacktrace($config);
25
+ $stacktrace->addFrame($file, $line, "[unknown]");
26
+
27
+ return $stacktrace;
28
+ }
29
+
30
+ public static function fromBacktrace($config, $backtrace, $topFile, $topLine)
31
+ {
32
+ $stacktrace = new Bugsnag_Stacktrace($config);
33
+
34
+ // PHP backtrace's are misaligned, we need to shift the file/line down a frame
35
+ foreach ($backtrace as $frame) {
36
+ if (!self::frameInsideBugsnag($frame)) {
37
+ $stacktrace->addFrame($topFile, $topLine, $frame['function'], isset($frame['class']) ? $frame['class'] : NULL);
38
+ }
39
+
40
+ if (isset($frame['file']) && isset($frame['line'])) {
41
+ $topFile = $frame['file'];
42
+ $topLine = $frame['line'];
43
+ } else {
44
+ $topFile = "[internal]";
45
+ $topLine = 0;
46
+ }
47
+ }
48
+
49
+ // Add a final stackframe for the "main" method
50
+ $stacktrace->addFrame($topFile, $topLine, '[main]');
51
+
52
+ return $stacktrace;
53
+ }
54
+
55
+ public static function frameInsideBugsnag($frame)
56
+ {
57
+ return isset($frame['class']) && strpos($frame['class'], 'Bugsnag_') === 0;
58
+ }
59
+
60
+
61
+ public function __construct($config)
62
+ {
63
+ $this->config = $config;
64
+ }
65
+
66
+ public function toArray()
67
+ {
68
+ return $this->frames;
69
+ }
70
+
71
+ public function addFrame($file, $line, $method, $class=NULL)
72
+ {
73
+ // Check if this frame is inProject
74
+ $inProject = !is_null($this->config->projectRootRegex) && preg_match($this->config->projectRootRegex, $file);
75
+
76
+ // Strip out projectRoot from start of file path
77
+ if (!is_null($this->config->stripPathRegex)) {
78
+ $file = preg_replace($this->config->stripPathRegex, '', $file);
79
+ }
80
+
81
+ // Construct the frame
82
+ $frame = array(
83
+ 'file' => $file,
84
+ 'lineNumber' => $line,
85
+ 'method' => $method,
86
+ 'inProject' => $inProject
87
+ );
88
+
89
+ if (!empty($class)) {
90
+ $frame['class'] = $class;
91
+ }
92
+
93
+ $this->frames[] = $frame;
94
+ }
95
+ }
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Bugsnag_Notifier</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <license>MIT License</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>The Bugsnag Notifier for Magento gives you instant notification of errors in your Magento applications.</summary>
10
+ <description>Bugsnag captures errors in real-time from your web, mobile and desktop applications, helping you to understand and resolve them as fast as possible. Create a free account to start capturing errors from your applications.</description>
11
+ <notes>The first release!</notes>
12
+ <authors><author><name>MAG002804728</name><user>MAG002804728</user><email>kyrylo@bugsnag.com</email></author></authors>
13
+ <date>2014-09-05</date>
14
+ <time>21:05:54</time>
15
+ <contents><target name="magecommunity"><dir name="Bugsnag"><dir name="Notifier"><dir name="Block"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Form"><file name="Button.php" hash="6d8ad5ed45ecddc929d8f627a663779d"/></dir></dir></dir></dir></dir><dir name="Model"><file name="Observer.php" hash="e64e947aaad75845173f73ed611ba334"/><file name="Severity.php" hash="2ffc28c09bee8ae4d3194ceb33e93cd4"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="BugsnagController.php" hash="26f257486b19e4674f47b47f63529821"/></dir></dir><dir name="etc"><file name="config.xml" hash="212f7e4cf9650c2d54f098e56b4d1514"/><file name="system.xml" hash="6c74fe6e6e23fc5685002ed103a53366"/></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="bugsnag"><dir name="system"><dir name="config"><file name="button.phtml" hash="3cbf97942a6124b5bbdc1633dff99352"/></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Bugsnag_Notifier.xml" hash="5af25ac572e2c7e04f03edfa30d36976"/></dir></target><target name="magelib"><dir name="bugsnag-php"><file name="Autoload.php" hash="141a845b8baaebee4232b30540d5cda5"/><file name="Client.php" hash="4c43e67c648cac333f78f363cf37dc69"/><file name="Configuration.php" hash="805b07608edb9a2d930558fa52ade5a7"/><file name="Diagnostics.php" hash="2ee79a831f84025135ef4fc0c8550747"/><file name="Error.php" hash="e8e1cb0bc961263185c4ec9e68387a65"/><file name="ErrorTypes.php" hash="118f8a29bfcd0d33a98363c30eb9f39f"/><file name="Notification.php" hash="6cf0c6f5a30852f559d6beec0a40ef0c"/><file name="Request.php" hash="422a3cc71777af8bb426988a77dcaa6e"/><file name="Stacktrace.php" hash="9d131aa378157acbffba7136128fe57d"/></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.2.0</min><max>5.5.15</max></php></required></dependencies>
18
+ </package>