Plugin Directory

Changeset 1542350


Ignore:
Timestamp:
11/29/2016 12:54:44 AM (9 years ago)
Author:
loopj
Message:

Release version 1.3.0

Location:
bugsnag/trunk
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • bugsnag/trunk/bugsnag-php/Autoload.php

    r871602 r1542350  
    11<?php
    22
    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";
     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.
     8require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Client.php';
     9require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Configuration.php';
     10require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Diagnostics.php';
     11require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Error.php';
     12require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'ErrorTypes.php';
     13require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Notification.php';
     14require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Request.php';
     15require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Stacktrace.php';
  • bugsnag/trunk/bugsnag-php/Client.php

    r871602 r1542350  
    33class Bugsnag_Client
    44{
     5    /**
     6     * The config instance.
     7     *
     8     * @var Bugsnag_Configuration
     9     */
    510    private $config;
     11
     12    /**
     13     * The diagnostics instance.
     14     *
     15     * @var Bugsnag_Diagnostics
     16     */
     17    private $diagnostics;
     18
     19    /**
     20     * The notification instance.
     21     *
     22     * @var Bugsnag_Notification|null
     23     */
    624    private $notification;
    725
    826    /**
    9      * Initialize Bugsnag
    10      *
    11      * @param String $apiKey your Bugsnag API key
     27     * Create a new client instance.
     28     *
     29     * @param string $apiKey your Bugsnag API key
     30     *
     31     * @throws Exception
     32     *
     33     * @return void
    1234     */
    1335    public function __construct($apiKey)
     
    1840        }
    1941
    20         // Check cURL is enabled
    21         if (!function_exists('curl_version')) {
    22             throw new Exception('Bugsnag Error: Bugsnag requires cURL support to be enabled on your PHP installation');
    23         }
    24 
    2542        // Create a configuration object
    2643        $this->config = new Bugsnag_Configuration();
     
    3047        $this->diagnostics = new Bugsnag_Diagnostics($this->config);
    3148
    32         // Attempt to determine a sensible default for projectRoot
    33         if (isset($_SERVER) && !empty($_SERVER['DOCUMENT_ROOT'])) {
    34             $this->setProjectRoot($_SERVER['DOCUMENT_ROOT']);
    35         }
    36 
    3749        // Register a shutdown function to check for fatal errors
    3850        // and flush any buffered errors
     
    4153
    4254    /**
    43      * Set your release stage, eg "production" or "development"
    44      *
    45      * @param String $releaseStage the app's current release stage
     55     * Set your release stage, eg "production" or "development".
     56     *
     57     * @param string $releaseStage the app's current release stage
     58     *
     59     * @return $this
    4660     */
    4761    public function setReleaseStage($releaseStage)
     
    5367
    5468    /**
    55      * Set your app's semantic version, eg "1.2.3"
    56      *
    57      * @param String $appVersion the app's version
     69     * Set your app's semantic version, eg "1.2.3".
     70     *
     71     * @param string $appVersion the app's version
     72     *
     73     * @return $this
    5874     */
    5975    public function setAppVersion($appVersion)
     
    6581
    6682    /**
    67      * Set which release stages should be allowed to notify Bugsnag
    68      * eg array("production", "development")
    69      *
    70      * @param Array $notifyReleaseStages array of release stages to notify for
     83     * Set the host name.
     84     *
     85     * @param string $hostname the host name
     86     *
     87     * @return $this
     88     */
     89    public function setHostname($hostname)
     90    {
     91        $this->config->hostname = $hostname;
     92
     93        return $this;
     94    }
     95
     96    /**
     97     * Set which release stages should be allowed to notify Bugsnag.
     98     *
     99     * Eg array('production', 'development').
     100     *
     101     * @param array $notifyReleaseStages array of release stages to notify for
     102     *
     103     * @return $this
    71104     */
    72105    public function setNotifyReleaseStages(array $notifyReleaseStages)
     
    80113     * Set which Bugsnag endpoint to send errors to.
    81114     *
    82      * @param String $endpoint endpoint URL
     115     * @param string $endpoint endpoint URL
     116     *
     117     * @return $this
    83118     */
    84119    public function setEndpoint($endpoint)
     
    90125
    91126    /**
    92      * Set whether or not to use SSL when notifying bugsnag
    93      *
    94      * @param Boolean $useSSL whether to use SSL
     127     * Enable debug mode to help diagnose problems.
     128     *
     129     * @param bool $debug whether to enable debug mode
     130     *
     131     * @return $this
     132     */
     133    public function setDebug($debug)
     134    {
     135        $this->config->debug = $debug;
     136
     137        return $this;
     138    }
     139
     140    /**
     141     * Set whether or not to use SSL when notifying bugsnag.
     142     *
     143     * @param bool $useSSL whether to use SSL
     144     *
     145     * @return $this
     146     *
     147     * @deprecated since version 2.5. Pass full URLs to setEndpoint.
    95148     */
    96149    public function setUseSSL($useSSL)
     
    102155
    103156    /**
    104      * Set the desired timeout for cURL connection when notifying bugsnag
    105      *
    106      * @param Integer $timeout the desired timeout in seconds
     157     * Set the desired timeout for cURL connection when notifying bugsnag.
     158     *
     159     * @param int $timeout the desired timeout in seconds
     160     *
     161     * @return $this
    107162     */
    108163    public function setTimeout($timeout)
     
    115170    /**
    116171     * Set the absolute path to the root of your application.
     172     *
    117173     * We use this to help with error grouping and to highlight "in project"
    118174     * stacktrace lines.
    119175     *
    120      * @param String $projectRoot the root path for your application
     176     * @param string $projectRoot the root path for your application
     177     *
     178     * @return $this
    121179     */
    122180    public function setProjectRoot($projectRoot)
     
    128186
    129187    /**
    130      * Set the a regular expression for matching filenames in stacktrace lines
    131      * that are part of your application.
    132      *
    133      * @param String $projectRootRegex regex matching paths belong to your project
     188     * Set the absolute split path.
     189     *
     190     * This is the path that should be stripped from the beginning of any
     191     * stacktrace file line. This helps to normalise filenames for grouping
     192     * and reduces the noise in stack traces.
     193     *
     194     * @param string $stripPath the path to strip from filenames
     195     *
     196     * @return $this
     197     */
     198    public function setStripPath($stripPath)
     199    {
     200        $this->config->setStripPath($stripPath);
     201
     202        return $this;
     203    }
     204
     205    /**
     206     * Set the a regular expression for matching filenames in stacktrace lines.
     207     *
     208     * @param string $projectRootRegex regex matching paths belong to your project
     209     *
     210     * @return $this
    134211     */
    135212    public function setProjectRootRegex($projectRootRegex)
     
    141218
    142219    /**
    143      * Set the strings to filter out from metaData arrays before sending then
    144      * to Bugsnag. Eg. array("password", "credit_card")
    145      *
    146      * @param Array $filters an array of metaData filters
     220     * Set the strings to filter out from metaData arrays before sending then.
     221     *
     222     * Eg. array('password', 'credit_card').
     223     *
     224     * @param array $filters an array of metaData filters
     225     *
     226     * @return $this
    147227     */
    148228    public function setFilters(array $filters)
     
    154234
    155235    /**
    156      * Set information about the current user of your app, including
    157      * id, name and email.
    158      *
    159      * @param Array $user an array of user information. Eg:
     236     * Set information about the current user of your app, including id, name and email.
     237     *
     238     * @param array $user an array of user information. Eg:
    160239     *        array(
    161240     *            'name' => 'Bob Hoskins',
    162241     *            'email' => '[email protected]'
    163242     *        )
     243     *
     244     * @return $this
    164245     */
    165246    public function setUser(array $user)
     
    171252
    172253    /**
    173      * @deprecated deprecated since version 2.1
     254     * @param $userId
     255     *
     256     * @return $this
     257     *
     258     * @deprecated since version 2.1. Use setUser instead.
    174259     */
    175260    public function setUserId($userId)
     
    187272     * Set a context representing the current type of request, or location in code.
    188273     *
    189      * @param String $context the current context
     274     * @param string $context the current context
     275     *
     276     * @return $this
    190277     */
    191278    public function setContext($context)
     
    197284
    198285    /**
    199      * Set the type of application executing the code. This is usually used to
    200      * represent if you are running plain PHP code "php", via a framework,
    201      * eg "laravel", or executing through delayed worker code, eg "resque".
    202      *
    203      * @param String $type the current type
     286     * Set the type of application executing the code.
     287     *
     288     * This is usually used to represent if you are running plain PHP code
     289     * "php", via a framework, eg "laravel", or executing through delayed
     290     * worker code, eg "resque".
     291     *
     292     * @param string $type the current type
     293     *
     294     * @return $this
    204295     */
    205296    public function setType($type)
     
    211302
    212303    /**
    213      * Set custom metadata to send to Bugsnag with every error. You can use
    214      * this to add custom tabs of data to each error on your Bugsnag dashboard
    215      *
    216      * @param Array $metaData an array of arrays of custom data. Eg:
     304     * Set custom metadata to send to Bugsnag with every error.
     305     *
     306     * You can use this to add custom tabs of data to each error on your
     307     * Bugsnag dashboard.
     308     *
     309     * @param array $metaData an array of arrays of custom data. Eg:
    217310     *        array(
    218      *            "user" => array(
    219      *                "name" => "James",
    220      *                "email" => "[email protected]"
     311     *            'user' => array(
     312     *                'name' => 'James',
     313     *                'email' => '[email protected]'
    221314     *            )
    222315     *        )
    223      */
    224     public function setMetaData(array $metaData)
    225     {
    226         $this->config->metaData = $metaData;
    227 
    228         return $this;
    229     }
    230 
    231     /**
    232      * Set proxy configuration
    233      *
    234      * @param Array $proxySettings an array with proxy settings. Eg:
     316     * @param bool $merge optionally merge the meta data
     317     *
     318     * @return $this
     319     */
     320    public function setMetaData(array $metaData, $merge = false)
     321    {
     322        if ($merge) {
     323            $this->config->metaData = array_merge_recursive((array) $this->config->metaData, $metaData);
     324        } else {
     325            $this->config->metaData = $metaData;
     326        }
     327
     328        return $this;
     329    }
     330
     331    /**
     332     * Set proxy configuration.
     333     *
     334     * @param array $proxySettings an array with proxy settings. Eg:
    235335     *        array(
    236      *            'host'     => "bugsnag.com",
     336     *            'host'     => 'bugsnag.com',
    237337     *            'port'     => 42,
    238      *            'user'     => "username"
    239      *            'password' => "password123"
    240      *            )
     338     *            'user'     => 'username'
     339     *            'password' => 'password123'
     340     *        )
     341     *
     342     * @return $this
    241343     */
    242344    public function setProxySettings(array $proxySettings)
     
    248350
    249351    /**
     352     * Set custom curl options.
     353     *
     354     * @param array $curlOptions an array with curl options. Eg:
     355     *        array(
     356     *            CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4
     357     *        )
     358     *
     359     * @return $this
     360     */
     361    public function setCurlOptions(array $curlOptions)
     362    {
     363        $this->config->curlOptions = $curlOptions;
     364
     365        return $this;
     366    }
     367
     368    /**
    250369     * Set a custom function to call before notifying Bugsnag of an error.
     370     *
    251371     * You can use this to call your own error handling functions, or to add
    252372     * custom tabs of data to each error on your Bugsnag dashboard.
     
    255375     * function before_bugsnag_notify($error) {
    256376     *     $error->addMetaData(array(
    257      *         "user" => array(
    258      *             "name" => "James"
     377     *         'user' => array(
     378     *             'name' => 'James'
    259379     *         )
    260380     *     ));
    261381     * }
    262      * $bugsnag->setBeforeNotifyFunction("before_bugsnag_notify");
    263      *
    264     */
     382     * $bugsnag->setBeforeNotifyFunction('before_bugsnag_notify');
     383     *
     384     * @param callable $beforeNotifyFunction
     385     *
     386     * @return $this
     387     */
    265388    public function setBeforeNotifyFunction($beforeNotifyFunction)
    266389    {
     
    272395    /**
    273396     * Set Bugsnag's error reporting level.
     397     *
    274398     * If this is not set, we'll use your current PHP error_reporting value
    275399     * from your ini file or error_reporting(...) calls.
    276400     *
    277      * @param Integer $errorReportingLevel the error reporting level integer
     401     * @param int $errorReportingLevel the error reporting level integer
    278402     *                exactly as you would pass to PHP's error_reporting
     403     *
     404     * @return $this
    279405     */
    280406    public function setErrorReportingLevel($errorReportingLevel)
     
    286412
    287413    /**
    288      * Sets whether Bugsnag should be automatically notified of unhandled
    289      * exceptions and errors.
    290      *
    291      * @param Boolean $autoNotify whether to auto notify or not
     414     * Sets whether Bugsnag should be automatically notified of unhandled exceptions and errors.
     415     *
     416     * @param bool $autoNotify whether to auto notify or not
     417     *
     418     * @return $this
    292419     */
    293420    public function setAutoNotify($autoNotify)
     
    299426
    300427    /**
    301      * Sets whether errors should be batched together and send at the end of
    302      * each request.
    303      *
    304      * @param Boolean $batchSending whether to batch together errors
     428     * Sets whether errors should be batched together and send at the end of each request.
     429     *
     430     * @param bool $batchSending whether to batch together errors
     431     *
     432     * @return $this
    305433     */
    306434    public function setBatchSending($batchSending)
     
    312440
    313441    /**
    314      * Sets the notifier to report as to Bugsnag. This should only be
    315      * set by other notifier libraries.
    316      *
    317      * @param Array  $notifier  an array of name, version, url.
     442     * Sets the notifier to report as to Bugsnag.
     443     *
     444     * This should only be set by other notifier libraries.
     445     *
     446     * @param array $notifier an array of name, version, url.
     447     *
     448     * @return $this
    318449     */
    319450    public function setNotifier($notifier)
     
    325456
    326457    /**
    327      * Notify Bugsnag of a non-fatal/handled exception
    328      *
    329      * @param Exception $exception the exception to notify Bugsnag about
    330      * @param Array     $metaData  optional metaData to send with this error
    331      * @param String    $severity  optional severity of this error (fatal/error/warning/info)
    332      */
    333     public function notifyException(Exception $exception, array $metaData=null, $severity=null)
    334     {
    335         $error = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
    336         $error->setSeverity($severity);
    337 
    338         $this->notify($error, $metaData);
    339     }
    340 
    341     /**
    342      * Notify Bugsnag of a non-fatal/handled error
    343      *
    344      * @param String $errorName    the name of the error, a short (1 word) string
    345      * @param String $errorMessage the error message
    346      * @param Array  $metaData     optional metaData to send with this error
    347      * @param String $severity     optional severity of this error (fatal/error/warning/info)
    348      */
    349     public function notifyError($name, $message, array $metaData=null, $severity=null)
     458     * Sets whether Bugsnag should send $_ENV with each error.
     459     *
     460     * @param bool $sendEnvironment whether to send the environment
     461     *
     462     * @return $this
     463     */
     464    public function setSendEnvironment($sendEnvironment)
     465    {
     466        $this->config->sendEnvironment = $sendEnvironment;
     467
     468        return $this;
     469    }
     470
     471    /**
     472     * Sets whether Bugsnag should send $_COOKIE with each error.
     473     *
     474     * @param bool $sendCookies whether to send the environment
     475     *
     476     * @return $this
     477     */
     478    public function setSendCookies($sendCookies)
     479    {
     480        $this->config->sendCookies = $sendCookies;
     481
     482        return $this;
     483    }
     484
     485    /**
     486     * Sets whether Bugsnag should send $_SESSION with each error.
     487     *
     488     * @param bool $sendSession whether to send the environment
     489     *
     490     * @return $this
     491     */
     492    public function setSendSession($sendSession)
     493    {
     494        $this->config->sendSession = $sendSession;
     495
     496        return $this;
     497    }
     498
     499    /**
     500     * Should we send a small snippet of the code that crashed.
     501     *
     502     * This can help you diagnose even faster from within your dashboard.
     503     *
     504     * @param bool $sendCode whether to send code to Bugsnag
     505     *
     506     * @return $this
     507     */
     508    public function setSendCode($sendCode)
     509    {
     510        $this->config->sendCode = $sendCode;
     511
     512        return $this;
     513    }
     514
     515    /**
     516     * Notify Bugsnag of a non-fatal/handled throwable.
     517     *
     518     * @param Throwable $throwable the throwable to notify Bugsnag about
     519     * @param array     $metaData  optional metaData to send with this error
     520     * @param string    $severity  optional severity of this error (fatal/error/warning/info)
     521     *
     522     * @return void
     523     */
     524    public function notifyException($throwable, array $metaData = null, $severity = null)
     525    {
     526        if (is_subclass_of($throwable, 'Throwable') || is_subclass_of($throwable, 'Exception') || get_class($throwable) == 'Exception') {
     527            $error = Bugsnag_Error::fromPHPThrowable($this->config, $this->diagnostics, $throwable);
     528            $error->setSeverity($severity);
     529
     530            $this->notify($error, $metaData);
     531        }
     532    }
     533
     534    /**
     535     * Notify Bugsnag of a non-fatal/handled error.
     536     *
     537     * @param string $name     the name of the error, a short (1 word) string
     538     * @param string $message  the error message
     539     * @param array  $metaData optional metaData to send with this error
     540     * @param string $severity optional severity of this error (fatal/error/warning/info)
     541     *
     542     * @return void
     543     */
     544    public function notifyError($name, $message, array $metaData = null, $severity = null)
    350545    {
    351546        $error = Bugsnag_Error::fromNamedError($this->config, $this->diagnostics, $name, $message);
     
    355550    }
    356551
    357     // Exception handler callback, should only be called internally by PHP's set_exception_handler
    358     public function exceptionHandler($exception)
    359     {
    360         $error = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
    361         $error->setSeverity("fatal");
    362 
    363         if (!$error->shouldIgnore() && $this->config->autoNotify) {
    364             $this->notify($error);
    365         }
    366     }
    367 
    368     // Exception handler callback, should only be called internally by PHP's set_error_handler
    369     public function errorHandler($errno, $errstr, $errfile='', $errline=0)
    370     {
     552    /**
     553     * Exception handler callback.
     554     *
     555     * Should only be called internally by PHP's set_exception_handler.
     556     *
     557     * @param Throwable $throwable the exception was was thrown
     558     *
     559     * @return void
     560     */
     561    public function exceptionHandler($throwable)
     562    {
     563        if (!$this->config->autoNotify) {
     564            return;
     565        }
     566
     567        $error = Bugsnag_Error::fromPHPThrowable($this->config, $this->diagnostics, $throwable);
     568        $error->setSeverity('error');
     569        $this->notify($error);
     570    }
     571
     572    /**
     573     * Error handler callback.
     574     *
     575     * Should only be called internally by PHP's set_error_handler.
     576     *
     577     * @param int    $errno   the level of the error raised
     578     * @param string $errstr  the error message
     579     * @param string $errfile the filename that the error was raised in
     580     * @param int    $errline the line number the error was raised at
     581     *
     582     * @return void
     583     */
     584    public function errorHandler($errno, $errstr, $errfile = '', $errline = 0)
     585    {
     586        if (!$this->config->autoNotify || $this->config->shouldIgnoreErrorCode($errno)) {
     587            return;
     588        }
     589
    371590        $error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $errno, $errstr, $errfile, $errline);
    372 
    373         if (!$error->shouldIgnore() && $this->config->autoNotify) {
    374             $this->notify($error);
    375         }
    376     }
    377 
    378     // Shutdown handler callback, called when the PHP process has finished running
    379     // Should only be called internally by PHP's register_shutdown_function
     591        $this->notify($error);
     592    }
     593
     594    /**
     595     * Shutdown handler callback.
     596     *
     597     * Called when the PHP process has finished running. Should only be called
     598     * internally by PHP's register_shutdown_function.
     599     *
     600     * @return void
     601     */
    380602    public function shutdownHandler()
    381603    {
     
    384606
    385607        // Check if a fatal error caused this shutdown
    386         if (!is_null($lastError) && Bugsnag_ErrorTypes::isFatal($lastError['type'])) {
     608        if (!is_null($lastError) && Bugsnag_ErrorTypes::isFatal($lastError['type']) && $this->config->autoNotify && !$this->config->shouldIgnoreErrorCode($lastError['type'])) {
    387609            $error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $lastError['type'], $lastError['message'], $lastError['file'], $lastError['line'], true);
    388             $error->setSeverity("fatal");
    389 
    390             if (!$error->shouldIgnore() && $this->config->autoNotify) {
    391                 $this->notify($error);
    392             }
     610            $error->setSeverity('error');
     611            $this->notify($error);
    393612        }
    394613
     
    400619    }
    401620
    402     // Batches up errors into notifications for later sending
    403     public function notify($error, $metaData=array())
     621    /**
     622     * Batches up errors into notifications for later sending.
     623     *
     624     * @param Bugsnag_Error $error    the error to batch up
     625     * @param array         $metaData optional meta data to send with the error
     626     *
     627     * @return void
     628     */
     629    public function notify(Bugsnag_Error $error, $metaData = array())
    404630    {
    405631        // Queue or send the error
     
    413639            $this->notification->addError($error, $metaData);
    414640        } else {
    415             // Create and deliver notification immediatelt
     641            // Create and deliver notification immediately
    416642            $notif = new Bugsnag_Notification($this->config);
    417643            $notif->addError($error, $metaData);
     
    420646    }
    421647
    422     // Should we send errors immediately or on shutdown
     648    /**
     649     * Should we send errors immediately, or on shutdown?
     650     *
     651     * @return bool
     652     */
    423653    private function sendErrorsOnShutdown()
    424654    {
  • bugsnag/trunk/bugsnag-php/Configuration.php

    r871602 r1542350  
    33class Bugsnag_Configuration
    44{
     5    public static $DEFAULT_TIMEOUT = 10;
     6    public static $DEFAULT_ENDPOINT = 'https://notify.bugsnag.com';
     7    public static $DEFAULT_NON_SSL_ENDPOINT = 'http://notify.bugsnag.com';
     8
    59    public $apiKey;
    610    public $autoNotify = true;
    711    public $batchSending = true;
    812    public $useSSL = true;
    9     public $endpoint = 'notify.bugsnag.com';
    10     public $timeout = 2;
     13    public $endpoint;
    1114    public $notifyReleaseStages;
    1215    public $filters = array('password');
     
    1518    public $proxySettings = array();
    1619    public $notifier = array(
    17         'name'    => 'Bugsnag PHP (Official)',
    18         'version' => '2.2.0',
    19         'url'     => 'https://bugsnag.com'
     20        'name' => 'Bugsnag PHP (Official)',
     21        'version' => '2.9.2',
     22        'url' => 'https://bugsnag.com',
    2023    );
     24    public $sendEnvironment = false;
     25    public $sendCookies = true;
     26    public $sendSession = true;
     27    public $sendCode = true;
     28    public $stripPath;
     29    public $stripPathRegex;
    2130
    2231    public $context;
     
    3140    public $errorReportingLevel;
    3241
     42    public $curlOptions = array();
     43
     44    public $debug = false;
     45
     46    /**
     47     * Create a new config instance.
     48     *
     49     * @return void
     50     */
     51    public function __construct()
     52    {
     53        $this->timeout = self::$DEFAULT_TIMEOUT;
     54    }
     55
     56    /**
     57     * Get the notify endpoint.
     58     *
     59     * @return string
     60     */
    3361    public function getNotifyEndpoint()
    3462    {
    35         return $this->getProtocol()."://".$this->endpoint;
     63        if (is_null($this->endpoint)) {
     64            return $this->useSSL ? self::$DEFAULT_ENDPOINT : self::$DEFAULT_NON_SSL_ENDPOINT;
     65        } elseif (preg_match('/^(http:\/\/|https:\/\/)/', $this->endpoint)) {
     66            return $this->endpoint;
     67        } else {
     68            return ($this->useSSL ? 'https' : 'http').'://'.$this->endpoint;
     69        }
    3670    }
    3771
     72    /**
     73     * Should we notify?
     74     *
     75     * @return bool
     76     */
    3877    public function shouldNotify()
    3978    {
     
    4180    }
    4281
     82    /**
     83     * Should we ignore the given error code?
     84     *
     85     * @param int $code the error code
     86     *
     87     * @return bool
     88     */
     89    public function shouldIgnoreErrorCode($code)
     90    {
     91        if (isset($this->errorReportingLevel)) {
     92            return !($this->errorReportingLevel & $code);
     93        } else {
     94            return !(error_reporting() & $code);
     95        }
     96    }
     97
     98    /**
     99     * Set the project root.
     100     *
     101     * @param string $projectRoot the project root path
     102     *
     103     * @return void
     104     */
    43105    public function setProjectRoot($projectRoot)
    44106    {
    45107        $this->projectRoot = $projectRoot;
    46         $this->projectRootRegex = '/'.preg_quote($projectRoot, '/')."[\\/]?/i";
     108        $this->projectRootRegex = '/'.preg_quote($projectRoot, '/').'[\\/]?/i';
     109        if (is_null($this->stripPath)) {
     110            $this->setStripPath($projectRoot);
     111        }
    47112    }
    48113
    49     public function get($prop, $default=NULL)
     114    /**
     115     * Set the strip path.
     116     *
     117     * @param string $stripPath the absolute strip path
     118     *
     119     * @return void
     120     */
     121    public function setStripPath($stripPath)
     122    {
     123        $this->stripPath = $stripPath;
     124        $this->stripPathRegex = '/'.preg_quote($stripPath, '/').'[\\/]?/i';
     125    }
     126
     127    /**
     128     * Get the given configuration.
     129     *
     130     * @param string $prop    the property to get
     131     * @param mixed  $default the value to fallback to
     132     *
     133     * @return mixed
     134     */
     135    public function get($prop, $default = null)
    50136    {
    51137        $configured = $this->$prop;
     
    57143        }
    58144    }
    59 
    60     private function getProtocol()
    61     {
    62         return $this->useSSL ? "https" : "http";
    63     }
    64145}
  • bugsnag/trunk/bugsnag-php/Diagnostics.php

    r871602 r1542350  
    33class Bugsnag_Diagnostics
    44{
     5    /**
     6     * The config instance.
     7     *
     8     * @var Bugsnag_Configuration
     9     */
    510    private $config;
    611
     12    /**
     13     * Create a new diagnostics instance.
     14     *
     15     * @param Bugsnag_Configuration $config the configuration instance
     16     *
     17     * @return void
     18     */
    719    public function __construct(Bugsnag_Configuration $config)
    820    {
     
    1022    }
    1123
     24    /**
     25     * Get the application information.
     26     *
     27     * @return array
     28     */
    1229    public function getAppData()
    1330    {
     
    2946    }
    3047
     48    /**
     49     * Get the device information.
     50     *
     51     * @return array
     52     */
    3153    public function getDeviceData()
    3254    {
    3355        return array(
    34             'hostname' => $this->config->get('hostname', php_uname('n'))
     56            'hostname' => $this->config->get('hostname', php_uname('n')),
    3557        );
    3658    }
    3759
     60    /**
     61     * Get the error context.
     62     *
     63     * @return array
     64     */
    3865    public function getContext()
    3966    {
     
    4168    }
    4269
     70    /**
     71     * Get the current user.
     72     *
     73     * @return array
     74     */
    4375    public function getUser()
    4476    {
  • bugsnag/trunk/bugsnag-php/Error.php

    r871602 r1542350  
    44{
    55    private static $VALID_SEVERITIES = array(
    6         'fatal',
    76        'error',
    87        'warning',
    9         'info'
     8        'info',
    109    );
    1110
    1211    public $name;
     12    public $payloadVersion = '2';
    1313    public $message;
    14     public $severity = "error";
     14    public $severity = 'warning';
     15    /** @var Bugsnag_Stacktrace */
    1516    public $stacktrace;
    1617    public $metaData = array();
     18    public $user;
    1719    public $config;
    1820    public $diagnostics;
    19     public $code;
     21    /** @var Bugsnag_Error|null */
    2022    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);
     23    public $groupingHash;
     24
     25    /**
     26     * Create a new error from a PHP error.
     27     *
     28     * @param Bugsnag_Configuration $config      the config instance
     29     * @param Bugsnag_Diagnostics   $diagnostics the diagnostics instance
     30     * @param int                   $code        the error code
     31     * @param string                $message     the error message
     32     * @param string                $file        the error file
     33     * @param int                   $line        the error line
     34     * @param bool                  $fatal       if the error was fatal
     35     *
     36     * @return self
     37     */
     38    public static function fromPHPError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $code, $message, $file, $line, $fatal = false)
     39    {
     40        $error = new self($config, $diagnostics);
    2641        $error->setPHPError($code, $message, $file, $line, $fatal);
    2742
     
    2944    }
    3045
    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);
     46    /**
     47     * Create a new error from a PHP throwable.
     48     *
     49     * @param Bugsnag_Configuration $config      the config instance
     50     * @param Bugsnag_Diagnostics   $diagnostics the diagnostics instance
     51     * @param Throwable             $throwable   te he throwable instance
     52     *
     53     * @return self
     54     */
     55    public static function fromPHPThrowable(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $throwable)
     56    {
     57        $error = new self($config, $diagnostics);
     58        $error->setPHPThrowable($throwable);
    3559
    3660        return $error;
    3761    }
    3862
    39     public static function fromNamedError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $name, $message=NULL)
    40     {
    41         $error = new Bugsnag_Error($config, $diagnostics);
     63    /**
     64     * Create a new error from a named error.
     65     *
     66     * @param Bugsnag_Configuration $config      the config instance
     67     * @param Bugsnag_Diagnostics   $diagnostics the diagnostics instance
     68     * @param string                $name        the error name
     69     * @param string|null           $message     the error message
     70     *
     71     * @return self
     72     */
     73    public static function fromNamedError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $name, $message = null)
     74    {
     75        $error = new self($config, $diagnostics);
    4276        $error->setName($name)
    4377              ->setMessage($message)
     
    4781    }
    4882
    49     // Private constructor (for use only by the static methods above)
     83    /**
     84     * Create a new error instance.
     85     *
     86     * This is only for for use only by the static methods above.
     87     *
     88     * @param Bugsnag_Configuration $config      the config instance
     89     * @param Bugsnag_Diagnostics   $diagnostics the diagnostics instance
     90     *
     91     * @return void
     92     */
    5093    private function __construct(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics)
    5194    {
     
    5497    }
    5598
     99    /**
     100     * Set the error name.
     101     *
     102     * @param string $name the error name
     103     *
     104     * @throws InvalidArgumentException
     105     *
     106     * @return $this
     107     */
    56108    public function setName($name)
    57109    {
    58         $this->name = $name;
    59 
    60         return $this;
    61     }
    62 
     110        if (is_scalar($name) || method_exists($name, '__toString')) {
     111            $this->name = (string) $name;
     112        } else {
     113            throw new InvalidArgumentException('Name must be a string.');
     114        }
     115
     116        return $this;
     117    }
     118
     119    /**
     120     * Set the error message.
     121     *
     122     * @param string|null $message the error message
     123     *
     124     * @throws InvalidArgumentException
     125     *
     126     * @return $this
     127     */
    63128    public function setMessage($message)
    64129    {
    65         $this->message = $message;
    66 
    67         return $this;
    68     }
    69 
    70     public function setStacktrace($stacktrace)
     130        if ($message === null) {
     131            $this->message = null;
     132        } elseif (is_scalar($message) || method_exists($message, '__toString')) {
     133            $this->message = (string) $message;
     134        } else {
     135            throw new InvalidArgumentException('Message must be a string.');
     136        }
     137
     138        return $this;
     139    }
     140
     141    /**
     142     * Set the grouping hash.
     143     *
     144     * @param string $groupingHash the grouping hash
     145     *
     146     * @return $this
     147     */
     148    public function setGroupingHash($groupingHash)
     149    {
     150        $this->groupingHash = $groupingHash;
     151
     152        return $this;
     153    }
     154
     155    /**
     156     * Set the bugsnag stacktrace.
     157     *
     158     * @param Bugsnag_Stacktrace $stacktrace the stacktrace instance
     159     *
     160     * @return $this
     161     */
     162    public function setStacktrace(Bugsnag_Stacktrace $stacktrace)
    71163    {
    72164        $this->stacktrace = $stacktrace;
     
    75167    }
    76168
    77     public function setCode($code)
    78     {
    79         $this->code = $code;
    80 
    81         return $this;
    82     }
    83 
     169    /**
     170     * Set the error severity.
     171     *
     172     * @param int|null $severity the error severity
     173     *
     174     * @return $this
     175     */
    84176    public function setSeverity($severity)
    85177    {
    86178        if (!is_null($severity)) {
    87             if (in_array($severity, Bugsnag_Error::$VALID_SEVERITIES)) {
     179            if (in_array($severity, self::$VALID_SEVERITIES)) {
    88180                $this->severity = $severity;
    89181            } else {
    90                 error_log('Bugsnag Warning: Tried to set error severity to '. $severity .' which is not allowed.');
     182                error_log('Bugsnag Warning: Tried to set error severity to '.$severity.' which is not allowed.');
    91183            }
    92184        }
     
    95187    }
    96188
    97     public function setPHPException(Exception $exception)
    98     {
     189    /**
     190     * Set the PHP exception.
     191     *
     192     * @param Throwable $exception the throwable instance
     193     *
     194     * @return $this
     195     *
     196     * @deprecated since version 2.9. Use setPHPThrowable instead.
     197     */
     198    public function setPHPException($exception)
     199    {
     200        return $this->setPHPThrowable($exception);
     201    }
     202
     203    /**
     204     * Set the PHP throwable.
     205     *
     206     * @param Throwable $exception the throwable instance
     207     *
     208     * @return $this
     209     */
     210    public function setPHPThrowable($exception)
     211    {
     212        if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
     213            if (!$exception instanceof Throwable) {
     214                error_log('Bugsnag Warning: The exception must implement Throwable.');
     215
     216                return $this;
     217            }
     218        } else {
     219            if (!$exception instanceof Exception) {
     220                error_log('Bugsnag Warning: The exception must be an Exception.');
     221
     222                return $this;
     223            }
     224        }
     225
    99226        $this->setName(get_class($exception))
    100227             ->setMessage($exception->getMessage())
     
    108235    }
    109236
    110     public function setPHPError($code, $message, $file, $line, $fatal=false)
     237    /**
     238     * Set the PHP error.
     239     *
     240     * @param int    $code     the error code
     241     * @param string $message  the error message
     242     * @param string $file     the error file
     243     * @param int    $line     the error line
     244     * @param bool   $fatal    if the error was fatal
     245     *
     246     * @return $this
     247     */
     248    public function setPHPError($code, $message, $file, $line, $fatal = false)
    111249    {
    112250        if ($fatal) {
     
    125263             ->setMessage($message)
    126264             ->setSeverity(Bugsnag_ErrorTypes::getSeverity($code))
    127              ->setStacktrace($stacktrace)
    128              ->setCode($code);
    129 
    130         return $this;
    131     }
    132 
     265             ->setStacktrace($stacktrace);
     266
     267        return $this;
     268    }
     269
     270    /**
     271     * Set the error meta data.
     272     *
     273     * @param array $metaData the error meta data
     274     *
     275     * @return $this
     276     */
    133277    public function setMetaData($metaData)
    134278    {
     
    140284    }
    141285
     286    /**
     287     * Set the current user.
     288     *
     289     * @param array|null $user the current user
     290     *
     291     * @return $this
     292     */
     293    public function setUser($user)
     294    {
     295        $this->user = $user;
     296
     297        return $this;
     298    }
     299
     300    /**
     301     * Set the previous throwable.
     302     *
     303     * @param Throwable $exception the previous throwable
     304     *
     305     * @return $this
     306     */
    142307    public function setPrevious($exception)
    143308    {
    144309        if ($exception) {
    145             $this->previous = Bugsnag_Error::fromPHPException($this->config, $this->diagnostics, $exception);
    146         }
    147 
    148         return $this;
    149     }
    150 
    151     public function shouldIgnore()
    152     {
    153         // Check if we should ignore errors of this type
    154         if (isset($this->code)) {
    155             if (isset($this->config->errorReportingLevel)) {
    156                 return !($this->config->errorReportingLevel & $this->code);
    157             } else {
    158                 return !(error_reporting() & $this->code);
    159             }
    160         }
    161 
    162         return false;
    163     }
    164 
     310            $this->previous = self::fromPHPThrowable($this->config, $this->diagnostics, $exception);
     311        }
     312
     313        return $this;
     314    }
     315
     316    /**
     317     * Get the array representation.
     318     *
     319     * @return array
     320     */
    165321    public function toArray()
    166322    {
    167         return array(
     323        $errorArray = array(
    168324            'app' => $this->diagnostics->getAppData(),
    169325            'device' => $this->diagnostics->getDeviceData(),
    170             'user' => $this->diagnostics->getUser(),
     326            'user' => is_null($this->user) ? $this->diagnostics->getUser() : $this->user,
    171327            'context' => $this->diagnostics->getContext(),
     328            'payloadVersion' => $this->payloadVersion,
    172329            'severity' => $this->severity,
    173330            'exceptions' => $this->exceptionArray(),
    174             'metaData' => $this->applyFilters($this->metaData)
     331            'metaData' => $this->cleanupObj($this->metaData, true),
    175332        );
    176     }
    177 
     333
     334        if (isset($this->groupingHash)) {
     335            $errorArray['groupingHash'] = $this->groupingHash;
     336        }
     337
     338        return $errorArray;
     339    }
     340
     341    /**
     342     * Get the exception array.
     343     *
     344     * @return array
     345     */
    178346    public function exceptionArray()
    179347    {
     
    187355            'errorClass' => $this->name,
    188356            'message' => $this->message,
    189             'stacktrace' => $this->stacktrace->toArray()
     357            'stacktrace' => $this->stacktrace->toArray(),
    190358        );
    191359
    192         return $exceptionArray;
    193     }
    194 
    195     private function applyFilters($metaData)
    196     {
    197         if (!empty($this->config->filters)) {
    198             $cleanMetaData = array();
    199 
    200             foreach ($metaData as $key => $value) {
     360        return $this->cleanupObj($exceptionArray, false);
     361    }
     362
     363    /**
     364     * Cleanup the given object.
     365     *
     366     * @param mixed $obj        the data to cleanup
     367     * @param bool  $isMetaData if it is meta data
     368     *
     369     * @return array|null
     370     */
     371    private function cleanupObj($obj, $isMetaData)
     372    {
     373        if (is_null($obj)) {
     374            return;
     375        }
     376
     377        if (is_array($obj)) {
     378            $cleanArray = array();
     379            foreach ($obj as $key => $value) {
     380                // Check if this key should be filtered
    201381                $shouldFilter = false;
    202                 foreach ($this->config->filters as $filter) {
    203                     if (strpos($key, $filter) !== false) {
    204                         $shouldFilter = true;
    205                         break;
     382
     383                // Apply filters to metadata if required
     384                if ($isMetaData && is_array($this->config->filters)) {
     385                    foreach ($this->config->filters as $filter) {
     386                        if (strpos($key, $filter) !== false) {
     387                            $shouldFilter = true;
     388                            break;
     389                        }
    206390                    }
    207391                }
    208 
     392                // Apply filter
    209393                if ($shouldFilter) {
    210                     $cleanMetaData[$key] = '[FILTERED]';
     394                    $cleanArray[$key] = '[FILTERED]';
    211395                } else {
    212                     if (is_array($value)) {
    213                         $cleanMetaData[$key] = $this->applyFilters($value);
    214                     } else {
    215                         $cleanMetaData[$key] = $value;
    216                     }
     396                    $cleanArray[$key] = $this->cleanupObj($value, $isMetaData);
    217397                }
    218398            }
    219399
    220             return $cleanMetaData;
    221         } else {
    222             return $metaData;
     400            return $cleanArray;
     401        } elseif (is_string($obj)) {
     402            // UTF8-encode if not already encoded
     403            if (function_exists('mb_detect_encoding') && !mb_detect_encoding($obj, 'UTF-8', true)) {
     404                return utf8_encode($obj);
     405            } else {
     406                return $obj;
     407            }
     408        } elseif (is_object($obj)) {
     409            // json_encode -> json_decode trick turns an object into an array
     410            return $this->cleanupObj(json_decode(json_encode($obj), true), $isMetaData);
     411        } else {
     412            return $obj;
    223413        }
    224414    }
  • bugsnag/trunk/bugsnag-php/ErrorTypes.php

    r871602 r1542350  
    66        E_ERROR => array(
    77            'name' => 'PHP Fatal Error',
    8             'severity' => 'fatal'
     8            'severity' => 'error',
    99        ),
    1010
    1111        E_WARNING => array(
    1212            'name' => 'PHP Warning',
    13             'severity' => 'warning'
     13            'severity' => 'warning',
    1414        ),
    1515
    1616        E_PARSE => array(
    1717            'name' => 'PHP Parse Error',
    18             'severity' => 'fatal'
     18            'severity' => 'error',
    1919        ),
    2020
    2121        E_NOTICE => array(
    2222            'name' => 'PHP Notice',
    23             'severity' => 'info'
     23            'severity' => 'info',
    2424        ),
    2525
    2626        E_CORE_ERROR => array(
    2727            'name' => 'PHP Core Error',
    28             'severity' => 'fatal'
     28            'severity' => 'error',
    2929        ),
    3030
    3131        E_CORE_WARNING => array(
    3232            'name' => 'PHP Core Warning',
    33             'severity' => 'warning'
     33            'severity' => 'warning',
    3434        ),
    3535
    3636        E_COMPILE_ERROR => array(
    3737            'name' => 'PHP Compile Error',
    38             'severity' => 'fatal'
     38            'severity' => 'error',
    3939        ),
    4040
    4141        E_COMPILE_WARNING => array(
    4242            'name' => 'PHP Compile Warning',
    43             'severity' => 'warning'
     43            'severity' => 'warning',
    4444        ),
    4545
    4646        E_USER_ERROR => array(
    4747            'name' => 'User Error',
    48             'severity' => 'error'
     48            'severity' => 'error',
    4949        ),
    5050
    5151        E_USER_WARNING => array(
    5252            'name' => 'User Warning',
    53             'severity' => 'warning'
     53            'severity' => 'warning',
    5454        ),
    5555
    5656        E_USER_NOTICE => array(
    5757            'name' => 'User Notice',
    58             'severity' => 'info'
     58            'severity' => 'info',
    5959        ),
    6060
    6161        E_STRICT => array(
    6262            'name' => 'PHP Strict',
    63             'severity' => 'info'
     63            'severity' => 'info',
    6464        ),
    6565
    6666        E_RECOVERABLE_ERROR => array(
    6767            'name' => 'PHP Recoverable Error',
    68             'severity' => 'error'
     68            'severity' => 'error',
    6969        ),
    7070
     
    7272        8192 => array(
    7373            'name' => 'PHP Deprecated',
    74             'severity' => 'info'
     74            'severity' => 'info',
    7575        ),
    7676
     
    7878        16384 => array(
    7979            'name' => 'User Deprecated',
    80             'severity' => 'info'
    81         )
     80            'severity' => 'info',
     81        ),
    8282    );
    8383
     84    /**
     85     * Is the given error code fatal?
     86     *
     87     * @param int $code the error code
     88     *
     89     * @return bool
     90     */
    8491    public static function isFatal($code)
    8592    {
    86         return self::getSeverity($code) == 'fatal';
     93        return self::getSeverity($code) == 'error';
    8794    }
    8895
     96    /**
     97     * Get the name of the given error code.
     98     *
     99     * @param int $code the error code
     100     *
     101     * @return string
     102     */
    89103    public static function getName($code)
    90104    {
     
    92106            return self::$ERROR_TYPES[$code]['name'];
    93107        } else {
    94             return "Unknown";
     108            return 'Unknown';
    95109        }
    96110    }
    97111
     112    /**
     113     * Get the severity of the given error code.
     114     *
     115     * @param int $code the error code
     116     *
     117     * @return string
     118     */
    98119    public static function getSeverity($code)
    99120    {
     
    101122            return self::$ERROR_TYPES[$code]['severity'];
    102123        } else {
    103             return "error";
     124            return 'error';
    104125        }
    105126    }
    106127
     128    /**
     129     * Get the the levels for the given severity.
     130     *
     131     * @param string $severity the given severity
     132     *
     133     * @return int
     134     */
    107135    public static function getLevelsForSeverity($severity)
    108136    {
    109137        $levels = 0;
    110         foreach (Bugsnag_ErrorTypes::$ERROR_TYPES as $level => $info) {
     138        foreach (self::$ERROR_TYPES as $level => $info) {
    111139            if ($info['severity'] == $severity) {
    112140                $levels |= $level;
  • bugsnag/trunk/bugsnag-php/Notification.php

    r871602 r1542350  
    33class Bugsnag_Notification
    44{
     5    private static $CONTENT_TYPE_HEADER = 'Content-type: application/json';
     6
     7    /**
     8     * The config instance.
     9     *
     10     * @var Bugsnag_Configuration
     11     */
    512    private $config;
     13
     14    /**
     15     * The queue of errors to send to Bugsnag.
     16     *
     17     * @var Bugsnag_Error[]
     18     */
    619    private $errorQueue = array();
    720
     21    /**
     22     * Create a new notification instance.
     23     *
     24     * @param Bugsnag_Configuration $config the configuration instance
     25     *
     26     * @return void
     27     */
    828    public function __construct(Bugsnag_Configuration $config)
    929    {
     
    1131    }
    1232
    13     public function addError($error, $passedMetaData=array())
     33    /**
     34     * Add an error to the queue.
     35     *
     36     * @param Bugsnag_Error $config         the bugsnag error instance
     37     * @param array         $passedMetaData the associated meta data
     38     *
     39     * @return bool
     40     */
     41    public function addError(Bugsnag_Error $error, $passedMetaData = array())
    1442    {
    1543        // Check if this error should be sent to Bugsnag
    1644        if (!$this->config->shouldNotify()) {
    17             return FALSE;
     45            return false;
    1846        }
    1947
     
    2654        }
    2755
     56        // Session Tab
     57        if ($this->config->sendSession && !empty($_SESSION)) {
     58            $error->setMetaData(array('session' => $_SESSION));
     59        }
     60
     61        // Cookies Tab
     62        if ($this->config->sendCookies && !empty($_COOKIE)) {
     63            $error->setMetaData(array('cookies' => $_COOKIE));
     64        }
     65
    2866        // Add environment meta-data to error
    29         if (!empty($_ENV)) {
    30             $error->setMetaData(array("Environment" => $_ENV));
     67        if ($this->config->sendEnvironment && !empty($_ENV)) {
     68            $error->setMetaData(array('Environment' => $_ENV));
    3169        }
    3270
     
    4078
    4179        // Skip this error if the beforeNotify function returned FALSE
    42         if (!isset($beforeNotifyReturn) || $beforeNotifyReturn !== FALSE) {
     80        if (!isset($beforeNotifyReturn) || $beforeNotifyReturn !== false) {
    4381            $this->errorQueue[] = $error;
    4482
    45             return TRUE;
     83            return true;
    4684        } else {
    47             return FALSE;
    48         }
    49     }
    50 
     85            return false;
     86        }
     87    }
     88
     89    /**
     90     * Get the array representation.
     91     *
     92     * @return array
     93     */
    5194    public function toArray()
    5295    {
     
    63106            'apiKey' => $this->config->apiKey,
    64107            'notifier' => $this->config->notifier,
    65             'events' => $events
     108            'events' => $events,
    66109        );
    67110    }
    68111
     112    /**
     113     * Deliver everything on the queue to Bugsnag.
     114     *
     115     * @return void
     116     */
    69117    public function deliver()
    70118    {
    71         if (!empty($this->errorQueue)) {
    72             // Post the request to bugsnag
    73             $this->postJSON($this->config->getNotifyEndpoint(), $this->toArray());
    74 
    75             // Clear the error queue
    76             $this->errorQueue = array();
    77         }
    78     }
    79 
     119        if (empty($this->errorQueue)) {
     120            return;
     121        }
     122
     123        // Post the request to bugsnag
     124        $this->postJSON($this->config->getNotifyEndpoint(), $this->toArray());
     125
     126        // Clear the error queue
     127        $this->errorQueue = array();
     128    }
     129
     130    /**
     131     * Post the given data to Bugsnag in json form.
     132     *
     133     * @param string $url  the url to hit
     134     * @param array  $data the data send
     135     *
     136     * @return void
     137     */
    80138    public function postJSON($url, $data)
     139    {
     140        // Try to send the whole lot, or without the meta data for the first
     141        // event. If failed, try to send the first event, and then the rest of
     142        // them, revursively. Decrease by a constant and concquer if you like.
     143        // Note that the base case is satisfied as soon as the payload is small
     144        // enought to send, or when it's simply discarded.
     145        try {
     146            $body = $this->encode($data);
     147        } catch (RuntimeException $e) {
     148            if (count($data['events']) > 1) {
     149                $event = array_shift($data['events']);
     150                $this->postJSON($url, array_merge($data, array('events' => array($event))));
     151                $this->postJSON($url, $data);
     152            } else {
     153                error_log('Bugsnag Warning: '.$e->getMessage());
     154            }
     155
     156            return;
     157        }
     158
     159        // Prefer cURL if it is installed, otherwise fall back to fopen()
     160        // cURL supports both timeouts and proxies
     161
     162        try {
     163            if (function_exists('curl_version')) {
     164                $this->postWithCurl($url, $body);
     165            } elseif (ini_get('allow_url_fopen')) {
     166                $this->postWithFopen($url, $body);
     167            } else {
     168                error_log('Bugsnag Warning: Couldn\'t notify (neither cURL or allow_url_fopen are available on your PHP installation)');
     169            }
     170        } catch (Exception $e) {
     171            error_log('Bugsnag Warning: Couldn\'t notify. '.$e->getMessage());
     172        }
     173    }
     174
     175    /**
     176     * Json encode the given data.
     177     *
     178     * We will also strip out the meta data if it's too large.
     179     *
     180     * @param array $data the data to encode
     181     *
     182     * @throws RuntimeException
     183     *
     184     * @return string
     185     */
     186    private function encode(array $data)
     187    {
     188        $body = json_encode($data);
     189
     190        if ($this->length($body) > 500000) {
     191            unset($data['events'][0]['metaData']);
     192        }
     193
     194        $body = json_encode($data);
     195
     196        if ($this->length($body) > 500000) {
     197            throw new RuntimeException('Payload too large');
     198        }
     199
     200        return $body;
     201    }
     202
     203    /**
     204     * Get the length of the given string in bytes.
     205     *
     206     * @param string $str the string to get the length of
     207     *
     208     * @return int
     209     */
     210    private function length($str)
     211    {
     212        return function_exists('mb_strlen') ? mb_strlen($str, '8bit') : strlen($str);
     213    }
     214
     215    /**
     216     * Post the given info to Bugsnag using cURL.
     217     *
     218     * @param string $url  the url to hit
     219     * @param string $body the request body
     220     *
     221     * @return void
     222     */
     223    private function postWithCurl($url, $body)
    81224    {
    82225        $http = curl_init($url);
     
    86229        curl_setopt($http, CURLOPT_RETURNTRANSFER, true);
    87230        curl_setopt($http, CURLOPT_POST, true);
    88         curl_setopt($http, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
    89         curl_setopt($http, CURLOPT_POSTFIELDS, json_encode($data));
     231        curl_setopt($http, CURLOPT_HTTPHEADER, array(self::$CONTENT_TYPE_HEADER, 'Expect:'));
     232        curl_setopt($http, CURLOPT_POSTFIELDS, $body);
    90233        curl_setopt($http, CURLOPT_CONNECTTIMEOUT, $this->config->timeout);
    91234        curl_setopt($http, CURLOPT_SSL_VERIFYPEER, false);
    92235        curl_setopt($http, CURLOPT_VERBOSE, false);
    93 
     236        if (defined('HHVM_VERSION')) {
     237            curl_setopt($http, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
     238        } else {
     239            curl_setopt($http, CURL_IPRESOLVE_V4, true);
     240        }
     241
     242        if (!empty($this->config->curlOptions)) {
     243            foreach ($this->config->curlOptions as $option => $value) {
     244                curl_setopt($http, $option, $value);
     245            }
     246        }
    94247        // Apply proxy settings (if present)
    95248        if (count($this->config->proxySettings)) {
     
    101254            }
    102255            if (isset($this->config->proxySettings['user'])) {
    103                 $userPassword = $this->config->proxySettings['user'] . ':';
    104                 $userPassword .= isset($this->config->proxySettings['password'])? $this->config->proxySettings['password'] : '';
     256                $userPassword = $this->config->proxySettings['user'].':';
     257                $userPassword .= isset($this->config->proxySettings['password']) ? $this->config->proxySettings['password'] : '';
    105258                curl_setopt($http, CURLOPT_PROXYUSERPWD, $userPassword);
    106259            }
     
    113266        if ($statusCode > 200) {
    114267            error_log('Bugsnag Warning: Couldn\'t notify ('.$responseBody.')');
     268
     269            if ($this->config->debug) {
     270                error_log('Bugsnag Debug: Attempted to post to URL - "'.$url.'"');
     271                error_log('Bugsnag Debug: Attempted to post payload - "'.$body.'"');
     272            }
     273        }
     274
     275        if (curl_errno($http)) {
     276            error_log('Bugsnag Warning: Couldn\'t notify ('.curl_error($http).')');
    115277        }
    116278
    117279        curl_close($http);
    118 
    119         return $statusCode;
     280    }
     281
     282    /**
     283     * Post the given info to Bugsnag using fopen.
     284     *
     285     * @param string $url  the url to hit
     286     * @param string $body the request body
     287     *
     288     * @return void
     289     */
     290    private function postWithFopen($url, $body)
     291    {
     292        // Warn about lack of proxy support if we are using fopen()
     293        if (count($this->config->proxySettings)) {
     294            error_log('Bugsnag Warning: Can\'t use proxy settings unless cURL is installed');
     295        }
     296
     297        // Create the request context
     298        $context = stream_context_create(array(
     299            'http' => array(
     300                'method' => 'POST',
     301                'header' => self::$CONTENT_TYPE_HEADER.'\r\n',
     302                'content' => $body,
     303                'timeout' => $this->config->timeout,
     304            ),
     305            'ssl' => array(
     306                'verify_peer' => false,
     307            ),
     308        ));
     309
     310        // Execute the request and fetch the response
     311        if ($stream = fopen($url, 'rb', false, $context)) {
     312            $response = stream_get_contents($stream);
     313
     314            if (!$response) {
     315                error_log('Bugsnag Warning: Couldn\'t notify (no response)');
     316            }
     317        } else {
     318            error_log('Bugsnag Warning: Couldn\'t notify (fopen failed)');
     319        }
    120320    }
    121321}
  • bugsnag/trunk/bugsnag-php/Request.php

    r825757 r1542350  
    33class Bugsnag_Request
    44{
     5    /**
     6     * Are we currently processing a request?
     7     *
     8     * @return bool
     9     */
    510    public static function isRequest()
    611    {
     
    813    }
    914
     15    /**
     16     * Get the request formatted as meta data.
     17     *
     18     * @return array
     19     */
    1020    public static function getRequestMetaData()
    1121    {
     22        static $requestData;
     23
     24        if ($requestData !== null) {
     25            return $requestData;
     26        }
     27
    1228        $requestData = array();
     29
     30        $methodsWithPayload = array('PUT');
    1331
    1432        // Request Tab
     
    2240            $requestData['request']['params'] = $_POST;
    2341        } else {
     42            $input = file_get_contents('php://input');
     43
    2444            if (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'], 'application/json') === 0) {
    25                 $requestData['request']['params'] = json_decode(file_get_contents('php://input'));
     45                $requestData['request']['params'] = json_decode($input, true);
     46            }
     47
     48            if (isset($_SERVER['REQUEST_METHOD']) && in_array(strtoupper($_SERVER['REQUEST_METHOD']), $methodsWithPayload)) {
     49                parse_str($input, $params);
     50                if (isset($requestData['request']['params']) && is_array($requestData['request']['params'])) {
     51                    $requestData['request']['params'] = array_merge($requestData['request']['params'], $params);
     52                } else {
     53                    $requestData['request']['params'] = $params;
     54                }
    2655            }
    2756        }
    2857
    29         $requestData['request']['ip'] = self::getRequestIp();
     58        $requestData['request']['clientIp'] = self::getRequestIp();
    3059        if (isset($_SERVER['HTTP_USER_AGENT'])) {
    3160            $requestData['request']['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
    3261        }
    3362
    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;
     63        $headers = self::getRequestHeaders();
     64        if (!empty($headers)) {
     65            $requestData['request']['headers'] = $headers;
    4966        }
    5067
     
    5269    }
    5370
     71    /**
     72     * Get the request context.
     73     *
     74     * @return string|null
     75     */
    5476    public static function getContext()
    5577    {
    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;
     78        if (self::isRequest() && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER['REQUEST_URI'])) {
     79            return $_SERVER['REQUEST_METHOD'].' '.strtok($_SERVER['REQUEST_URI'], '?');
    6080        }
    6181    }
    6282
     83    /**
     84     * Get the request id.
     85     *
     86     * @return string|null
     87     */
    6388    public static function getUserId()
    6489    {
    6590        if (self::isRequest()) {
    6691            return self::getRequestIp();
    67         } else {
    68             return null;
    6992        }
    7093    }
    7194
     95    /**
     96     * Get the request url.
     97     *
     98     * @return string
     99     */
    72100    public static function getCurrentUrl()
    73101    {
    74         $schema = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://';
     102        $schema = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)) ? 'https://' : 'http://';
    75103
    76         return $schema.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
     104        $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
     105
     106        return $schema.$host.$_SERVER['REQUEST_URI'];
    77107    }
    78108
     109    /**
     110     * Get the request ip.
     111     *
     112     * @return string
     113     */
    79114    public static function getRequestIp()
    80115    {
    81116        return isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
    82117    }
     118
     119    /**
     120     * Get the request headers.
     121     *
     122     * @return array
     123     */
     124    public static function getRequestHeaders()
     125    {
     126        $headers = array();
     127
     128        foreach ($_SERVER as $name => $value) {
     129            if (substr($name, 0, 5) == 'HTTP_') {
     130                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
     131            }
     132        }
     133
     134        return $headers;
     135    }
    83136}
  • bugsnag/trunk/bugsnag-php/Stacktrace.php

    r871602 r1542350  
    33class Bugsnag_Stacktrace
    44{
    5     private $frames = array();
     5    private static $DEFAULT_NUM_LINES = 7;
     6    private static $MAX_LINE_LENGTH = 200;
     7
     8    public $frames = array();
    69    private $config;
    710
     11    /**
     12     * Generate a new stacktrace using the given config.
     13     *
     14     * @param Bugsnag_Configuration $config the configuration instance
     15     *
     16     * @return self
     17     */
    818    public static function generate($config)
    919    {
     
    1222            $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS & ~DEBUG_BACKTRACE_PROVIDE_OBJECT);
    1323        } elseif (version_compare(PHP_VERSION, '5.2.5') >= 0) {
    14             $backtrace = debug_backtrace(FALSE);
     24            $backtrace = debug_backtrace(false);
    1525        } else {
    1626            $backtrace = debug_backtrace();
    1727        }
    1828
    19         return self::fromBacktrace($config, $backtrace, "[generator]", 0);
    20     }
    21 
     29        return self::fromBacktrace($config, $backtrace, '[generator]', 0);
     30    }
     31
     32    /**
     33     * Create a new stacktrace instance from a frame.
     34     *
     35     * @param Bugsnag_Configuration $config the configuration instance
     36     * @param string                $file   the associated file
     37     * @param int                   $line   the line number
     38     *
     39     * @return self
     40     */
    2241    public static function fromFrame($config, $file, $line)
    2342    {
    24         $stacktrace = new Bugsnag_Stacktrace($config);
    25         $stacktrace->addFrame($file, $line, "[unknown]");
     43        $stacktrace = new self($config);
     44        $stacktrace->addFrame($file, $line, '[unknown]');
    2645
    2746        return $stacktrace;
    2847    }
    2948
     49    /**
     50     * Create a new stacktrace instance from a backtrace.
     51     *
     52     * @param Bugsnag_Configuration $config    the configuration instance
     53     * @param array                 $backtrace the associated backtrace
     54     * @param int                   $topFile   the top file to use
     55     * @param int                   $topLine   the top line to use
     56     *
     57     * @return self
     58     */
    3059    public static function fromBacktrace($config, $backtrace, $topFile, $topLine)
    3160    {
    32         $stacktrace = new Bugsnag_Stacktrace($config);
     61        $stacktrace = new self($config);
    3362
    3463        // PHP backtrace's are misaligned, we need to shift the file/line down a frame
    3564        foreach ($backtrace as $frame) {
    3665            if (!self::frameInsideBugsnag($frame)) {
    37                 $stacktrace->addFrame($topFile, $topLine, $frame['function'], isset($frame['class']) ? $frame['class'] : NULL);
     66                $stacktrace->addFrame(
     67                    $topFile,
     68                    $topLine,
     69                    isset($frame['function']) ? $frame['function'] : null,
     70                    isset($frame['class']) ? $frame['class'] : null
     71                );
    3872            }
    3973
     
    4276                $topLine = $frame['line'];
    4377            } else {
    44                 $topFile = "[internal]";
     78                $topFile = '[internal]';
    4579                $topLine = 0;
    4680            }
     
    5387    }
    5488
     89    /**
     90     * Does the given frame internally belong to bugsnag.
     91     *
     92     * @param array $frame the given frame to check
     93     *
     94     * @return bool
     95     */
    5596    public static function frameInsideBugsnag($frame)
    5697    {
     
    5899    }
    59100
    60 
     101    /**
     102     * Create a new stacktrace instance.
     103     *
     104     * @param Bugsnag_Configuration $config the configuration instance
     105     *
     106     * @return void
     107     */
    61108    public function __construct($config)
    62109    {
     
    64111    }
    65112
     113    /**
     114     * Get the array representation.
     115     *
     116     * @return array
     117     */
    66118    public function toArray()
    67119    {
     
    69121    }
    70122
    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 ($inProject) {
    78             $file = preg_replace($this->config->projectRootRegex, '', $file);
     123    /**
     124     * Add the given frame to the stacktrace.
     125     *
     126     * @param string      $file   the associated file
     127     * @param int         $line   the line number
     128     * @param string      $method the method called
     129     * @param string|null $class the associated class
     130     *
     131     * @return void
     132     */
     133    public function addFrame($file, $line, $method, $class = null)
     134    {
     135        // Account for special "filenames" in eval'd code
     136        $matches = array();
     137        if (preg_match("/^(.*?)\((\d+)\) : (?:eval\(\)'d code|runtime-created function)$/", $file, $matches)) {
     138            $file = $matches[1];
     139            $line = $matches[2];
    79140        }
    80141
    81142        // Construct the frame
    82143        $frame = array(
    83             'file' => $file,
    84             'lineNumber' => $line,
    85             'method' => $method,
    86             'inProject' => $inProject
     144            'lineNumber' => (int) $line,
     145            'method' => $class ? "$class::$method" : $method,
    87146        );
    88147
    89         if (!empty($class)) {
    90             $frame['class'] = $class;
     148        // Attach some lines of code for context
     149        if ($this->config->sendCode) {
     150            $frame['code'] = $this->getCode($file, $line, self::$DEFAULT_NUM_LINES);
     151        }
     152
     153        // Check if this frame is inProject
     154        $frame['inProject'] = !is_null($this->config->projectRootRegex) && preg_match($this->config->projectRootRegex, $file);
     155
     156        // Strip out projectRoot from start of file path
     157        if (is_null($this->config->stripPathRegex)) {
     158            $frame['file'] = $file;
     159        } else {
     160            $frame['file'] = preg_replace($this->config->stripPathRegex, '', $file);
    91161        }
    92162
    93163        $this->frames[] = $frame;
    94164    }
     165
     166    /**
     167     * Extract the code for the given file and lines.
     168     *
     169     * @param string $path     the path to the file
     170     * @param int    $line     the line to centre about
     171     * @param string $numLines the number of lines to fetch
     172     *
     173     * @return string[]|null
     174     */
     175    private function getCode($path, $line, $numLines)
     176    {
     177        if (empty($path) || empty($line) || !file_exists($path)) {
     178            return;
     179        }
     180
     181        try {
     182            // Get the number of lines in the file
     183            $file = new SplFileObject($path);
     184            $file->seek(PHP_INT_MAX);
     185            $totalLines = $file->key() + 1;
     186
     187            // Work out which lines we should fetch
     188            $start = max($line - floor($numLines / 2), 1);
     189            $end = $start + ($numLines - 1);
     190            if ($end > $totalLines) {
     191                $end = $totalLines;
     192                $start = max($end - ($numLines - 1), 1);
     193            }
     194
     195            // Get the code for this range
     196            $code = array();
     197
     198            $file->seek($start - 1);
     199            while ($file->key() < $end) {
     200                $code[$file->key() + 1] = rtrim(substr($file->current(), 0, self::$MAX_LINE_LENGTH));
     201                $file->next();
     202            }
     203
     204            return $code;
     205        } catch (RuntimeException $ex) {
     206            return;
     207        }
     208    }
    95209}
  • bugsnag/trunk/bugsnag.php

    r904344 r1542350  
    44Plugin URI: https://bugsnag.com
    55Description: Bugsnag monitors for errors and crashes on your wordpress site, sends them to your bugsnag.com dashboard, and notifies you by email of each error.
    6 Version: 1.1.2
     6Version: 1.3.0
    77Author: Bugsnag Inc.
    88Author URI: https://bugsnag.com
     
    1818    private static $NOTIFIER = array(
    1919        'name' => 'Bugsnag Wordpress (Official)',
    20         'version' => '1.1.2',
     20        'version' => '1.3.0',
    2121        'url' => 'https://bugsnag.com/notifiers/wordpress'
    2222    );
     
    2626    private $notifySeverities;
    2727    private $filterFields;
     28    private $pluginBase;
    2829
    2930    public function __construct()
     
    3233        $this->activateBugsnag();
    3334
     35        $this->pluginBase = 'bugsnag/bugsnag.php';
     36
    3437        // Run init actions (loading wp user)
    3538        add_action('init', array($this, 'initActions'));
     
    3841        add_action('admin_menu', array($this, 'adminMenuActions'));
    3942
     43        // Load network admin menu if using multisite
     44        add_action('network_admin_menu', array($this, 'networkAdminMenuActions'));
     45
    4046        add_action('wp_ajax_test_bugsnag', array($this, 'testBugsnag'));
    4147    }
     
    4349    private function activateBugsnag()
    4450    {
    45         // Require bugsnag-php
    46         if(file_exists($this->relativePath(self::$COMPOSER_AUTOLOADER))) {
    47             require_once $this->relativePath(self::$COMPOSER_AUTOLOADER);
    48         } elseif (file_exists($this->relativePath(self::$PACKAGED_AUTOLOADER))) {
    49             require_once $this->relativePath(self::$PACKAGED_AUTOLOADER);
    50         } else {
     51        $is_load_success = $this->requireBugsnagPhp();
     52        if (!$is_load_success) {
    5153            error_log("Bugsnag Error: Couldn't activate Bugsnag Error Monitoring due to missing Bugsnag library!");
    5254            return;
     
    5456
    5557        // Load bugsnag settings
    56         $this->apiKey = get_option('bugsnag_api_key');
    57         $this->notifySeverities = get_option('bugsnag_notify_severities');
    58         $this->filterFields = get_option('bugsnag_filterfields');
     58        if (!get_site_option('bugsnag_network')) {
     59            // Regular
     60            $this->apiKey           = get_option( 'bugsnag_api_key' );
     61            $this->notifySeverities = get_option( 'bugsnag_notify_severities' );
     62            $this->filterFields     = get_option( 'bugsnag_filterfields' );
     63        } else {
     64            // Multisite
     65            $this->apiKey           = get_site_option( 'bugsnag_api_key' );
     66            $this->notifySeverities = get_site_option( 'bugsnag_notify_severities' );
     67            $this->filterFields     = get_site_option( 'bugsnag_filterfields' );
     68        }
    5969
    6070        $this->constructBugsnag();
     
    7282            $this->client->setNotifier(self::$NOTIFIER);
    7383
    74             // Hook up automatic error handling
    75             set_error_handler(array($this->client, "errorHandler"));
    76             set_exception_handler(array($this->client, "exceptionHandler"));
    77         }
    78 
     84            // If handlers are not set, errors are still going to be reported
     85            // to bugsnag, difference is execution will not stop.
     86            //
     87            // Can be useful to see inline errors and traces with xdebug too.
     88            $set_error_and_exception_handlers = apply_filters('bugsnag_set_error_and_exception_handlers', true);
     89            if ($set_error_and_exception_handlers === true) {
     90                // Hook up automatic error handling
     91                set_error_handler(array($this->client, "errorHandler"));
     92                set_exception_handler(array($this->client, "exceptionHandler"));
     93            }
     94        }
     95
     96    }
     97
     98    private function requireBugsnagPhp()
     99    {
     100        // Bugsnag-php was already loaded by some 3rd-party code, don't need to load it again.
     101        if (class_exists('Bugsnag_Client')) {
     102            return true;
     103        }
     104
     105        // Try loading bugsnag-php with composer autoloader.
     106        $composer_autoloader_path = $this->relativePath(self::$COMPOSER_AUTOLOADER);
     107        $composer_autoloader_path_filtered = apply_filters('bugsnag_composer_autoloader_path', $composer_autoloader_path);
     108        if (file_exists($composer_autoloader_path_filtered)) {
     109            require_once $composer_autoloader_path_filtered;
     110            return true;
     111        }
     112
     113        // Try loading bugsnag-php from packaged autoloader.
     114        $packaged_autoloader_path = $this->relativePath(self::$PACKAGED_AUTOLOADER);
     115        $packaged_autoloader_path_filtered = apply_filters('bugsnag_packaged_autoloader_path', $packaged_autoloader_path);
     116        if (file_exists($packaged_autoloader_path_filtered)) {
     117            require_once $packaged_autoloader_path_filtered;
     118            return true;
     119        }
     120
     121        return false;
    79122    }
    80123
     
    99142    private function filterFields()
    100143    {
    101         return array_map('trim', explode("\n", $this->filterFields));
     144        $filter_fields = apply_filters('bugsnag_filter_fields', $this->filterFields);
     145
     146        // Array with empty string will break things.
     147        if ($filter_fields === '') {
     148            return array();
     149        }
     150
     151        return array_map('trim', explode("\n", $filter_fields));
    102152    }
    103153
    104154    private function releaseStage()
    105155    {
    106         return defined('WP_ENV') ? WP_ENV : "production";
     156        $release_stage = defined('WP_ENV') ? WP_ENV : "production";
     157        $release_stage_filtered = apply_filters('bugsnag_release_stage', $release_stage);
     158        return $release_stage_filtered;
    107159    }
    108160
     
    111163    public function initActions()
    112164    {
    113         // Set the bugsnag user using the current WordPress user if available
    114         $wpUser = wp_get_current_user();
    115         if(!empty($this->client) && !empty($wpUser)) {
    116             $user = array();
    117 
    118             if(!empty($wpUser->user_login)) {
    119                 $user['id'] = $wpUser->user_login;
     165        // This should be handled on stage of initializing,
     166        // not even adding action if init failed.
     167        //
     168        // Leaving it here for now.
     169        if(empty($this->client)) {
     170            return;
     171        }
     172
     173
     174        // Set the bugsnag user using the current WordPress user if available,
     175        // set as anonymous otherwise.
     176        $user = array();
     177        if (is_user_logged_in()) {
     178            $wp_user = wp_get_current_user();
     179
     180            // Removed checks for !empty($wp_user->display_name), it should not be required.
     181            $user['id'] = $wp_user->user_login;
     182            $user['email'] = $wp_user->user_email;
     183            $user['name'] = $wp_user->display_name;
     184        }
     185        else {
     186            $use_unsafe_spoofable_ip_address_getter = apply_filters('bugsnag_use_unsafe_spoofable_ip_address_getter', true);
     187            $user['id'] = $use_unsafe_spoofable_ip_address_getter ?
     188                $this->getClientIpAddressUnsafe() :
     189                $this->getClientIpAddress();
     190            $user['name'] = 'anonymous';
     191        }
     192
     193        $this->client->setUser($user);
     194    }
     195
     196    // Unsafe: client can spoof address.
     197    // http://stackoverflow.com/questions/1634782/what-is-the-most-accurate-way-to-retrieve-a-users-correct-ip-address-in-php
     198    private function getClientIpAddressUnsafe()
     199    {
     200        foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){
     201            if (array_key_exists($key, $_SERVER) === true) {
     202                foreach (explode(',', $_SERVER[$key]) as $ip) {
     203                    $ip = trim($ip);
     204                    if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
     205                        return $ip;
     206                    }
     207                }
    120208            }
    121 
    122             if(!empty($wpUser->user_email)) {
    123                 $user['email'] = $wpUser->user_email;
    124             }
    125 
    126             if(!empty($wpUser->user_display_name)) {
    127                 $user['name'] = $wpUser->user_display_name;
    128             }
    129 
    130             $this->client->setUser($user);
    131         }
     209        }
     210    }
     211
     212    // Can not be spoofed, but can show ip of NAT or proxies.
     213    private function getClientIpAddress()
     214    {
     215        return $_SERVER['REMOTE_ADDR'];
    132216    }
    133217
    134218    public function adminMenuActions()
    135219    {
    136         // Add the "settings" link to the Bugsnag row of plugins.php
    137         add_filter('plugin_action_links', array($this, 'pluginActionLinksFilter'), 10, 2);
    138 
    139         // Create the settings page
    140         add_options_page('Bugsnag Settings', 'Bugsnag', 'manage_options', 'bugsnag', array($this, 'renderSettings'));
     220        if ( ! function_exists( 'is_plugin_active_for_network' ) || ! is_plugin_active_for_network($this->pluginBase)) {
     221            // Add the "settings" link to the Bugsnag row of plugins.php
     222            add_filter('plugin_action_links', array($this, 'pluginActionLinksFilter'), 10, 2);
     223
     224            // Create the settings page
     225            add_options_page('Bugsnag Settings', 'Bugsnag', 'manage_options', 'bugsnag', array($this, 'renderSettings'));
     226        }
     227    }
     228
     229    public function networkAdminMenuActions()
     230    {
     231        if (function_exists('is_plugin_active_for_network') && is_plugin_active_for_network($this->pluginBase)) {
     232            // Create the network settings page
     233            add_submenu_page('settings.php', 'Bugsnag Settings', 'Bugsnag', 'manage_network_options', 'bugsnag', array($this, 'renderSettings'));
     234        }
     235    }
     236
     237    private function updateNetworkSettings( $settings )
     238    {
     239        // Update options
     240        update_site_option('bugsnag_api_key', isset($_POST['bugsnag_api_key']) ? $_POST['bugsnag_api_key'] : '');
     241        update_site_option('bugsnag_notify_severities', isset($_POST['bugsnag_notify_severities']) ? $_POST['bugsnag_notify_severities'] : '');
     242        update_site_option('bugsnag_filterfields', isset($_POST['bugsnag_filterfields']) ? $_POST['bugsnag_filterfields'] : '');
     243        update_site_option('bugsnag_network', true);
     244
     245        // Update variables
     246        $this->apiKey           = get_site_option( 'bugsnag_api_key' );
     247        $this->notifySeverities = get_site_option( 'bugsnag_notify_severities' );
     248        $this->filterFields     = get_site_option( 'bugsnag_filterfields' );
     249
     250        echo '<div class="updated"><p>Settings saved.</p></div>';
    141251    }
    142252
     
    171281    public function renderSettings()
    172282    {
     283        if ( ! empty($_POST[ 'action' ]) && $_POST[ 'action' ] == 'update') {
     284            $this->updateNetworkSettings( $_POST );
     285        }
     286
    173287        include $this->relativePath('views/settings.php');
    174288    }
     
    179293        echo "<option value=\"$value\"$selected>$name</option>";
    180294    }
     295
     296    /**
     297     * Fluent interface to $this->client, simply call the methods on this object and this will proxy them through.
     298     *
     299     * @param string $method
     300     * @param array  $arguments
     301     *
     302     * @return mixed
     303     */
     304    public function __call($method, $arguments)
     305    {
     306        if (method_exists($this->client, $method)) {
     307            return call_user_func_array(array($this->client, $method), $arguments);
     308        }
     309
     310        throw new BadMethodCallException(sprintf('Method %s does not exist on %s or Bugsnag_Client', $method, __CLASS__ ));
     311    }
    181312}
    182313
  • bugsnag/trunk/readme.txt

    r982429 r1542350  
    33Tags: bugsnag, error, monitoring, exception, logging
    44Requires at least: 2.0
    5 Tested up to: 3.8
    6 Stable tag: 1.1.2
     5Tested up to: 4.5
     6Stable tag: 1.3.0
    77License: GPLv2 or later
    88
     
    3939== Changelog ==
    4040
     41= 1.3.0 =
     42* Fix version constraints
     43* General fixes for WP 4.5 compatibility
     44
     45= 1.2.1 =
     46* Add support for WordPress Multisite installations.
     47
    4148= 1.2.0 =
    4249* Update bugsnag-php to allow cURL or fopen.
  • bugsnag/trunk/views/settings.php

    r904344 r1542350  
    99  </p>
    1010
    11   <form method="post" action="options.php">
     11  <?php if (function_exists('is_plugin_active_for_network') && is_plugin_active_for_network($this->pluginBase)) { ?>
     12  <form method='post'>
     13  <?php } else { ?>
     14  <form method="post" action="options.php">
     15  <?php } ?>
    1216    <?php if(empty($this->apiKey)) { ?>
    1317
     
    3943        </th>
    4044        <td>
    41           <input type="text" id="bugsnag_api_key" name="bugsnag_api_key" value="<?php echo get_option('bugsnag_api_key'); ?>" class="regular-text code" /><br>
     45          <input type="text" id="bugsnag_api_key" name="bugsnag_api_key" value="<?php echo $this->apiKey ?>" class="regular-text code" /><br>
    4246
    4347          <p class="description">
     
    6771        </th>
    6872        <td>
    69           <textarea id="bugsnag_filterfields" name="bugsnag_filterfields" class="regular-text filterfields"  style="width: 355px; height: 150px;"><?php echo get_option('bugsnag_filterfields'); ?></textarea>
     73          <textarea id="bugsnag_filterfields" name="bugsnag_filterfields" class="regular-text filterfields"  style="width: 355px; height: 150px;"><?php echo $this->filterFields; ?></textarea>
    7074          <p class="description" style="max-width: 400px">
    7175            The information to remove from Bugsnag reports, one per line. Use this if you want to ensure you don't send sensitive data such as passwords, and credit card numbers to our servers.
Note: See TracChangeset for help on using the changeset viewer.