laravel-6 support

This commit is contained in:
RafficMohammed
2023-01-08 01:17:22 +05:30
parent 1a5c16ae4b
commit 774eed8b0e
4962 changed files with 279380 additions and 297961 deletions

297
.idea/community.iml generated
View File

@@ -5,169 +5,158 @@
<sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" />
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="false" packagePrefix="Tests\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
<excludeFolder url="file://$MODULE_DIR$/vendor/brozot/laravel-fcm" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dragonmantank/cron-expression" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nicolaslopezj/searchable" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-webdriver/webdriver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/intervention/image" />
<excludeFolder url="file://$MODULE_DIR$/vendor/thomaswelton/laravel-gravatar" />
<excludeFolder url="file://$MODULE_DIR$/vendor/thomaswelton/gravatarlib" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
<excludeFolder url="file://$MODULE_DIR$/vendor/yajra/laravel-datatables-oracle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/maatwebsite/excel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/beyondcode/laravel-dump-server" />
<excludeFolder url="file://$MODULE_DIR$/vendor/davibennun/laravel-push-notification" />
<excludeFolder url="file://$MODULE_DIR$/vendor/tymon/jwt-auth" />
<excludeFolder url="file://$MODULE_DIR$/vendor/egulias/email-validator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fzaninotto/faker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phenx/php-font-lib" />
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
<excludeFolder url="file://$MODULE_DIR$/vendor/mremi/url-shortener" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/php-parser" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dnoegel/php-xdg-base-dir" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/chumper/datatable" />
<excludeFolder url="file://$MODULE_DIR$/vendor/opis/closure" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
<excludeFolder url="file://$MODULE_DIR$/vendor/chumper/zipper" />
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoffice/phpspreadsheet" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoption/phpoption" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sly/notification-pusher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
<excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/complex" />
<excludeFolder url="file://$MODULE_DIR$/vendor/bugsnag/bugsnag-laravel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/matrix" />
<excludeFolder url="file://$MODULE_DIR$/vendor/mtdowling/jmespath.php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
<excludeFolder url="file://$MODULE_DIR$/vendor/vsmoraes/laravel-pdf" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fideloper/proxy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/maennchen/zipstream-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psy/psysh" />
<excludeFolder url="file://$MODULE_DIR$/vendor/bugsnag/bugsnag" />
<excludeFolder url="file://$MODULE_DIR$/vendor/unisharp/laravel-filemanager" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
<excludeFolder url="file://$MODULE_DIR$/vendor/unisharp/laravel-ckeditor" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/vendor/vlucas/phpdotenv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/lcobucci/jwt" />
<excludeFolder url="file://$MODULE_DIR$/vendor/neitanod/forceutf8" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-client" />
<excludeFolder url="file://$MODULE_DIR$/vendor/torann/geoip" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
<excludeFolder url="file://$MODULE_DIR$/vendor/julien-c/iso3166" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hamcrest/hamcrest-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/inflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/gitonomy/gitlib" />
<excludeFolder url="file://$MODULE_DIR$/vendor/tedivm/fetch" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/event-manager" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/dbal" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/deprecations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/lexer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/predis/predis" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nesbot/carbon" />
<excludeFolder url="file://$MODULE_DIR$/vendor/namshi/jose" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/flysystem" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/mime-type-detection" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/oauth1-client" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzle/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/debug" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/dom-crawler" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
<excludeFolder url="file://$MODULE_DIR$/vendor/flowjs/flow-php-server" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ezyang/htmlpurifier" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/debug" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/filesystem" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-client-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/css-selector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/error-handler" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-dumper" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
<excludeFolder url="file://$MODULE_DIR$/vendor/aws/aws-crt-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/yaml" />
<excludeFolder url="file://$MODULE_DIR$/vendor/aws/aws-sdk-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/process" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/erusev/parsedown" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/php-diff" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-parallel-lint/php-console-color" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/phpspec" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-parallel-lint/php-console-highlighter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dompdf/dompdf" />
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/php-enum" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-escaper" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/davejamesmiller/laravel-breadcrumbs" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/container-interop/container-interop" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravelcollective/html" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/dusk" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-http" />
<excludeFolder url="file://$MODULE_DIR$/vendor/codacy/coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/mockery/mockery" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zendservice-apple-apns" />
<excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-loader" />
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-validator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/giggsey/libphonenumber-for-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-uri" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/socialite" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zendservice-google-gcm" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/tinker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-stdlib" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/framework" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-json" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/giggsey/locale" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
<excludeFolder url="file://$MODULE_DIR$/vendor/propaganistas/laravel-phone" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/css-selector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-client-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nicolaslopezj/searchable" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-webdriver/webdriver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dragonmantank/cron-expression" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
<excludeFolder url="file://$MODULE_DIR$/vendor/thomaswelton/laravel-gravatar" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/error-handler" />
<excludeFolder url="file://$MODULE_DIR$/vendor/thomaswelton/gravatarlib" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/intervention/image" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/maatwebsite/excel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-dumper" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/yaml" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/process" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/type" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/php-diff" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/phpspec" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoption/phpoption" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/php-enum" />
<excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoffice/phpspreadsheet" />
<excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
<excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/matrix" />
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/mtdowling/jmespath.php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/dusk" />
<excludeFolder url="file://$MODULE_DIR$/vendor/maennchen/zipstream-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/mockery/mockery" />
<excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/complex" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/tinker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/vsmoraes/laravel-pdf" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/framework" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fideloper/proxy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/giggsey/libphonenumber-for-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/unisharp/laravel-ckeditor" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/socialite" />
<excludeFolder url="file://$MODULE_DIR$/vendor/unisharp/laravel-filemanager" />
<excludeFolder url="file://$MODULE_DIR$/vendor/egulias/email-validator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/lcobucci/jwt" />
<excludeFolder url="file://$MODULE_DIR$/vendor/giggsey/locale" />
<excludeFolder url="file://$MODULE_DIR$/vendor/neitanod/forceutf8" />
<excludeFolder url="file://$MODULE_DIR$/vendor/bugsnag/bugsnag-psr-logger" />
<excludeFolder url="file://$MODULE_DIR$/vendor/gitonomy/gitlib" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dnoegel/php-xdg-base-dir" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hamcrest/hamcrest-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/bugsnag/bugsnag" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/inflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/bugsnag/bugsnag-laravel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fakerphp/faker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/torann/geoip" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/dbal" />
<excludeFolder url="file://$MODULE_DIR$/vendor/vlucas/phpdotenv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/deprecations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/scrivo/highlight.php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/event-manager" />
<excludeFolder url="file://$MODULE_DIR$/vendor/tedivm/fetch" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
<excludeFolder url="file://$MODULE_DIR$/vendor/predis/predis" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/lexer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/namshi/jose" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nesbot/carbon" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/commonmark" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/flysystem" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/oauth1-client" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/iso3166" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzle/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/league/mime-type-detection" />
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/ignition-contracts" />
<excludeFolder url="file://$MODULE_DIR$/vendor/flowjs/flow-php-server" />
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/flare-client-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/ignition" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dompdf/dompdf" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ezyang/htmlpurifier" />
<excludeFolder url="file://$MODULE_DIR$/vendor/brozot/laravel-fcm" />
<excludeFolder url="file://$MODULE_DIR$/vendor/codacy/coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/tymon/jwt-auth" />
<excludeFolder url="file://$MODULE_DIR$/vendor/yajra/laravel-datatables-oracle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/php-parser" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phenx/php-font-lib" />
<excludeFolder url="file://$MODULE_DIR$/vendor/opis/closure" />
<excludeFolder url="file://$MODULE_DIR$/vendor/mremi/url-shortener" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psy/psysh" />
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-client" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/aws/aws-crt-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/aws/aws-sdk-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

297
.idea/php.xml generated
View File

@@ -2,169 +2,158 @@
<project version="4">
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
<path value="$PROJECT_DIR$/vendor/brozot/laravel-fcm" />
<path value="$PROJECT_DIR$/vendor/dragonmantank/cron-expression" />
<path value="$PROJECT_DIR$/vendor/nicolaslopezj/searchable" />
<path value="$PROJECT_DIR$/vendor/php-webdriver/webdriver" />
<path value="$PROJECT_DIR$/vendor/swiftmailer/swiftmailer" />
<path value="$PROJECT_DIR$/vendor/intervention/image" />
<path value="$PROJECT_DIR$/vendor/thomaswelton/laravel-gravatar" />
<path value="$PROJECT_DIR$/vendor/thomaswelton/gravatarlib" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
<path value="$PROJECT_DIR$/vendor/nunomaduro/collision" />
<path value="$PROJECT_DIR$/vendor/yajra/laravel-datatables-oracle" />
<path value="$PROJECT_DIR$/vendor/maatwebsite/excel" />
<path value="$PROJECT_DIR$/vendor/beyondcode/laravel-dump-server" />
<path value="$PROJECT_DIR$/vendor/davibennun/laravel-push-notification" />
<path value="$PROJECT_DIR$/vendor/tymon/jwt-auth" />
<path value="$PROJECT_DIR$/vendor/egulias/email-validator" />
<path value="$PROJECT_DIR$/vendor/fzaninotto/faker" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/phenx/php-font-lib" />
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
<path value="$PROJECT_DIR$/vendor/mremi/url-shortener" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
<path value="$PROJECT_DIR$/vendor/dnoegel/php-xdg-base-dir" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/chumper/datatable" />
<path value="$PROJECT_DIR$/vendor/opis/closure" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/chumper/zipper" />
<path value="$PROJECT_DIR$/vendor/filp/whoops" />
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
<path value="$PROJECT_DIR$/vendor/phpoption/phpoption" />
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
<path value="$PROJECT_DIR$/vendor/sly/notification-pusher" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
<path value="$PROJECT_DIR$/vendor/bugsnag/bugsnag-laravel" />
<path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
<path value="$PROJECT_DIR$/vendor/mtdowling/jmespath.php" />
<path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
<path value="$PROJECT_DIR$/vendor/vsmoraes/laravel-pdf" />
<path value="$PROJECT_DIR$/vendor/fideloper/proxy" />
<path value="$PROJECT_DIR$/vendor/maennchen/zipstream-php" />
<path value="$PROJECT_DIR$/vendor/psy/psysh" />
<path value="$PROJECT_DIR$/vendor/bugsnag/bugsnag" />
<path value="$PROJECT_DIR$/vendor/unisharp/laravel-filemanager" />
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/vendor/unisharp/laravel-ckeditor" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/vlucas/phpdotenv" />
<path value="$PROJECT_DIR$/vendor/lcobucci/jwt" />
<path value="$PROJECT_DIR$/vendor/neitanod/forceutf8" />
<path value="$PROJECT_DIR$/vendor/psr/http-client" />
<path value="$PROJECT_DIR$/vendor/torann/geoip" />
<path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
<path value="$PROJECT_DIR$/vendor/julien-c/iso3166" />
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
<path value="$PROJECT_DIR$/vendor/hamcrest/hamcrest-php" />
<path value="$PROJECT_DIR$/vendor/doctrine/inflector" />
<path value="$PROJECT_DIR$/vendor/gitonomy/gitlib" />
<path value="$PROJECT_DIR$/vendor/tedivm/fetch" />
<path value="$PROJECT_DIR$/vendor/doctrine/event-manager" />
<path value="$PROJECT_DIR$/vendor/doctrine/cache" />
<path value="$PROJECT_DIR$/vendor/doctrine/dbal" />
<path value="$PROJECT_DIR$/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/vendor/psr/container" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/doctrine/lexer" />
<path value="$PROJECT_DIR$/vendor/predis/predis" />
<path value="$PROJECT_DIR$/vendor/ramsey/uuid" />
<path value="$PROJECT_DIR$/vendor/nesbot/carbon" />
<path value="$PROJECT_DIR$/vendor/namshi/jose" />
<path value="$PROJECT_DIR$/vendor/composer" />
<path value="$PROJECT_DIR$/vendor/league/flysystem" />
<path value="$PROJECT_DIR$/vendor/league/mime-type-detection" />
<path value="$PROJECT_DIR$/vendor/league/oauth1-client" />
<path value="$PROJECT_DIR$/vendor/guzzle/guzzle" />
<path value="$PROJECT_DIR$/vendor/theseer/tokenizer" />
<path value="$PROJECT_DIR$/vendor/symfony/debug" />
<path value="$PROJECT_DIR$/vendor/symfony/dom-crawler" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
<path value="$PROJECT_DIR$/vendor/flowjs/flow-php-server" />
<path value="$PROJECT_DIR$/vendor/ezyang/htmlpurifier" />
<path value="$PROJECT_DIR$/vendor/symfony/console" />
<path value="$PROJECT_DIR$/vendor/symfony/debug" />
<path value="$PROJECT_DIR$/vendor/symfony/translation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/filesystem" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-iconv" />
<path value="$PROJECT_DIR$/vendor/symfony/http-kernel" />
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
<path value="$PROJECT_DIR$/vendor/symfony/css-selector" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/symfony/error-handler" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
<path value="$PROJECT_DIR$/vendor/aws/aws-crt-php" />
<path value="$PROJECT_DIR$/vendor/symfony/yaml" />
<path value="$PROJECT_DIR$/vendor/aws/aws-sdk-php" />
<path value="$PROJECT_DIR$/vendor/symfony/process" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/erusev/parsedown" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/phpspec/php-diff" />
<path value="$PROJECT_DIR$/vendor/php-parallel-lint/php-console-color" />
<path value="$PROJECT_DIR$/vendor/phpspec/phpspec" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
<path value="$PROJECT_DIR$/vendor/php-parallel-lint/php-console-highlighter" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-token-stream" />
<path value="$PROJECT_DIR$/vendor/dompdf/dompdf" />
<path value="$PROJECT_DIR$/vendor/myclabs/php-enum" />
<path value="$PROJECT_DIR$/vendor/zendframework/zend-escaper" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/symfony/translation" />
<path value="$PROJECT_DIR$/vendor/davejamesmiller/laravel-breadcrumbs" />
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/container-interop/container-interop" />
<path value="$PROJECT_DIR$/vendor/phpspec/prophecy" />
<path value="$PROJECT_DIR$/vendor/symfony/console" />
<path value="$PROJECT_DIR$/vendor/laravelcollective/html" />
<path value="$PROJECT_DIR$/vendor/laravel/dusk" />
<path value="$PROJECT_DIR$/vendor/zendframework/zend-http" />
<path value="$PROJECT_DIR$/vendor/codacy/coverage" />
<path value="$PROJECT_DIR$/vendor/mockery/mockery" />
<path value="$PROJECT_DIR$/vendor/zendframework/zendservice-apple-apns" />
<path value="$PROJECT_DIR$/vendor/monolog/monolog" />
<path value="$PROJECT_DIR$/vendor/zendframework/zend-loader" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/zendframework/zend-validator" />
<path value="$PROJECT_DIR$/vendor/giggsey/libphonenumber-for-php" />
<path value="$PROJECT_DIR$/vendor/zendframework/zend-uri" />
<path value="$PROJECT_DIR$/vendor/laravel/socialite" />
<path value="$PROJECT_DIR$/vendor/zendframework/zendservice-google-gcm" />
<path value="$PROJECT_DIR$/vendor/laravel/tinker" />
<path value="$PROJECT_DIR$/vendor/zendframework/zend-stdlib" />
<path value="$PROJECT_DIR$/vendor/laravel/framework" />
<path value="$PROJECT_DIR$/vendor/zendframework/zend-json" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
<path value="$PROJECT_DIR$/vendor/symfony/http-kernel" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/type-resolver" />
<path value="$PROJECT_DIR$/vendor/giggsey/locale" />
<path value="$PROJECT_DIR$/vendor/symfony/routing" />
<path value="$PROJECT_DIR$/vendor/propaganistas/laravel-phone" />
<path value="$PROJECT_DIR$/vendor/symfony/css-selector" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-docblock" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-iconv" />
<path value="$PROJECT_DIR$/vendor/phpdocumentor/reflection-common" />
<path value="$PROJECT_DIR$/vendor/symfony/http-client-contracts" />
<path value="$PROJECT_DIR$/vendor/nicolaslopezj/searchable" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
<path value="$PROJECT_DIR$/vendor/php-webdriver/webdriver" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
<path value="$PROJECT_DIR$/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/vendor/dragonmantank/cron-expression" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
<path value="$PROJECT_DIR$/vendor/thomaswelton/laravel-gravatar" />
<path value="$PROJECT_DIR$/vendor/symfony/error-handler" />
<path value="$PROJECT_DIR$/vendor/thomaswelton/gravatarlib" />
<path value="$PROJECT_DIR$/vendor/symfony/mime" />
<path value="$PROJECT_DIR$/vendor/swiftmailer/swiftmailer" />
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/vendor/intervention/image" />
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/vendor/nunomaduro/collision" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/vendor/maatwebsite/excel" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/vendor/symfony/http-foundation" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
<path value="$PROJECT_DIR$/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/vendor/webmozart/assert" />
<path value="$PROJECT_DIR$/vendor/symfony/var-dumper" />
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
<path value="$PROJECT_DIR$/vendor/symfony/yaml" />
<path value="$PROJECT_DIR$/vendor/sebastian/comparator" />
<path value="$PROJECT_DIR$/vendor/symfony/process" />
<path value="$PROJECT_DIR$/vendor/sebastian/type" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
<path value="$PROJECT_DIR$/vendor/sebastian/global-state" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-file-iterator" />
<path value="$PROJECT_DIR$/vendor/sebastian/environment" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-text-template" />
<path value="$PROJECT_DIR$/vendor/sebastian/resource-operations" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-timer" />
<path value="$PROJECT_DIR$/vendor/sebastian/exporter" />
<path value="$PROJECT_DIR$/vendor/phpunit/phpunit" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-enumerator" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-code-coverage" />
<path value="$PROJECT_DIR$/vendor/sebastian/object-reflector" />
<path value="$PROJECT_DIR$/vendor/phpunit/php-token-stream" />
<path value="$PROJECT_DIR$/vendor/sebastian/version" />
<path value="$PROJECT_DIR$/vendor/phpspec/php-diff" />
<path value="$PROJECT_DIR$/vendor/sebastian/diff" />
<path value="$PROJECT_DIR$/vendor/phpspec/phpspec" />
<path value="$PROJECT_DIR$/vendor/sebastian/recursion-context" />
<path value="$PROJECT_DIR$/vendor/phar-io/manifest" />
<path value="$PROJECT_DIR$/vendor/phpoption/phpoption" />
<path value="$PROJECT_DIR$/vendor/phpspec/prophecy" />
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
<path value="$PROJECT_DIR$/vendor/myclabs/php-enum" />
<path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
<path value="$PROJECT_DIR$/vendor/phar-io/version" />
<path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" />
<path value="$PROJECT_DIR$/vendor/monolog/monolog" />
<path value="$PROJECT_DIR$/vendor/markbaker/matrix" />
<path value="$PROJECT_DIR$/vendor/myclabs/deep-copy" />
<path value="$PROJECT_DIR$/vendor/mtdowling/jmespath.php" />
<path value="$PROJECT_DIR$/vendor/laravel/dusk" />
<path value="$PROJECT_DIR$/vendor/maennchen/zipstream-php" />
<path value="$PROJECT_DIR$/vendor/mockery/mockery" />
<path value="$PROJECT_DIR$/vendor/markbaker/complex" />
<path value="$PROJECT_DIR$/vendor/laravel/tinker" />
<path value="$PROJECT_DIR$/vendor/vsmoraes/laravel-pdf" />
<path value="$PROJECT_DIR$/vendor/laravel/framework" />
<path value="$PROJECT_DIR$/vendor/fideloper/proxy" />
<path value="$PROJECT_DIR$/vendor/giggsey/libphonenumber-for-php" />
<path value="$PROJECT_DIR$/vendor/unisharp/laravel-ckeditor" />
<path value="$PROJECT_DIR$/vendor/laravel/socialite" />
<path value="$PROJECT_DIR$/vendor/unisharp/laravel-filemanager" />
<path value="$PROJECT_DIR$/vendor/egulias/email-validator" />
<path value="$PROJECT_DIR$/vendor/lcobucci/jwt" />
<path value="$PROJECT_DIR$/vendor/giggsey/locale" />
<path value="$PROJECT_DIR$/vendor/neitanod/forceutf8" />
<path value="$PROJECT_DIR$/vendor/bugsnag/bugsnag-psr-logger" />
<path value="$PROJECT_DIR$/vendor/gitonomy/gitlib" />
<path value="$PROJECT_DIR$/vendor/dnoegel/php-xdg-base-dir" />
<path value="$PROJECT_DIR$/vendor/hamcrest/hamcrest-php" />
<path value="$PROJECT_DIR$/vendor/bugsnag/bugsnag" />
<path value="$PROJECT_DIR$/vendor/doctrine/inflector" />
<path value="$PROJECT_DIR$/vendor/bugsnag/bugsnag-laravel" />
<path value="$PROJECT_DIR$/vendor/fakerphp/faker" />
<path value="$PROJECT_DIR$/vendor/torann/geoip" />
<path value="$PROJECT_DIR$/vendor/doctrine/dbal" />
<path value="$PROJECT_DIR$/vendor/vlucas/phpdotenv" />
<path value="$PROJECT_DIR$/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/vendor/scrivo/highlight.php" />
<path value="$PROJECT_DIR$/vendor/doctrine/event-manager" />
<path value="$PROJECT_DIR$/vendor/tedivm/fetch" />
<path value="$PROJECT_DIR$/vendor/doctrine/cache" />
<path value="$PROJECT_DIR$/vendor/predis/predis" />
<path value="$PROJECT_DIR$/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/vendor/ramsey/uuid" />
<path value="$PROJECT_DIR$/vendor/doctrine/lexer" />
<path value="$PROJECT_DIR$/vendor/namshi/jose" />
<path value="$PROJECT_DIR$/vendor/nesbot/carbon" />
<path value="$PROJECT_DIR$/vendor/league/commonmark" />
<path value="$PROJECT_DIR$/vendor/league/flysystem" />
<path value="$PROJECT_DIR$/vendor/league/oauth1-client" />
<path value="$PROJECT_DIR$/vendor/league/iso3166" />
<path value="$PROJECT_DIR$/vendor/guzzle/guzzle" />
<path value="$PROJECT_DIR$/vendor/league/mime-type-detection" />
<path value="$PROJECT_DIR$/vendor/facade/ignition-contracts" />
<path value="$PROJECT_DIR$/vendor/flowjs/flow-php-server" />
<path value="$PROJECT_DIR$/vendor/facade/flare-client-php" />
<path value="$PROJECT_DIR$/vendor/facade/ignition" />
<path value="$PROJECT_DIR$/vendor/dompdf/dompdf" />
<path value="$PROJECT_DIR$/vendor/ezyang/htmlpurifier" />
<path value="$PROJECT_DIR$/vendor/brozot/laravel-fcm" />
<path value="$PROJECT_DIR$/vendor/codacy/coverage" />
<path value="$PROJECT_DIR$/vendor/tymon/jwt-auth" />
<path value="$PROJECT_DIR$/vendor/yajra/laravel-datatables-oracle" />
<path value="$PROJECT_DIR$/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/vendor/phenx/php-font-lib" />
<path value="$PROJECT_DIR$/vendor/opis/closure" />
<path value="$PROJECT_DIR$/vendor/mremi/url-shortener" />
<path value="$PROJECT_DIR$/vendor/psy/psysh" />
<path value="$PROJECT_DIR$/vendor/filp/whoops" />
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/psr/simple-cache" />
<path value="$PROJECT_DIR$/vendor/psr/http-client" />
<path value="$PROJECT_DIR$/vendor/psr/container" />
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
<path value="$PROJECT_DIR$/vendor/aws/aws-crt-php" />
<path value="$PROJECT_DIR$/vendor/aws/aws-sdk-php" />
<path value="$PROJECT_DIR$/vendor/composer" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />

View File

@@ -2,229 +2,54 @@
namespace App\Exceptions;
// controller
use Bugsnag;
use Bugsnag\BugsnagLaravel\BugsnagExceptionHandler as ExceptionHandler;
use Config;
use Exception;
// use Symfony\Component\HttpKernel\Exception\HttpException;
// use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Foundation\Validation\ValidationException as foundation;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [
// 'Symfony\Component\HttpKernel\Exception\HttpException',
\Illuminate\Http\Exception\HttpResponseException::class,
foundation::class,
AuthorizationException::class,
HttpResponseException::class,
ModelNotFoundException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
ValidationException::class,
\DaveJamesMiller\Breadcrumbs\Exception::class,
//
];
/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
* @param \Exception $exception
* @return void
*/
public function report(Exception $e)
{
Bugsnag::setBeforeNotifyFunction(function ($error) { //set bugsnag
return false;
});
// check if system is running in production environment
if (\App::environment() == 'production') {
$debug = Config::get('app.bugsnag_reporting'); //get bugsang reporting preference
if ($debug) { //if preference is true for reporting
$version = Config::get('app.version'); //set app version in report
Bugsnag::setAppVersion($version);
Bugsnag::setBeforeNotifyFunction(function ($error) { //set bugsnag
return true;
}); //set bugsnag reporting as true
}
}
return parent::report($e);
}
/**
* Convert a validation exception into a JSON response.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Validation\ValidationException $exception
* @return \Illuminate\Http\JsonResponse
* @throws \Exception
*/
protected function invalidJson($request, ValidationException $exception)
public function report(Exception $exception)
{
return response()->json(['success' => false, 'errors' => $exception->errors()], $exception->status);
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @param type $request
* @param Exception $e
* @return type mixed
*/
public function render($request, Exception $e)
{
switch ($e) {
case $e instanceof \Illuminate\Http\Exception\HttpResponseException:
return parent::render($request, $e);
case $e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException:
return response()->json(['message' => $e->getMessage(), 'code' => $e->getStatusCode()]);
case $e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException:
return response()->json(['message' => $e->getMessage(), 'code' => $e->getStatusCode()]);
case $e instanceof TokenMismatchException:
if ($request->ajax() || $request->wantsJson()) {
$result = ['fails' => \Lang::get('lang.session-expired')];
return response()->json(compact('result'), 402);
}
return redirect()->back()->with('fails', \Lang::get('lang.session-expired'));
default:
return $this->common($request, $e);
}
}
/**
* Function to render 500 error page.
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Symfony\Component\HttpFoundation\Response
*
* @param type $request
* @param type $e
* @return type mixed
* @throws \Exception
*/
public function render500($request, $e)
public function render($request, Exception $exception)
{
$seg = $request->segments();
if (in_array('api', $seg)) {
if ($e instanceof ValidationException) {
return $this->invalidJson($request, $e);
}
return response()->json(['error' => $e->getMessage()], 500);
}
if (config('app.debug') == true) {
return parent::render($request, $e);
} elseif ($e instanceof ValidationException) {
return parent::render($request, $e);
} elseif ($e instanceof \Illuminate\Validation\ValidationException) {
return parent::render($request, $e);
}
return response()->view('errors.500');
//return redirect()->route('error500', []);
}
/**
* Function to render 404 error page.
*
* @param type $request
* @param type $e
* @return type mixed
*/
public function render404($request, $e)
{
$seg = $request->segments();
if (in_array('api', $seg)) {
return response()->json(['success' => false, 'message' => 'not-found'], 404);
}
if (config('app.debug') == true) {
if ($e->getStatusCode() == '404') {
return redirect()->route('error404', []);
}
return parent::render($request, $e);
}
return redirect()->route('error404', []);
}
/**
* Function to render database connection failed.
*
* @param type $request
* @param type $e
* @return type mixed
*/
public function renderDB($request, $e)
{
$seg = $request->segments();
if (in_array('api', $seg)) {
return response()->json(['status' => '404']);
}
if (config('app.debug') == true) {
return parent::render($request, $e);
}
return redirect()->route('error404', []);
}
/**
* Common finction to render both types of codes.
*
* @param type $request
* @param type $e
* @return type mixed
*/
public function common($request, $e)
{
switch ($e) {
case $e instanceof HttpException:
return $this->render404($request, $e);
case $e instanceof NotFoundHttpException:
return $this->render404($request, $e);
case $e instanceof PDOException:
if (strpos('1045', $e->getMessage()) == true) {
return $this->renderDB($request, $e);
} else {
return $this->render500($request, $e);
}
// case $e instanceof ErrorException:
// if($e->getMessage() == 'Breadcrumb not found with name "" ') {
// return $this->render404($request, $e);
// } else {
// return parent::render($request, $e);
// }
case $e instanceof TokenMismatchException:
if ($request->ajax() || $request->wantsJson()) {
$result = ['fails' => \Lang::get('lang.session-expired')];
return response()->json(compact('result'), 402);
}
return redirect()->back()->with('fails', \Lang::get('lang.session-expired'));
case $e instanceof AuthorizationException:
return redirect('/')->with('fails', \Lang::get('lang.access-denied'));
case $e instanceof MethodNotAllowedHttpException:
if (stripos($request->url(), 'api')) {
$result = ['message' => \Lang::get('lang.methon_not_allowed'), 'success' => false];
return response()->json($result, 405);
}
$this->render500($request, $e);
default:
return $this->render500($request, $e);
}
return parent::render($request, $e);
return parent::render($request, $exception);
}
}

View File

@@ -17,11 +17,8 @@
"thomaswelton/laravel-gravatar": "^1.2",
"neitanod/forceutf8": "dev-master",
"nicolaslopezj/searchable": "^1.12",
"chumper/datatable": "dev-develop",
"chumper/zipper": "1.0.x",
"tymon/jwt-auth": "1.0.2",
"davejamesmiller/laravel-breadcrumbs": "^5.3",
"davibennun/laravel-push-notification": "dev-laravel5",
"brozot/laravel-fcm": "^1.3",
"aws/aws-sdk-php": "~3.0",
"predis/predis": "~1.0",
@@ -36,7 +33,7 @@
"flowjs/flow-php-server": "^1.0",
"doctrine/dbal": "^2.6",
"fideloper/proxy": "^4.4",
"laravel/tinker": "^2.5"
"laravel/tinker": "^1.0.10"
},
"require-dev": {
"phpunit/phpunit": "^8.5.8|^9.3.3",

2543
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -133,7 +133,6 @@ return [
*/
'providers' => [
'Illuminate\Broadcasting\BroadcastServiceProvider',
'DaveJamesMiller\Breadcrumbs\ServiceProvider',
/*
* Laravel Framework Service Providers...
*/
@@ -168,12 +167,8 @@ return [
App\Providers\RouteServiceProvider::class,
App\Providers\ConfigServiceProvider::class,
App\Providers\ComposerServiceProvider::class,
'Propaganistas\LaravelPhone\LaravelPhoneServiceProvider',
'Bugsnag\BugsnagLaravel\BugsnagLaravelServiceProvider',
'Vsmoraes\Pdf\PdfServiceProvider',
'Thomaswelton\LaravelGravatar\LaravelGravatarServiceProvider',
'Chumper\Datatable\DatatableServiceProvider',
'Chumper\Zipper\ZipperServiceProvider',
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
\Torann\GeoIP\GeoIPServiceProvider::class,
Unisharp\Laravelfilemanager\LaravelFilemanagerServiceProvider::class,

2
vendor/autoload.php vendored
View File

@@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit010a6d64109fbd19b023ae0ce2ea0027::getLoader();
return ComposerAutoloaderInite7140a2ec68f998789fab4fcb333821a::getLoader();

View File

@@ -1,7 +0,0 @@
# Changelog
All notable changes to `laravel-dump-server` will be documented in this file
## 1.0.0 - 2018-07-09
- initial release

View File

@@ -1,55 +0,0 @@
# Contributing
Contributions are **welcome** and will be fully **credited**.
Please read and understand the contribution guide before creating an issue or pull request.
## Etiquette
This project is open source, and as such, the maintainers give their free time to build and maintain the source code
held within. They make the code freely available in the hope that it will be of use to other developers. It would be
extremely unfair for them to suffer abuse or anger for their hard work.
Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
world that developers are civilized and selfless people.
It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
## Viability
When requesting or submitting new features, first consider whether it might be useful to others. Open
source projects are used by many developers, who may have entirely different needs to your own. Think about
whether or not your feature is likely to be used by other users of the project.
## Procedure
Before filing an issue:
- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
- Check to make sure your feature suggestion isn't already present within the project.
- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
- Check the pull requests tab to ensure that the feature isn't already in progress.
Before submitting a pull request:
- Check the codebase to ensure that your feature doesn't already exist.
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
## Requirements
If the project maintainer has any additional requirements, you will find them listed here.
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer).
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
**Happy coding**!

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) Beyond Code GmbH <hello@beyondco.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,73 +0,0 @@
# Laravel Dump Server
[![Latest Version on Packagist](https://img.shields.io/packagist/v/beyondcode/laravel-dump-server.svg?style=flat-square)](https://packagist.org/packages/beyondcode/laravel-dump-server)
[![Quality Score](https://img.shields.io/scrutinizer/g/beyondcode/laravel-dump-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/beyondcode/laravel-dump-server)
[![Total Downloads](https://img.shields.io/packagist/dt/beyondcode/laravel-dump-server.svg?style=flat-square)](https://packagist.org/packages/beyondcode/laravel-dump-server)
Bringing the [Symfony Var-Dump Server](https://symfony.com/doc/current/components/var_dumper.html#the-dump-server) to Laravel.
This package will give you a dump server, that collects all your `dump` call outputs, so that it does not interfere with HTTP / API responses.
> If you want to learn how to create reusable PHP packages yourself, take a look at my upcoming [PHP Package Development](https://phppackagedevelopment.com) video course.
## Installation
You can install the package via composer:
```bash
composer require --dev beyondcode/laravel-dump-server
```
The package will register itself automatically.
Optionally you can publish the package configuration using:
```bash
php artisan vendor:publish --provider=BeyondCode\\DumpServer\\DumpServerServiceProvider
```
This will publish a file called `debug-server.php` in your `config` folder.
In the config file, you can specify the dump server host that you want to listen on, in case you want to change the default value.
## Usage
Start the dump server by calling the artisan command:
```bash
php artisan dump-server
```
You can set the output format to HTML using the `--format` option:
```bash
php artisan dump-server --format=html > dump.html
```
And then you can, as you are used to, put `dump` calls in your methods. But instead of dumping the output in your current HTTP request, they will be dumped in the artisan command.
This is very useful, when you want to dump data from API requests, without having to deal with HTTP errors.
You can see it in action here:
![Dump Server Demo](https://beyondco.de/github/dumpserver/dumpserver.gif)
### Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
## Contributing
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
### Security
If you discover any security related issues, please email marcel@beyondco.de instead of using the issue tracker.
## Credits
- [Marcel Pociot](https://github.com/mpociot)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

View File

@@ -1,56 +0,0 @@
{
"name": "beyondcode/laravel-dump-server",
"description": "Symfony Var-Dump Server for Laravel",
"keywords": [
"beyondcode",
"laravel-dump-server"
],
"homepage": "https://github.com/beyondcode/laravel-dump-server",
"license": "MIT",
"authors": [
{
"name": "Marcel Pociot",
"email": "marcel@beyondco.de",
"homepage": "https://beyondco.de",
"role": "Developer"
}
],
"require": {
"php": "^7.1",
"illuminate/console": "5.6.*|5.7.*|5.8.*|^6.0",
"illuminate/http": "5.6.*|5.7.*|5.8.*|^6.0",
"illuminate/support": "5.6.*|5.7.*|5.8.*|^6.0",
"symfony/var-dumper": "^4.1.1"
},
"require-dev": {
"larapack/dd": "^1.0",
"phpunit/phpunit": "^7.0"
},
"autoload": {
"psr-4": {
"BeyondCode\\DumpServer\\": "src"
},
"files": [
"helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"BeyondCode\\DumpServer\\Tests\\": "tests"
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"test-coverage": "vendor/bin/phpunit --coverage-html coverage"
},
"config": {
"sort-packages": true
},
"extra": {
"laravel": {
"providers": [
"BeyondCode\\DumpServer\\DumpServerServiceProvider"
]
}
}
}

View File

@@ -1,8 +0,0 @@
<?php
return [
/*
* The host to use when listening for debug server connections.
*/
'host' => 'tcp://127.0.0.1:9912',
];

View File

@@ -1,16 +0,0 @@
<?php
if (! function_exists('config_path')) {
/**
* Get the configuration path.
*
* This is a polyfill for the missing shorthand function in lumen.
*
* @param string $path
* @return string
*/
function config_path($path = '')
{
return app()->basePath('config').($path ? DIRECTORY_SEPARATOR.$path : $path);
}
}

View File

@@ -1,84 +0,0 @@
<?php
namespace BeyondCode\DumpServer;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Server\DumpServer;
use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor;
use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor;
class DumpServerCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $signature = 'dump-server {--format=cli : The output format (cli,html).}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Start the dump server to collect dump information.';
/**
* The Dump server.
*
* @var \Symfony\Component\VarDumper\Server\DumpServer
*/
protected $server;
/**
* DumpServerCommand constructor.
*
* @param \Symfony\Component\VarDumper\Server\DumpServer $server
* @return void
*/
public function __construct(DumpServer $server)
{
$this->server = $server;
parent::__construct();
}
/**
* Handle the command.
*
* @return void
*/
public function handle()
{
switch ($format = $this->option('format')) {
case 'cli':
$descriptor = new CliDescriptor(new CliDumper);
break;
case 'html':
$descriptor = new HtmlDescriptor(new HtmlDumper);
break;
default:
throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format));
}
$io = new SymfonyStyle($this->input, $this->output);
$errorIo = $io->getErrorStyle();
$errorIo->title('Laravel Var Dump Server');
$this->server->start();
$errorIo->success(sprintf('Server listening on %s', $this->server->getHost()));
$errorIo->comment('Quit the server with CONTROL-C.');
$this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) {
$descriptor->describe($io, $data, $context, $clientId);
});
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace BeyondCode\DumpServer;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\VarDumper\VarDumper;
use Symfony\Component\VarDumper\Server\Connection;
use Symfony\Component\VarDumper\Server\DumpServer;
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
class DumpServerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../config/config.php' => config_path('debug-server.php'),
], 'config');
}
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'debug-server');
$this->app->bind('command.dumpserver', DumpServerCommand::class);
$this->commands([
'command.dumpserver',
]);
$host = $this->app['config']->get('debug-server.host');
$this->app->when(DumpServer::class)->needs('$host')->give($host);
$connection = new Connection($host, [
'request' => new RequestContextProvider($this->app['request']),
'source' => new SourceContextProvider('utf-8', base_path()),
]);
VarDumper::setHandler(function ($var) use ($connection) {
(new Dumper($connection))->dump($var);
});
}
}

View File

@@ -1,49 +0,0 @@
<?php
namespace BeyondCode\DumpServer;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Server\Connection;
class Dumper
{
/**
* The connection.
*
* @var \Symfony\Component\VarDumper\Server\Connection|null
*/
private $connection;
/**
* Dumper constructor.
*
* @param \Symfony\Component\VarDumper\Server\Connection|null $connection
* @return void
*/
public function __construct(Connection $connection = null)
{
$this->connection = $connection;
}
/**
* Dump a value with elegance.
*
* @param mixed $value
* @return void
*/
public function dump($value)
{
if (class_exists(CliDumper::class)) {
$data = (new VarCloner)->cloneVar($value);
if ($this->connection === null || $this->connection->write($data) === false) {
$dumper = in_array(PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper : new HtmlDumper;
$dumper->dump($data);
}
} else {
var_dump($value);
}
}
}

View File

@@ -1,66 +0,0 @@
<?php
namespace BeyondCode\DumpServer;
use Illuminate\Http\Request;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface;
class RequestContextProvider implements ContextProviderInterface
{
/**
* The current request.
*
* @var \Illuminate\Http\Request|null
*/
private $currentRequest;
/**
* The variable cloner.
*
* @var \Symfony\Component\VarDumper\Cloner\VarCloner
*/
private $cloner;
/**
* RequestContextProvider constructor.
*
* @param \Illuminate\Http\Request|null $currentRequest
* @return void
*/
public function __construct(Request $currentRequest = null)
{
$this->currentRequest = $currentRequest;
$this->cloner = new VarCloner;
$this->cloner->setMaxItems(0);
}
/**
* Get the context.
*
* @return array|null
*/
public function getContext(): ?array
{
if ($this->currentRequest === null) {
return null;
}
$controller = null;
if ($route = $this->currentRequest->route()) {
$controller = $route->controller;
if (! $controller && ! is_string($route->action['uses'])) {
$controller = $route->action['uses'];
}
}
return [
'uri' => $this->currentRequest->getUri(),
'method' => $this->currentRequest->getMethod(),
'controller' => $controller ? $this->cloner->cloneVar(class_basename($controller)) : $this->cloner->cloneVar(null),
'identifier' => spl_object_hash($this->currentRequest),
];
}
}

117
vendor/bin/commonmark vendored Executable file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../league/commonmark/bin/commonmark)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/league/commonmark/bin/commonmark');
exit(0);
}
}
include __DIR__ . '/..'.'/league/commonmark/bin/commonmark';

117
vendor/bin/debug vendored
View File

@@ -1,117 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../unisharp/laravel-filemanager/bin/debug)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/unisharp/laravel-filemanager/bin/debug');
exit(0);
}
}
include __DIR__ . '/..'.'/unisharp/laravel-filemanager/bin/debug';

117
vendor/bin/np vendored
View File

@@ -1,117 +0,0 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../sly/notification-pusher/np)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_seek($offset, $whence)
{
if (0 === fseek($this->handle, $offset, $whence)) {
$this->position = ftell($this->handle);
return true;
}
return false;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/sly/notification-pusher/np');
exit(0);
}
}
include __DIR__ . '/..'.'/sly/notification-pusher/np';

View File

@@ -0,0 +1,4 @@
source 'https://rubygems.org'
gem 'bugsnag-maze-runner', git: 'https://github.com/bugsnag/maze-runner', tag: 'v3.6.0'
gem "os", "~> 1.0"

View File

@@ -0,0 +1,58 @@
Upgrading
=========
## Laravel 5.5 to 5.6
Laravel 5.6 has a brand new Monolog-base logging system changing the integration
point for the Bugsnag handlers. The `Illuminate\Contracts\Logging\Log` class has
been removed in favor of `Illuminate\Log\LogManager`.
To upgrade, remove the existing Bugsnag logging integration from the `register`
method of `app/Providers/AppServiceProvider.php`:
```diff
- $this->app->alias('bugsnag.logger', \Illuminate\Contracts\Logging\Log::class);
- $this->app->alias('bugsnag.logger', \Psr\Log\LoggerInterface::class);
```
Or if using the multi-logger:
```diff
- $this->app->alias('bugsnag.multi', \Illuminate\Contracts\Logging\Log::class);
- $this->app->alias('bugsnag.multi', \Psr\Log\LoggerInterface::class);
```
Then add Bugsnag to your logging stack in `config/logging.php`:
```php
'channels' => [
'stack' => [
'driver' => 'stack',
// Add bugsnag to the stack:
'channels' => ['single', 'bugsnag'],
],
// ...
// Create a bugsnag logging channel:
'bugsnag' => [
'driver' => 'bugsnag',
],
],
```
References:
* The [bugsnag-laravel integration guide](https://docs.bugsnag.com/platforms/php/laravel/)
* Our [blog post about the new changes in Laravel 5.6](https://blog.bugsnag.com/laravel-5-6/)
* [Laravel 5.6 Logging documentation](https://laravel.com/docs/5.6/logging)
## 1.x to 2.x
*Our library has gone through some major improvements. The primary change to watch out for is we're no longer overriding your exception handler.*
Since we're no longer overriding your exception handler, you'll need to restore your original handler, and then see our [new integration guide](http://docs.bugsnag.com/platforms/php/laravel/) for how to bind our new logger to the container.
If you'd like access to all our new configuration, you'll need to re-publish our config file.

View File

@@ -11,18 +11,43 @@
}
],
"require": {
"php": ">=5.3.0",
"illuminate/support": "^4.0|^5.0",
"bugsnag/bugsnag": "^2.5"
"php": ">=5.5",
"bugsnag/bugsnag": "^3.29.0",
"bugsnag/bugsnag-psr-logger": "^1.4|^2.0",
"illuminate/contracts": "^5.0|^6.0|^7.0|^8.0|^9.0",
"illuminate/support": "^5.0|^6.0|^7.0|^8.0|^9.0",
"monolog/monolog": "^1.12|^2.0|^3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.0"
"orchestra/testbench": "^3.1|^4.0|^5.0|^6.0|^7.0",
"phpunit/phpunit": "^4.8.36|^6.3.1|^7.5.15|^8.3.5|^9.3.10"
},
"autoload": {
"psr-0": {
"psr-4": {
"Bugsnag\\BugsnagLaravel\\": "src/"
}
},
"autoload-dev": {
"psr-4" : {
"Bugsnag\\BugsnagLaravel\\Tests\\" : "tests/"
},
"files": [
"tests/bc.php"
]
},
"extra": {
"branch-alias": {
"dev-master": "2.18-dev"
}
},
"scripts": {
"test": "phpunit"
},
"minimum-stability": "dev",
"prefer-stable": true
"prefer-stable": true,
"config": {
"allow-plugins": {
"kylekatarnls/update-helper": false
}
}
}

View File

@@ -0,0 +1,364 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| API Key
|--------------------------------------------------------------------------
|
| You can find your API key on your Bugsnag dashboard.
|
| This api key points the Bugsnag notifier to the project in your account
| which should receive your application's uncaught exceptions.
|
*/
'api_key' => env('BUGSNAG_API_KEY', ''),
/*
|--------------------------------------------------------------------------
| App Type
|--------------------------------------------------------------------------
|
| Set the type of application executing the current code.
|
*/
'app_type' => env('BUGSNAG_APP_TYPE'),
/*
|--------------------------------------------------------------------------
| App Version
|--------------------------------------------------------------------------
|
| Set the version of application executing the current code.
|
*/
'app_version' => env('BUGSNAG_APP_VERSION'),
/*
|--------------------------------------------------------------------------
| Batch Sending
|--------------------------------------------------------------------------
|
| Set to true to send the errors through to Bugsnag when the PHP process
| shuts down, in order to prevent your app waiting on HTTP requests.
|
| Setting this to false will send an HTTP request straight away for each
| error.
|
*/
'batch_sending' => env('BUGSNAG_BATCH_SENDING'),
/*
|--------------------------------------------------------------------------
| Endpoint
|--------------------------------------------------------------------------
|
| Set what server the Bugsnag notifier should send errors to. By default
| this is set to 'https://notify.bugsnag.com', but for Bugsnag Enterprise
| this should be the URL to your Bugsnag instance.
|
*/
'endpoint' => env('BUGSNAG_ENDPOINT'),
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Use this if you want to ensure you don't send sensitive data such as
| passwords, and credit card numbers to our servers. Any keys which
| contain these strings will be filtered.
|
| This option has been deprecated in favour of 'redacted_keys'
|
*/
'filters' => empty(env('BUGSNAG_FILTERS')) ? null : explode(',', str_replace(' ', '', env('BUGSNAG_FILTERS'))),
/*
|--------------------------------------------------------------------------
| Hostname
|--------------------------------------------------------------------------
|
| You can set the hostname of your server to something specific for you to
| identify it by if needed.
|
*/
'hostname' => env('BUGSNAG_HOSTNAME'),
/*
|--------------------------------------------------------------------------
| Proxy
|--------------------------------------------------------------------------
|
| This is where you can set the proxy settings you'd like us to use when
| communicating with Bugsnag when reporting errors.
|
*/
'proxy' => array_filter([
'http' => env('HTTP_PROXY'),
'https' => env('HTTPS_PROXY'),
'no' => empty(env('NO_PROXY')) ? null : explode(',', str_replace(' ', '', env('NO_PROXY'))),
]),
/*
|--------------------------------------------------------------------------
| Project Root
|--------------------------------------------------------------------------
|
| Bugsnag marks stacktrace lines as in-project if they come from files
| inside your “project root”. You can set this here.
|
| If this is not set, we will automatically try to detect it.
|
*/
'project_root' => env('BUGSNAG_PROJECT_ROOT'),
/*
|--------------------------------------------------------------------------
| Project Root Regex
|--------------------------------------------------------------------------
|
| Bugsnag marks stacktrace lines as in-project if they come from files
| inside your “project root”. You can set this here.
|
| This option allows you to set it as a regular expression and will take
| precedence over "project_root" if both are defined.
|
*/
'project_root_regex' => env('BUGSNAG_PROJECT_ROOT_REGEX'),
/*
|--------------------------------------------------------------------------
| Strip Path
|--------------------------------------------------------------------------
|
| The strip path is a path to be trimmed from the start of any filepaths in
| your stacktraces.
|
| If this is not set, we will automatically try to detect it.
|
*/
'strip_path' => env('BUGSNAG_STRIP_PATH'),
/*
|--------------------------------------------------------------------------
| Strip Path Regex
|--------------------------------------------------------------------------
|
| The strip path is a path to be trimmed from the start of any filepaths in
| your stacktraces.
|
| This option allows you to set it as a regular expression and will take
| precedence over "strip_path" if both are defined.
|
*/
'strip_path_regex' => env('BUGSNAG_STRIP_PATH_REGEX'),
/*
|--------------------------------------------------------------------------
| Query
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to automatically record all queries executed
| as breadcrumbs.
|
*/
'query' => env('BUGSNAG_QUERY', true),
/*
|--------------------------------------------------------------------------
| Bindings
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to include the query bindings in our query
| breadcrumbs.
|
*/
'bindings' => env('BUGSNAG_QUERY_BINDINGS', false),
/*
|--------------------------------------------------------------------------
| Release Stage
|--------------------------------------------------------------------------
|
| Set the release stage to use when sending notifications to Bugsnag.
|
| Leaving this unset will default to using the application environment.
|
*/
'release_stage' => env('BUGSNAG_RELEASE_STAGE'),
/*
|--------------------------------------------------------------------------
| Notify Release Stages
|--------------------------------------------------------------------------
|
| Set which release stages should send notifications to Bugsnag.
|
*/
'notify_release_stages' => empty(env('BUGSNAG_NOTIFY_RELEASE_STAGES')) ? null : explode(',', str_replace(' ', '', env('BUGSNAG_NOTIFY_RELEASE_STAGES'))),
/*
|--------------------------------------------------------------------------
| Send Code
|--------------------------------------------------------------------------
|
| Bugsnag automatically sends a small snippet of the code that crashed to
| help you diagnose even faster from within your dashboard. If you dont
| want to send this snippet, then set this to false.
|
*/
'send_code' => env('BUGSNAG_SEND_CODE', true),
/*
|--------------------------------------------------------------------------
| Callbacks
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to enable our default set of notification
| callbacks. These add things like the cookie information and session
| details to the error to be sent to Bugsnag.
|
| If you'd like to add your own callbacks, you can call the
| Bugsnag::registerCallback method from the boot method of your app
| service provider.
|
*/
'callbacks' => env('BUGSNAG_CALLBACKS', true),
/*
|--------------------------------------------------------------------------
| User
|--------------------------------------------------------------------------
|
| Enable this if you'd like us to set the current user logged in via
| Laravel's authentication system.
|
| If you'd like to add your own user resolver, you can do this by using
| callbacks via Bugsnag::registerCallback.
|
*/
'user' => env('BUGSNAG_USER', true),
/*
|--------------------------------------------------------------------------
| Logger Notify Level
|--------------------------------------------------------------------------
|
| This sets the level at which a logged message will trigger a notification
| to Bugsnag. By default this level will be 'notice'.
|
| Must be one of the Psr\Log\LogLevel levels from the Psr specification.
|
*/
'logger_notify_level' => env('BUGSNAG_LOGGER_LEVEL'),
/*
|--------------------------------------------------------------------------
| Auto Capture Sessions
|--------------------------------------------------------------------------
|
| Enable this to start tracking sessions and deliver them to Bugsnag.
|
*/
'auto_capture_sessions' => env('BUGSNAG_CAPTURE_SESSIONS', false),
/*
|--------------------------------------------------------------------------
| Sessions Endpoint
|--------------------------------------------------------------------------
|
| Sets a url to send tracked sessions to.
|
*/
'session_endpoint' => env('BUGSNAG_SESSION_ENDPOINT'),
/*
|--------------------------------------------------------------------------
| Builds Endpoint
|--------------------------------------------------------------------------
|
| Sets a url to send build reports to.
|
*/
'build_endpoint' => env('BUGSNAG_BUILD_ENDPOINT'),
/*
|--------------------------------------------------------------------------
| Discard Classes
|--------------------------------------------------------------------------
|
| An array of classes that should not be sent to Bugsnag.
|
| This can contain both fully qualified class names and regular expressions.
|
*/
'discard_classes' => empty(env('BUGSNAG_DISCARD_CLASSES')) ? null : explode(',', env('BUGSNAG_DISCARD_CLASSES')),
/*
|--------------------------------------------------------------------------
| Redacted Keys
|--------------------------------------------------------------------------
|
| An array of metadata keys that should be redacted.
|
*/
'redacted_keys' => empty(env('BUGSNAG_REDACTED_KEYS')) ? null : explode(',', env('BUGSNAG_REDACTED_KEYS')),
/*
|--------------------------------------------------------------------------
| Feature flags
|--------------------------------------------------------------------------
|
| An array of feature flags to add to all reports.
|
| Each element in the array must have a "name" key and can optionally have a
| "variant" key, for example:
|
| [
| ['name' => 'example without a variant'],
| ['name' => 'example with a variant', 'variant' => 'example of a variant'],
| ]
|
*/
'feature_flags' => [],
/*
|--------------------------------------------------------------------------
| Max breadcrumbs
|--------------------------------------------------------------------------
|
| The maximum number of breadcrumbs to send with a report.
|
| This should be an integer between 0-100 (inclusive).
*/
'max_breadcrumbs' => null,
];

View File

@@ -1,11 +0,0 @@
{
"providers": [
"Bugsnag\BugsnagLaravel\BugsnagLaravelServiceProvider"
],
"aliases": [
{
"alias": "Bugsnag",
"facade": "Bugsnag\BugsnagLaravel\BugsnagFacade"
}
]
}

View File

@@ -1,27 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class BugsnagExceptionHandler extends ExceptionHandler
{
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
*
* @return void
*/
public function report(Exception $e)
{
if ($this->shouldReport($e) && app()->bound('bugsnag')) {
app('bugsnag')->notifyException($e, null, 'error');
}
return parent::report($e);
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Illuminate\Support\Facades\Facade;
class BugsnagFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'bugsnag';
}
}

View File

@@ -1,115 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Illuminate\Support\ServiceProvider;
class BugsnagLaravelServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
$app = $this->app;
if (version_compare($app::VERSION, '5.0') < 0) {
$this->package('bugsnag/bugsnag-laravel', 'bugsnag');
// Register for exception handling
$app->error(function (\Exception $exception) use ($app) {
if ('Symfony\Component\Debug\Exception\FatalErrorException'
!== get_class($exception)
) {
$app['bugsnag']->notifyException($exception, null, 'error');
}
});
// Register for fatal error handling
$app->fatal(function ($exception) use ($app) {
$app['bugsnag']->notifyException($exception, null, 'error');
});
} else {
$this->publishes(array(
__DIR__.'/config.php' => config_path('bugsnag.php'),
), 'config');
}
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('bugsnag', function ($app) {
$config = isset($app['config']['services']['bugsnag']) ? $app['config']['services']['bugsnag'] : null;
if (is_null($config)) {
$config = $app['config']['bugsnag'] ?: $app['config']['bugsnag::config'];
}
$client = new \Bugsnag_Client($config['api_key']);
$client->setStripPath(base_path());
$client->setProjectRoot(app_path());
$client->setAutoNotify(false);
$client->setBatchSending(false);
$client->setReleaseStage($app->environment());
$client->setNotifier(array(
'name' => 'Bugsnag Laravel',
'version' => '1.7.0',
'url' => 'https://github.com/bugsnag/bugsnag-laravel',
));
if (isset($config['notify_release_stages']) && is_array($config['notify_release_stages'])) {
$client->setNotifyReleaseStages($config['notify_release_stages']);
}
if (isset($config['endpoint'])) {
$client->setEndpoint($config['endpoint']);
}
if (isset($config['filters']) && is_array($config['filters'])) {
$client->setFilters($config['filters']);
}
if (isset($config['proxy']) && is_array($config['proxy'])) {
$client->setProxySettings($config['proxy']);
}
// Check if someone is logged in.
try {
if ($app['auth']->check()) {
// User is logged in.
$user = $app['auth']->user();
// If these attributes are available: pass them on.
$client->setUser(array('id' => $user->getAuthIdentifier()));
}
} catch (\Exception $e) {
// Do nothing.
}
return $client;
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array('bugsnag');
}
}

View File

@@ -1,93 +0,0 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Illuminate\Support\ServiceProvider;
class BugsnagLumenServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Bootstrap the application events.
*
* @return void
*/
public function boot()
{
$this->app->configure('bugsnag');
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('bugsnag', function ($app) {
$config = isset($app['config']['services']['bugsnag']) ? $app['config']['services']['bugsnag'] : null;
if (is_null($config)) {
$config = $app['config']['bugsnag'] ?: $app['config']['bugsnag::config'];
}
$client = new \Bugsnag_Client($config['api_key']);
$client->setStripPath(base_path());
$client->setProjectRoot(base_path().'/app');
$client->setAutoNotify(false);
$client->setBatchSending(false);
$client->setReleaseStage($app->environment());
$client->setNotifier(array(
'name' => 'Bugsnag Lumen',
'version' => '1.7.0',
'url' => 'https://github.com/bugsnag/bugsnag-laravel',
));
if (isset($config['notify_release_stages']) && is_array($config['notify_release_stages'])) {
$client->setNotifyReleaseStages($config['notify_release_stages']);
}
if (isset($config['endpoint'])) {
$client->setEndpoint($config['endpoint']);
}
if (isset($config['filters']) && is_array($config['filters'])) {
$client->setFilters($config['filters']);
}
if (isset($config['proxy']) && is_array($config['proxy'])) {
$client->setProxySettings($config['proxy']);
}
// Check if someone is logged in.
try {
if ($app['auth']->check()) {
// User is logged in.
$user = $app['auth']->user();
// If these attributes are available: pass them on.
$client->setUser(array('id' => $user->getAuthIdentifier()));
}
} catch (\Exception $e) {
// Do nothing.
}
return $client;
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array('bugsnag');
}
}

View File

@@ -1,77 +0,0 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| API Key
|--------------------------------------------------------------------------
|
| You can find your API key on your Bugsnag dashboard.
|
| This api key points the Bugsnag notifier to the project in your account
| which should receive your application's uncaught exceptions.
|
*/
'api_key' => env('BUGSNAG_API_KEY', ''),
/*
|--------------------------------------------------------------------------
| Notify Release Stages
|--------------------------------------------------------------------------
|
| Set which release stages should send notifications to Bugsnag.
|
| Example: array('development', 'production')
|
*/
'notify_release_stages' => env('BUGSNAG_NOTIFY_RELEASE_STAGES', null),
/*
|--------------------------------------------------------------------------
| Endpoint
|--------------------------------------------------------------------------
|
| Set what server the Bugsnag notifier should send errors to. By default
| this is set to 'https://notify.bugsnag.com', but for Bugsnag Enterprise
| this should be the URL to your Bugsnag instance.
|
*/
'endpoint' => env('BUGSNAG_ENDPOINT', null),
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Use this if you want to ensure you don't send sensitive data such as
| passwords, and credit card numbers to our servers. Any keys which
| contain these strings will be filtered.
|
*/
'filters' => env('BUGSNAG_FILTERS', array('password')),
/*
|--------------------------------------------------------------------------
| Proxy
|--------------------------------------------------------------------------
|
| If your server is behind a proxy server, you can configure this as well.
| Other than the host, none of these settings are mandatory.
|
| Note: Proxy configuration is only possible if the PHP cURL extension
| is installed.
|
| Example:
|
| 'proxy' => array(
| 'host' => 'bugsnag.com',
| 'port' => 42,
| 'user' => 'username',
| 'password' => 'password123'
| )
|
*/
'proxy' => env('BUGSNAG_PROXY', null),
);

View File

@@ -0,0 +1,490 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Bugsnag\Breadcrumbs\Breadcrumb;
use Bugsnag\BugsnagLaravel\Middleware\UnhandledState;
use Bugsnag\BugsnagLaravel\Queue\Tracker;
use Bugsnag\BugsnagLaravel\Request\LaravelResolver;
use Bugsnag\Callbacks\CustomUser;
use Bugsnag\Client;
use Bugsnag\Configuration;
use Bugsnag\FeatureFlag;
use Bugsnag\PsrLogger\BugsnagLogger;
use Bugsnag\PsrLogger\MultiLogger as BaseMultiLogger;
use Bugsnag\Report;
use DateTime;
use Illuminate\Auth\GenericUser;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Logging\Log;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Foundation\Application as LaravelApplication;
use Illuminate\Log\LogManager;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\QueueManager;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\ServiceProvider;
use Laravel\Lumen\Application as LumenApplication;
use Monolog\Handler\PsrHandler;
use Monolog\Logger;
use ReflectionClass;
class BugsnagServiceProvider extends ServiceProvider
{
/**
* The package version.
*
* @var string
*/
const VERSION = '2.25.0';
/**
* Boot the service provider.
*
* @return void
*/
public function boot()
{
$this->setupConfig($this->app);
$this->setupEvents($this->app->events, $this->app->config->get('bugsnag'));
$this->setupQueue($this->app->queue);
// Load the Client instance up-front if the OOM bootstrapper has been
// loaded. This avoids the possibility of initialising during an OOM,
// which can take a non-trivial amount of memory
if (class_exists(OomBootstrapper::class, false) && !$this->app->runningUnitTests()) {
$this->app->make('bugsnag');
}
}
/**
* Setup the config.
*
* @param \Illuminate\Contracts\Container\Container $app
*
* @return void
*/
protected function setupConfig(Container $app)
{
$source = realpath($raw = __DIR__.'/../config/bugsnag.php') ?: $raw;
if ($app instanceof LaravelApplication && $app->runningInConsole()) {
$this->publishes([$source => config_path('bugsnag.php')]);
} elseif ($app instanceof LumenApplication) {
$app->configure('bugsnag');
}
$this->mergeConfigFrom($source, 'bugsnag');
}
/**
* Setup the events.
*
* @param \Illuminate\Contracts\Events\Dispatcher $events
* @param array $config
*
* @return void
*/
protected function setupEvents(Dispatcher $events, array $config)
{
if ($this->isSessionTrackingAllowed($config)) {
$events->listen(RouteMatched::class, function ($event) {
$this->app->bugsnag->getSessionTracker()->startSession();
});
}
if (isset($config['query']) && !$config['query']) {
return;
}
$show = isset($config['bindings']) && $config['bindings'];
if (class_exists(QueryExecuted::class)) {
$events->listen(QueryExecuted::class, function (QueryExecuted $query) use ($show) {
$this->app->bugsnag->leaveBreadcrumb(
'Query executed',
Breadcrumb::PROCESS_TYPE,
$this->formatQuery($query->sql, $show ? $query->bindings : [], $query->time, $query->connectionName)
);
});
} else {
$events->listen('illuminate.query', function ($sql, array $bindings, $time, $connection) use ($show) {
$this->app->bugsnag->leaveBreadcrumb(
'Query executed',
Breadcrumb::PROCESS_TYPE,
$this->formatQuery($sql, $show ? $bindings : [], $time, $connection)
);
});
}
}
/**
* Format the query as breadcrumb metadata.
*
* @param string $sql
* @param array $bindings
* @param float $time
* @param string $connection
*
* @return array
*/
protected function formatQuery($sql, array $bindings, $time, $connection)
{
$data = ['sql' => $sql];
foreach ($bindings as $index => $binding) {
$data["binding {$index}"] = $binding;
}
$data['time'] = "{$time}ms";
$data['connection'] = $connection;
return $data;
}
/**
* Setup the queue.
*
* @param \Illuminate\Queue\QueueManager $queue
*
* @return void
*/
protected function setupQueue(QueueManager $queue)
{
$queue->looping(function () {
$this->app->bugsnag->flush();
$this->app->bugsnag->clearBreadcrumbs();
$this->app->make(Tracker::class)->clear();
});
if (!class_exists(JobProcessing::class)) {
return;
}
$queue->before(function (JobProcessing $event) {
$this->app->bugsnag->setFallbackType('Queue');
$job = [
'name' => $event->job->getName(),
'queue' => $event->job->getQueue(),
'attempts' => $event->job->attempts(),
'connection' => $event->connectionName,
];
if (method_exists($event->job, 'resolveName')) {
$job['resolved'] = $event->job->resolveName();
}
$this->app->make(Tracker::class)->set($job);
});
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('bugsnag', function (Container $app) {
$config = $app->config->get('bugsnag');
$client = new Client(new Configuration($config['api_key']), new LaravelResolver($app), $this->getGuzzle($config));
$this->setupCallbacks($client, $app, $config);
$this->setupPaths($client, $app, $config);
$client->setReleaseStage(isset($config['release_stage']) ? $config['release_stage'] : $app->environment());
$client->setHostname(isset($config['hostname']) ? $config['hostname'] : null);
$client->getConfig()->mergeDeviceData(['runtimeVersions' => $this->getRuntimeVersion()]);
$client->setFallbackType($app->runningInConsole() ? 'Console' : 'HTTP');
$client->setAppType(isset($config['app_type']) ? $config['app_type'] : null);
$client->setAppVersion(isset($config['app_version']) ? $config['app_version'] : null);
$client->setBatchSending(isset($config['batch_sending']) ? $config['batch_sending'] : true);
$client->setSendCode(isset($config['send_code']) ? $config['send_code'] : true);
$client->getPipeline()->insertBefore(new UnhandledState(), 'Bugsnag\\Middleware\\SessionData');
$client->setNotifier([
'name' => 'Bugsnag Laravel',
'version' => static::VERSION,
'url' => 'https://github.com/bugsnag/bugsnag-laravel',
]);
if (isset($config['notify_release_stages']) && is_array($config['notify_release_stages'])) {
$client->setNotifyReleaseStages($config['notify_release_stages']);
}
if (isset($config['filters']) && is_array($config['filters'])) {
$client->setFilters($config['filters']);
}
if (isset($config['endpoint'])) {
$client->setNotifyEndpoint($config['endpoint']);
}
if ($this->isSessionTrackingAllowed($config)) {
$endpoint = isset($config['session_endpoint']) ? $config['session_endpoint'] : null;
$this->setupSessionTracking($client, $endpoint, $this->app->events);
}
if (isset($config['build_endpoint'])) {
$client->setBuildEndpoint($config['build_endpoint']);
}
if (array_key_exists('memory_limit_increase', $config)) {
$client->setMemoryLimitIncrease($config['memory_limit_increase']);
}
if (isset($config['discard_classes']) && is_array($config['discard_classes'])) {
$client->setDiscardClasses($config['discard_classes']);
}
if (isset($config['redacted_keys']) && is_array($config['redacted_keys'])) {
$client->setRedactedKeys($config['redacted_keys']);
}
if (isset($config['feature_flags']) && is_array($config['feature_flags']) && $config['feature_flags'] !== []) {
$featureFlags = [];
foreach ($config['feature_flags'] as $flag) {
if (!is_array($flag) || !array_key_exists('name', $flag)) {
continue;
}
if (array_key_exists('variant', $flag)) {
$featureFlags[] = new FeatureFlag($flag['name'], $flag['variant']);
} else {
$featureFlags[] = new FeatureFlag($flag['name']);
}
}
$client->addFeatureFlags($featureFlags);
}
if (isset($config['max_breadcrumbs'])) {
$client->setMaxBreadcrumbs($config['max_breadcrumbs']);
}
return $client;
});
$this->app->singleton('bugsnag.tracker', function () {
return new Tracker();
});
$this->app->singleton('bugsnag.logger', function (Container $app) {
$config = $app->config->get('bugsnag');
$logger = interface_exists(Log::class) ? new LaravelLogger($app['bugsnag'], $app['events']) : new BugsnagLogger($app['bugsnag']);
if (isset($config['logger_notify_level'])) {
$logger->setNotifyLevel($config['logger_notify_level']);
}
return $logger;
});
$this->app->singleton('bugsnag.multi', function (Container $app) {
return interface_exists(Log::class) ? new MultiLogger([$app['log'], $app['bugsnag.logger']]) : new BaseMultiLogger([$app['log'], $app['bugsnag.logger']]);
});
if ($this->app['log'] instanceof LogManager) {
$this->app['log']->extend('bugsnag', function (Container $app, array $config) {
$handler = new PsrHandler($app['bugsnag.logger']);
return new Logger('bugsnag', [$handler]);
});
}
$this->app->alias('bugsnag', Client::class);
$this->app->alias('bugsnag.tracker', Tracker::class);
$this->app->alias('bugsnag.logger', interface_exists(Log::class) ? LaravelLogger::class : BugsnagLogger::class);
$this->app->alias('bugsnag.multi', interface_exists(Log::class) ? MultiLogger::class : BaseMultiLogger::class);
}
/**
* Get the guzzle client instance.
*
* @param array $config
*
* @return \GuzzleHttp\ClientInterface
*/
protected function getGuzzle(array $config)
{
// If a 'bugsnag.guzzle' instance exists in the container, use it
if ($this->app->bound('bugsnag.guzzle')) {
return $this->app->make('bugsnag.guzzle');
}
$options = [];
if (isset($config['proxy']) && $config['proxy']) {
if (isset($config['proxy']['http']) && php_sapi_name() != 'cli') {
unset($config['proxy']['http']);
}
$options['proxy'] = $config['proxy'];
}
return Client::makeGuzzle(null, $options);
}
/**
* Setup the callbacks.
*
* @param \Bugsnag\Client $client
* @param \Illuminate\Contracts\Container\Container $app
* @param array $config
*
* @return void
*/
protected function setupCallbacks(Client $client, Container $app, array $config)
{
if (!isset($config['callbacks']) || $config['callbacks']) {
$client->registerDefaultCallbacks();
$client->registerCallback(function (Report $report) use ($app) {
$tracker = $app->make(Tracker::class);
if ($context = $tracker->context()) {
$report->setContext($context);
}
if ($job = $tracker->get()) {
$report->setMetaData(['job' => $job]);
}
});
}
if (!isset($config['user']) || $config['user']) {
$client->registerCallback(new CustomUser(function () use ($app) {
if ($user = $app->auth->user()) {
if (method_exists($user, 'attributesToArray') && is_callable([$user, 'attributesToArray'])) {
return $user->attributesToArray();
}
if ($user instanceof GenericUser) {
$reflection = new ReflectionClass($user);
$property = $reflection->getProperty('attributes');
$property->setAccessible(true);
return $property->getValue($user);
}
}
}));
}
}
/**
* Setup the client paths.
*
* @param \Bugsnag\Client $client
* @param \Illuminate\Contracts\Container\Container $app
* @param array $config
*
* @return void
*/
protected function setupPaths(Client $client, Container $app, array $config)
{
if (isset($config['project_root_regex'])) {
$client->setProjectRootRegex($config['project_root_regex']);
} elseif (isset($config['project_root'])) {
$client->setProjectRoot($config['project_root']);
} else {
$client->setProjectRoot($app->path());
}
if (isset($config['strip_path_regex'])) {
$client->setStripPathRegex($config['strip_path_regex']);
} elseif (isset($config['strip_path'])) {
$client->setStripPath($config['strip_path']);
} else {
$client->setStripPath($app->basePath());
}
}
/**
* Setup session tracking.
*
* @param \Bugsnag\Client $client
* @param string $endpoint
*
* @return void
*/
protected function setupSessionTracking(Client $client, $endpoint, $events)
{
$client->setAutoCaptureSessions(true);
if (!is_null($endpoint)) {
$client->setSessionEndpoint($endpoint);
}
$sessionTracker = $client->getSessionTracker();
$sessionStorage = function ($session = null) {
if (is_null($session)) {
return session('bugsnag-session', []);
} else {
session(['bugsnag-session' => $session]);
}
};
$sessionTracker->setSessionFunction($sessionStorage);
$cache = $this->app->cache;
$genericStorage = function ($key, $value = null) use ($cache) {
if (is_null($value)) {
return $cache->get($key, null);
} else {
$cache->put($key, $value, new DateTime('+ 1 hour'));
}
};
$sessionTracker->setStorageFunction($genericStorage);
}
/**
* Returns the framework name and version to add to the device data.
*
* Attempt to parse a semantic framework version from $app or else return
* the full version string.
* e.g. Lumen: "Lumen (x.x.x) (Laravel Components y.y.*)" => "x.x.x"
*
* @return array
*/
protected function getRuntimeVersion()
{
$version = $this->app->version();
if (preg_match('/(\d+\.\d+\.\d+)/', $version, $versionMatches)) {
$version = $versionMatches[0];
}
return [($this->app instanceof LumenApplication ? 'lumen' : 'laravel') => $version];
}
/**
* Tests whether session tracking can/should be enabled.
*
* @param array $config The configuration array
*
* @return bool true if session tracking should be enabled.
*/
protected function isSessionTrackingAllowed($config)
{
// Session support removed in Lumen 5.3 - only setup automatic session
// tracking if the session function is avaiable
return isset($config['auto_capture_sessions'])
&& $config['auto_capture_sessions']
&& function_exists('session');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['bugsnag', 'bugsnag.tracker', 'bugsnag.logger', 'bugsnag.multi'];
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Bugsnag\BugsnagLaravel\Commands;
use Bugsnag\BugsnagLaravel\Facades\Bugsnag;
use Bugsnag\Utils;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
class DeployCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'bugsnag:deploy';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Notifies Bugsnag of a build';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
Bugsnag::build(
$this->option('repository'),
$this->option('revision'),
$this->option('provider'),
$this->option('builder') ?: Utils::getBuilderName()
);
$this->info('Notified Bugsnag of the build!');
}
/**
* Execute the console command.
*
* @return void
*/
public function fire()
{
$this->handle();
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['repository', null, InputOption::VALUE_OPTIONAL, 'The repository from which you are deploying the code.', null],
['branch', null, InputOption::VALUE_OPTIONAL, 'The source control branch from which you are deploying. Deprecated.', null],
['revision', null, InputOption::VALUE_OPTIONAL, 'The source control revision you are currently deploying.', null],
['provider', null, InputOption::VALUE_OPTIONAL, 'The provider of your source control repository.', null],
['builder', null, InputOption::VALUE_OPTIONAL, 'The machine or person who has executed the build', null],
];
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Closure;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Log\Events\MessageLogged;
use RuntimeException;
trait EventTrait
{
/**
* The event dispatcher instance.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $dispatcher;
/**
* Get the event dispatcher instance.
*
* @return \Illuminate\Contracts\Events\Dispatcher
*/
public function getEventDispatcher()
{
return $this->dispatcher;
}
/**
* Set the event dispatcher instance.
*
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
*
* @return void
*/
public function setEventDispatcher(Dispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Register a new callback handler for when a log event is triggered.
*
* @param \Closure $callback
*
* @throws \RuntimeException
*
* @return void
*/
public function listen(Closure $callback)
{
if (!isset($this->dispatcher)) {
throw new RuntimeException('Events dispatcher has not been set.');
}
$this->dispatcher->listen(class_exists(MessageLogged::class) ? MessageLogged::class : 'illuminate.log', $callback);
}
/**
* Log a message to the logs.
*
* @param string $level
* @param mixed $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = [])
{
parent::log($level, $message, $context);
$this->fireLogEvent($level, $message, $context);
}
/**
* Fires a log event.
*
* @param string $level
* @param string $message
* @param array $context
*
* @return void
*/
protected function fireLogEvent($level, $message, array $context = [])
{
// If the event dispatcher is set, we will pass along the parameters to the
// log listeners. These are useful for building profilers or other tools
// that aggregate all of the log messages for a given "request" cycle.
if (!isset($this->dispatcher)) {
return;
}
if (class_exists(MessageLogged::class)) {
$this->dispatcher->dispatch(new MessageLogged($level, $message, $context));
} else {
$this->dispatcher->fire('illuminate.log', compact('level', 'message', 'context'));
}
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace Bugsnag\BugsnagLaravel\Facades;
use Illuminate\Support\Facades\Facade;
/**
* @method static void build(string|null $repository = null, string|null $revision = null, string|null $provider = null, string|null $builderName = null)
* @method static void clearBreadcrumbs()
* @method static void flush()
* @method static string getApiKey()
* @method static array getAppData()
* @method static string getBuildEndpoint()
* @method static \Bugsnag\Configuration getConfig()
* @method static array getDeviceData()
* @method static array getDiscardClasses()
* @method static array getFilters()
* @method static int|null getMemoryLimitIncrease()
* @method static array getMetaData()
* @method static array getNotifier()
* @method static string getNotifyEndpoint()
* @method static \Bugsnag\Pipeline getPipeline()
* @method static array getRedactedKeys()
* @method static string getSessionEndpoint()
* @method static \Bugsnag\SessionTracker getSessionTracker()
* @method static string getStrippedFilePath(string $file)
* @method static bool isBatchSending()
* @method static string isInProject(string $file)
* @method static void leaveBreadcrumb(string $name, string|null $type = null, array $metaData = [])
* @method static void notify(\Bugsnag\Report $report, callable|null $callback = null)
* @method static void notifyError(string $name, string $message, callable|null $callback = null)
* @method static void notifyException(\Throwable $throwable, callable|null $callback = null)
* @method static void registerCallback(callable $callback)
* @method static void registerDefaultCallbacks()
* @method static void registerMiddleware(callable $middleware)
* @method static \Bugsnag\Client setAppType(string|null $type)
* @method static \Bugsnag\Client setAppVersion(string|null $appVersion)
* @method static \Bugsnag\Client setAutoCaptureSessions(bool $track)
* @method static \Bugsnag\Client setBatchSending(bool $batchSending)
* @method static \Bugsnag\Client setBuildEndpoint(string $endpoint)
* @method static \Bugsnag\Client setDiscardClasses(array $discardClasses)
* @method static \Bugsnag\Client setErrorReportingLevel(int|null $errorReportingLevel)
* @method static \Bugsnag\Client setFallbackType(string|null $type)
* @method static \Bugsnag\Client setFilters(array $filters)
* @method static \Bugsnag\Client setHostname(string|null $hostname)
* @method static \Bugsnag\Client setMemoryLimitIncrease(int|null $value)
* @method static \Bugsnag\Client setMetaData(array $metaData, bool $merge = true)
* @method static \Bugsnag\Client setNotifier(array $notifier)
* @method static \Bugsnag\Client setNotifyEndpoint(string $endpoint)
* @method static \Bugsnag\Client setNotifyReleaseStages(array|null $notifyReleaseStages = null)
* @method static \Bugsnag\Client setProjectRoot(string|null $projectRoot)
* @method static \Bugsnag\Client setProjectRootRegex(string|null $projectRootRegex)
* @method static \Bugsnag\Client setReleaseStage(string|null $releaseStage)
* @method static \Bugsnag\Client setRedactedKeys(array $redactedKeys)
* @method static \Bugsnag\Client setSendCode(bool $sendCode)
* @method static \Bugsnag\Client setSessionEndpoint(string $endpoint)
* @method static \Bugsnag\Client setStripPath(string|null $stripPath)
* @method static \Bugsnag\Client setStripPathRegex(string|null $stripPathRegex)
* @method static bool shouldCaptureSessions()
* @method static bool shouldIgnoreErrorCode(int $code)
* @method static bool shouldNotify()
* @method static bool shouldSendCode()
* @method static void startSession()
*
* @see \Bugsnag\Client
*/
class Bugsnag extends Facade
{
protected static function getFacadeAccessor()
{
return 'bugsnag';
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace Bugsnag\BugsnagLaravel\Internal;
/**
* The criteria for an error to be unhandled in a Laravel or Lumen app is as
* follows.
*
* 1. All unhandled exceptions must pass through one of the `HANDLER_CLASSES`
* report method
* 2. Unhandled exceptions will have had a caller from inside a vendor namespace
* or the App exception handler
* 3. The above exception handler must have originally been called from
* within a vendor namespace
*/
final class BacktraceProcessor
{
/**
* Searching for a frame where the framework's error handler is called.
*/
const STATE_FRAMEWORK_HANDLER = 1;
/**
* Searching for a frame where the framework's error handler was called by
* the framework itself, or the user's exception handler.
*/
const STATE_HANDLER_CALLER = 2;
/**
* Deciding if the frame was unhandled.
*/
const STATE_IS_UNHANDLED = 3;
/**
* A state to signal that we're done processing frames and know if the error
* was unhandled.
*/
const STATE_DONE = 4;
/**
* Laravel and Lumen use different exception handlers which live in different
* namespaces.
*/
const LARAVEL_HANDLER_CLASS = \Illuminate\Foundation\Exceptions\Handler::class;
const LUMEN_HANDLER_CLASS = \Laravel\Lumen\Exceptions\Handler::class;
/**
* The method used by the x_HANDLER_CLASS to report errors.
*/
const HANDLER_METHOD = 'report';
/**
* Laravel uses the "Exception" namespace, Lumen uses the plural "Exceptions"
* for the default app exception handler.
*/
const LARAVEL_APP_EXCEPTION_HANDLER = \App\Exception\Handler::class;
const LUMEN_APP_EXCEPTION_HANDLER = \App\Exceptions\Handler::class;
/**
* Namespaces used by Laravel and Lumen so we can determine if code was
* called by the user's app or the framework itself.
*
* Note this is not a mistake - Lumen uses the 'Laravel' namespace but
* Laravel itself does not
*/
const LARAVEL_VENDOR_NAMESPACE = 'Illuminate\\';
const LUMEN_VENDOR_NAMESPACE = 'Laravel\\';
/**
* The current state; one of the self::STATE_ constants.
*
* @var int
*/
private $state = self::STATE_FRAMEWORK_HANDLER;
/**
* This flag will be set to 'true' if we determine the error was unhandled.
*
* @var bool
*/
private $unhandled = false;
/**
* A backtrace matching the format of PHP's 'debug_backtrace'.
*
* @var array
*/
private $backtrace;
/**
* @param array $backtrace
*/
public function __construct(array $backtrace)
{
$this->backtrace = $backtrace;
}
/**
* Determine if the backtrace was from an unhandled error.
*
* @return bool
*/
public function isUnhandled()
{
foreach ($this->backtrace as $frame) {
$this->processFrame($frame);
// stop iterating early if we know we're done
if ($this->state === self::STATE_DONE) {
break;
}
}
return $this->unhandled;
}
/**
* @param array $frame
*
* @return void
*/
private function processFrame(array $frame)
{
if (!isset($frame['class'])) {
return;
}
$class = $frame['class'];
switch ($this->state) {
case self::STATE_FRAMEWORK_HANDLER:
// if this class is a framework exception handler and the function
// matches self::HANDLER_METHOD, we can move on to searching for
// the caller
if (($class === self::LARAVEL_HANDLER_CLASS || $class === self::LUMEN_HANDLER_CLASS)
&& isset($frame['function'])
&& $frame['function'] === self::HANDLER_METHOD
) {
$this->state = self::STATE_HANDLER_CALLER;
}
break;
case self::STATE_HANDLER_CALLER:
// if this is an app exception handler or a framework class, we
// can move on to determine if this was unhandled or not
if ($class === self::LARAVEL_APP_EXCEPTION_HANDLER
|| $class === self::LUMEN_APP_EXCEPTION_HANDLER
|| $this->isVendor($class)
) {
$this->state = self::STATE_IS_UNHANDLED;
}
break;
case self::STATE_IS_UNHANDLED:
// we are only interested in running this once so move immediately
// into the "done" state. This ensures we only check the frame
// immediately before the caller of the exception handler
$this->state = self::STATE_DONE;
// if this class is internal to the framework then the exception
// was unhandled
if ($this->isVendor($class)) {
$this->unhandled = true;
}
break;
}
}
/**
* Does the given class belong to a vendor namespace?
*
* @see self::VENDOR_NAMESPACES
*
* @param string $class
*
* @return bool
*/
private function isVendor($class)
{
return substr($class, 0, strlen(self::LARAVEL_VENDOR_NAMESPACE)) === self::LARAVEL_VENDOR_NAMESPACE
|| substr($class, 0, strlen(self::LUMEN_VENDOR_NAMESPACE)) === self::LUMEN_VENDOR_NAMESPACE;
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Bugsnag\Client;
use Bugsnag\PsrLogger\BugsnagLogger;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Logging\Log;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
class LaravelLogger extends BugsnagLogger implements Log
{
use EventTrait;
/**
* Create a new laravel logger instance.
*
* @param \Bugsnag\Client $client
* @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher
*
* @return void
*/
public function __construct(Client $client, Dispatcher $dispatcher = null)
{
parent::__construct($client);
$this->dispatcher = $dispatcher;
}
/**
* Register a file log handler.
*
* @param string $path
* @param string $level
*
* @return void
*/
public function useFiles($path, $level = 'debug')
{
//
}
/**
* Register a daily file log handler.
*
* @param string $path
* @param int $days
* @param string $level
*
* @return void
*/
public function useDailyFiles($path, $days = 0, $level = 'debug')
{
//
}
/**
* Get the underlying Monolog instance.
*
* @return \Monolog\Logger
*/
public function getMonolog()
{
//
}
/**
* Format the parameters for the logger.
*
* @param mixed $message
*
* @return string
*/
protected function formatMessage($message)
{
if (is_array($message)) {
return var_export($message, true);
}
if ($message instanceof Jsonable) {
return $message->toJson();
}
if ($message instanceof Arrayable) {
return var_export($message->toArray(), true);
}
return $message;
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Bugsnag\BugsnagLaravel\Middleware;
use Bugsnag\BugsnagLaravel\Internal\BacktraceProcessor;
use Bugsnag\Report;
class UnhandledState
{
/**
* Execute the unhandled state middleware.
*
* @param \Bugsnag\Report $report the bugsnag report instance
* @param callable $next the next stage callback
*
* @return void
*/
public function __invoke(Report $report, callable $next)
{
$stackFrames = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
if (!is_array($stackFrames)) {
$stackFrames = [];
}
$backtraceProcessor = new BacktraceProcessor($stackFrames);
if ($backtraceProcessor->isUnhandled()) {
$report->setUnhandled(true);
$report->setSeverityReason([
'type' => 'unhandledExceptionMiddleware',
'attributes' => ['framework' => 'Laravel'],
]);
}
$next($report);
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Bugsnag\BugsnagLaravel;
use Bugsnag\PsrLogger\MultiLogger as BaseLogger;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Logging\Log;
class MultiLogger extends BaseLogger implements Log
{
use EventTrait;
/**
* Create a new multi logger instance.
*
* @param \Psr\Log\LoggerInterface[] $loggers
* @param \Illuminate\Contracts\Events\Dispatcher|null $dispatcher
*
* @return void
*/
public function __construct(array $loggers, Dispatcher $dispatcher = null)
{
parent::__construct($loggers);
$this->dispatcher = $dispatcher;
}
/**
* Register a file log handler.
*
* @param string $path
* @param string $level
*
* @return void
*/
public function useFiles($path, $level = 'debug')
{
foreach ($this->loggers as $logger) {
if ($logger instanceof Log) {
$logger->useFiles($path, $level);
}
}
}
/**
* Register a daily file log handler.
*
* @param string $path
* @param int $days
* @param string $level
*
* @return void
*/
public function useDailyFiles($path, $days = 0, $level = 'debug')
{
foreach ($this->loggers as $logger) {
if ($logger instanceof Log) {
$logger->useDailyFiles($path, $days, $level);
}
}
}
/**
* Get the underlying Monolog instance.
*
* @return \Monolog\Logger
*/
public function getMonolog()
{
foreach ($this->loggers as $logger) {
if (is_callable([$logger, 'getMonolog'])) {
$monolog = $logger->getMonolog();
if ($monolog === null) {
continue;
}
return $monolog;
}
}
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Bugsnag\BugsnagLaravel;
class OomBootstrapper
{
/**
* A bit of reserved memory to ensure we are able to increase the memory
* limit on an OOM.
*
* We can't reserve all of the memory that we need to send OOM reports
* because this would have a big overhead on every request, instead of just
* on shutdown in requests with errors.
*
* @var string|null
*/
private $reservedMemory;
/**
* A regex that matches PHP OOM errors.
*
* @var string
*/
private $oomRegex = '/^Allowed memory size of (\d+) bytes exhausted \(tried to allocate \d+ bytes\)/';
/**
* Allow Bugsnag to handle OOMs by registering a shutdown function that
* increases the memory limit. This must happen before Laravel's shutdown
* function is registered or it will have no effect.
*
* @return void
*/
public function bootstrap()
{
$this->reservedMemory = str_repeat(' ', 1024 * 256);
register_shutdown_function(function () {
$this->reservedMemory = null;
$lastError = error_get_last();
if (!$lastError) {
return;
}
$isOom = preg_match($this->oomRegex, $lastError['message'], $matches) === 1;
if (!$isOom) {
return;
}
/** @var \Bugsnag\Client|null $client */
$client = app('bugsnag');
// If the client exists and memory increase is enabled, bump the
// memory limit so we can report it. The client can be missing when
// the container isn't complete, e.g. when unit tests are running
if ($client && $client->getMemoryLimitIncrease() !== null) {
$currentMemoryLimit = (int) $matches[1];
ini_set('memory_limit', $currentMemoryLimit + $client->getMemoryLimitIncrease());
}
});
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Bugsnag\BugsnagLaravel\Queue;
class Tracker
{
/**
* The current job information.
*
* @var array|null
*/
protected $job;
/**
* Get the current context.
*
* @return string|null
*/
public function context()
{
if (isset($this->job['resolved'])) {
return $this->job['resolved'];
}
}
/**
* Get the current job information.
*
* @return array|null
*/
public function get()
{
return $this->job;
}
/**
* Set the current job information.
*
* @param array $job
*
* @return void
*/
public function set(array $job)
{
$this->job = $job;
}
/**
* Clear the current job information.
*
* @return void
*/
public function clear()
{
$this->job = null;
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Bugsnag\BugsnagLaravel\Request;
use Bugsnag\Request\RequestInterface;
use Exception;
use Illuminate\Http\Request;
class LaravelRequest implements RequestInterface
{
/**
* The illuminate request instance.
*
* @var \Illuminate\Http\Request
*/
protected $request;
/**
* Create a new laravel request instance.
*
* @param \Illuminate\Http\Request $request
*
* @return void
*/
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Are we currently processing a request?
*
* @return bool
*/
public function isRequest()
{
return true;
}
/**
* Get the session data.
*
* @return array
*/
public function getSession()
{
try {
$session = $this->request->getSession();
} catch (Exception $e) {
return [];
}
return $session ? $session->all() : [];
}
/**
* Get the cookies.
*
* @return array
*/
public function getCookies()
{
return $this->request->cookies->all();
}
/**
* Get the request formatted as meta data.
*
* @return array
*/
public function getMetaData()
{
$data = [];
$data['url'] = $this->request->fullUrl();
$data['httpMethod'] = $this->request->getMethod();
$data['params'] = $this->request->input();
$data['clientIp'] = $this->request->getClientIp();
if ($agent = $this->request->header('User-Agent')) {
$data['userAgent'] = $agent;
}
if ($headers = $this->request->headers->all()) {
$data['headers'] = $headers;
}
return ['request' => $data];
}
/**
* Get the request context.
*
* @return string|null
*/
public function getContext()
{
return $this->request->getMethod().' '.$this->request->getPathInfo();
}
/**
* Get the request user id.
*
* @return string|null
*/
public function getUserId()
{
return $this->request->getClientIp();
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Bugsnag\BugsnagLaravel\Request;
use Bugsnag\Request\ConsoleRequest;
use Bugsnag\Request\ResolverInterface;
use Illuminate\Contracts\Container\Container;
use Illuminate\Http\Request;
class LaravelResolver implements ResolverInterface
{
/**
* The application instance.
*
* @var \Illuminate\Contracts\Container\Container
*/
protected $app;
/**
* Create a new laravel request resolver instance.
*
* @param \Illuminate\Contracts\Container\Container $app
*
* @return void
*/
public function __construct(Container $app)
{
$this->app = $app;
}
/**
* Resolve the current request.
*
* @return \Bugsnag\Request\RequestInterface
*/
public function resolve()
{
$request = $this->app->make(Request::class);
if ($this->app->runningInConsole()) {
$command = $request->server('argv', []);
if (!is_array($command)) {
$command = explode(' ', $command);
}
return new ConsoleRequest($command);
}
return new LaravelRequest($request);
}
}

View File

@@ -1,77 +0,0 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| API Key
|--------------------------------------------------------------------------
|
| You can find your API key on your Bugsnag dashboard.
|
| This api key points the Bugsnag notifier to the project in your account
| which should receive your application's uncaught exceptions.
|
*/
'api_key' => 'YOUR-API-KEY-HERE',
/*
|--------------------------------------------------------------------------
| Notify Release Stages
|--------------------------------------------------------------------------
|
| Set which release stages should send notifications to Bugsnag.
|
| Example: array('development', 'production')
|
*/
'notify_release_stages' => null,
/*
|--------------------------------------------------------------------------
| Endpoint
|--------------------------------------------------------------------------
|
| Set what server the Bugsnag notifier should send errors to. By default
| this is set to 'https://notify.bugsnag.com', but for Bugsnag Enterprise
| this should be the URL to your Bugsnag instance.
|
*/
'endpoint' => null,
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Use this if you want to ensure you don't send sensitive data such as
| passwords, and credit card numbers to our servers. Any keys which
| contain these strings will be filtered.
|
*/
'filters' => array('password'),
/*
|--------------------------------------------------------------------------
| Proxy
|--------------------------------------------------------------------------
|
| If your server is behind a proxy server, you can configure this as well.
| Other than the host, none of these settings are mandatory.
|
| Note: Proxy configuration is only possible if the PHP cURL extension
| is installed.
|
| Example:
|
| 'proxy' => array(
| 'host' => 'bugsnag.com',
| 'port' => 42,
| 'user' => 'username',
| 'password' => 'password123'
| )
|
*/
'proxy' => null,
);

View File

@@ -0,0 +1,5 @@
disabled:
- align_double_arrow
enabled:
- unalign_double_arrow

View File

@@ -0,0 +1,20 @@
Copyright (c) 2016 Bugsnag
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,14 @@
test:
vendor/bin/phpunit
coverage:
vendor/bin/phpunit --coverage-html=build/coverage
coverage-show:
view-coverage
view-coverage:
open build/coverage/index.html
clean:
rm -rf build/*

View File

@@ -0,0 +1,40 @@
{
"name": "bugsnag/bugsnag-psr-logger",
"type": "library",
"description": "Official Bugsnag PHP PSR Logger.",
"keywords": ["bugsnag", "exceptions", "errors", "logging", "tracking", "psr"],
"homepage": "https://github.com/bugsnag/bugsnag-psr",
"license": "MIT",
"authors": [{
"name": "James Smith",
"email": "notifiers@bugsnag.com",
"homepage": "https://bugsnag.com"
}],
"require": {
"php": ">=5.5",
"bugsnag/bugsnag": "^3.10",
"psr/log": "^1.0|^2.0"
},
"require-dev": {
"graham-campbell/testbench-core": "^1.1",
"mockery/mockery": "^0.9.4|^1.3.1",
"phpunit/phpunit": "^4.8.36|^7.5.15|^9.4.3"
},
"autoload": {
"psr-4" : {
"Bugsnag\\PsrLogger\\" : "src/"
}
},
"autoload-dev": {
"psr-4" : {
"Bugsnag\\PsrLogger\\Tests\\" : "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Bugsnag\PsrLogger;
/**
* @deprecated Use \Psr\Log\AbstractLogger instead
*/
abstract class AbstractLogger extends \Psr\Log\AbstractLogger
{
}

View File

@@ -0,0 +1,203 @@
<?php
namespace Bugsnag\PsrLogger;
use Bugsnag\Client;
use Bugsnag\Report;
use Exception;
use Psr\Log\LogLevel;
use Throwable;
class BugsnagLogger extends AbstractLogger
{
/**
* The bugsnag client instance.
*
* @var \Bugsnag\Client
*/
protected $client;
/**
* The minimum level required to notify bugsnag.
*
* Logs underneath this level will be converted into breadcrumbs.
*
* @var string
*/
protected $notifyLevel = LogLevel::NOTICE;
/**
* Create a new bugsnag logger instance.
*
* @param \Bugsnag\Client $client
*
* @return void
*/
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* Set the notifyLevel of the logger, as defined in Psr\Log\LogLevel.
*
* @param string $notifyLevel
*
* @return void
*/
public function setNotifyLevel($notifyLevel)
{
if (!in_array($notifyLevel, $this->getLogLevelOrder())) {
syslog(LOG_WARNING, 'Bugsnag Warning: Invalid notify level supplied to Bugsnag Logger');
} else {
$this->notifyLevel = $notifyLevel;
}
}
/**
* Log a message to the logs.
*
* @param string $level
* @param mixed $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = [])
{
$title = 'Log '.$level;
if (isset($context['title'])) {
$title = $context['title'];
unset($context['title']);
}
$exception = null;
if (isset($context['exception']) && ($context['exception'] instanceof Exception || $context['exception'] instanceof Throwable)) {
$exception = $context['exception'];
unset($context['exception']);
} elseif ($message instanceof Exception || $message instanceof Throwable) {
$exception = $message;
}
// Below theshold, leave a breadcrumb but don't send a notification
if (!$this->aboveLevel($level, $this->notifyLevel)) {
if ($exception !== null) {
$title = get_class($exception);
$data = ['name' => $title, 'message' => $exception->getMessage()];
} else {
$data = ['message' => $message];
}
$metaData = array_merge($data, $context);
$this->client->leaveBreadcrumb($title, 'log', array_filter($metaData));
return;
}
$severityReason = [
'type' => 'log',
'attributes' => [
'level' => $level,
],
];
if ($exception !== null) {
$report = Report::fromPHPThrowable($this->client->getConfig(), $exception);
} else {
$report = Report::fromNamedError($this->client->getConfig(), $title, $this->formatMessage($message));
}
$report->setMetaData($context);
$report->setSeverity($this->getSeverity($level));
$report->setSeverityReason($severityReason);
$this->client->notify($report);
}
/**
* Checks whether the selected level is above another level.
*
* @param string $level
* @param string $base
*
* @return bool
*/
protected function aboveLevel($level, $base)
{
$levelOrder = $this->getLogLevelOrder();
$baseIndex = array_search($base, $levelOrder);
$levelIndex = array_search($level, $levelOrder);
return $levelIndex >= $baseIndex;
}
/**
* Returns the log levels in order.
*
* @return string[]
*/
protected function getLogLevelOrder()
{
return [
LogLevel::DEBUG,
LogLevel::INFO,
LogLevel::NOTICE,
LogLevel::WARNING,
LogLevel::ERROR,
LogLevel::CRITICAL,
LogLevel::ALERT,
LogLevel::EMERGENCY,
];
}
/**
* Get the severity for the logger.
*
* @param string $level
*
* @return string
*/
protected function getSeverity($level)
{
if ($this->aboveLevel($level, 'error')) {
return 'error';
} elseif ($this->aboveLevel($level, 'warning')) {
return 'warning';
} else {
return 'info';
}
}
/**
* Format the parameters for the logger.
*
* @param mixed $message
*
* @return string
*/
protected function formatMessage($message)
{
if (is_array($message)) {
return var_export($message, true);
}
return $message;
}
/**
* Ensure the given string is less than 100 characters.
*
* @param string $str
*
* @return string
*/
protected function limit($str)
{
if (strlen($str) <= 100) {
return $str;
}
return rtrim(substr($str, 0, 97)).'...';
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Bugsnag\PsrLogger;
class MultiLogger extends AbstractLogger
{
/**
* The registered loggers.
*
* @var \Psr\Log\LoggerInterface[]
*/
protected $loggers;
/**
* Create a new multi logger instance.
*
* @param \Psr\Log\LoggerInterface[] $loggers
*
* @return void
*/
public function __construct(array $loggers)
{
$this->loggers = $loggers;
}
/**
* Log a message to the logs.
*
* @param string $level
* @param mixed $message
* @param array $context
*
* @return void
*/
public function log($level, $message, array $context = [])
{
foreach ($this->loggers as $logger) {
$logger->log($level, $message, $context);
}
}
}

158
vendor/bugsnag/bugsnag/ARCHITECTURE.md vendored Normal file
View File

@@ -0,0 +1,158 @@
# Bugsnag-PHP Notifier Architecture
Version 1.0.0
Last updated 30/08/17
## Introduction
This document is one of a series describing the layout of the individual Bugsnag notifier libraries. Their purpose is to make it easier to understand the layout and working logic involved in the notifiers for new contributors and users, and the preferred ways of extending and modifying said libraries.
## Dependencies
- [composer/ca-bundle](https://github.com/composer/ca-bundle)
- [guzzlehttp/guzzle](https://github.com/guzzle/guzzle)
- [vlucas/phpdotenv](https://github.com/vlucas/phpdotenv)
## Dev Dependencies
- [graham-campbell/testbench-core](https://github.com/GrahamCampbell/Laravel-TestBench-Core)
- [mockery/mockery](https://github.com/mockery/mockery)
- [mtdowling/burgomaster](https://github.com/mtdowling/Burgomaster)
- [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit)
- [php-mock/php-mock-phpunit](https://github.com/php-mock/php-mock-phpunit)
## Bugsnag-PHP Architecture
All code required to run the Bugsnag PHP notifier can be found within the `src` directory.
### The Client Object
The main Bugsnag object that will be used in applications for the purpose of catching and notify of errors is the client object. This provides an API for the user to call on for the majority of the functions they will seek to utilise in the library, including: `notify`, `notify-exception`, `notify-error`, `leave-breadcrumb`, and `deploy`.
The client object has three constructor arguments that modify the way it will operate:
- `configuration` Accepts a `Configuration` object for the user to customize behavior of the Bugsnag notifier. The configuration options will be set in the most appropriate method for the framework being used, and parsed into this format.
- `resolver` Accepts an object that implements the `ResolverInterface`. This object will be responsible for returning an object that implements the `RequestInterface`, which will need to populate a `Report` upon request. By default if not given a resolver the client will create a `BasicResolver`.
- `guzzle` Accepts a `Guzzle` object provided by the guzzler library to use as an HTTP request object in the `HTTPClient` object. By default the client will create one using the static `ENDPOINT` variable. If it's necessary to change these defaults, the static `make_guzzle` may be used with a defined base url and options array, with the result being passed into this argument.
When utilising this library in a framework-specific way it is advised that the client object creation is handled in a way that makes the most sense for access to the notify functions and in order to extract the most relevant data, e.g. by using a different `resolver` with framework-specific functions, or wrapping the client in a service provider.
There is a provided static `make` method that creates the client object with suitable default `Configuration` and `Guzzle` arguments with the `api_key` and `endpoint` allowing the user to set their Bugsnag settings.
Another important aspect of the client object's construction is the registering of callbacks with a `pipeline` object. If created with the `make` method the default callbacks will be added to the `pipeline` unless
the `defaults` argument is set to false. These default callbacks will be responsible for populating the `report` object with information when necessary, and will should be customized to extract the most relevant information from the error and `resolver` objects used in the framework.
### The Handler Class
The handler class provides static functions for registering the Bugsnag unhandled error and exception handlers, as well as a shutdown handler. This is separate to the client object as these handlers and methods of registering them will change across frameworks.
The static registration functions take an instance of the client object to utilise as a source of application information and as a notifier when an unhandled error or exception occurs.
As this is an optional part of the library that must be initiated separately it should be replaced by an appropriate method of hooking into the error handlers or loggers of the relevant framework. It must respond to these events by creating a `Report` object from the `client` and exception, and then it must pass it into the client object's `notify` function for it to be sent off to the Bugsnag notification server.
### The Report Object
The Report class is used to create readable information from a PHP exception or throwable which can later be used to populate an HTTP request to notify Bugsnag. It is accessible through three static methods:
- `fromPHPError` to create from a given decompiled PHP Error from the error handler
- `fromPHPThrowable` to create from a PHP Throwable object
- `fromNamedError` to create from name and message strings
These methods should be used to create a report object to ensure that the correct fields are populated for the later notification stages.
### The Pipeline and Callbacks
Upon being passed to the `notify` function, the report object will also be populated with information provided by a series of callbacks created with the `client` object. Registered with the `registerCallback` method, each callback will be passed the `report` in turn and can populate it with additional information if necessary.
The pipeline object itself is responsible for executing these callbacks as a series of closures until they have all been able to access the `report` object and modify its content. The pipeline object is merely a method of utilising the callbacks, and does not need to be modified per framework.
The default callbacks, registered through the `client` object's `registerDefaultCallbacks` use the `Resolver` and its `Request` objects to extract data about the current environment in the server, where the error-causing request originated from, and any more metadata it can report.
There are two callbacks registered with the pipeline automatically by the client: `BreadcrumbData` which ensures that the recorded `Breadcrumb` objects are attached to the `report`, and `NotificationSkipper` which stops the notification process in the event that the notification should not be sent i.e. when missing an `api_key` or a non-releasing `releaseStage`.
It is recommended to create callback functions that can extract additional information from the framework to attach as metadata if necessary. These callbacks are automatically wrapped in a `CallbackBridge` when registered to the pipeline which ensures they will automatically be called.
### The HTTPClient
Once the `report` object has been populated by the `pipeline` a callback is finally triggered in the `notify` function that will send the `report` to the HTTPClient. This object exists on the `client` and is intiated with a `guzzle` client that it will use to call off to the configured endpoint.
This object will queue each `report` object it is given until `send` is called. At this point it will iteratively create a single object containing all of the data from the `queue`, and ensure that the payload is correctly set up to be sent while remaining under the payload size limit.
Once the payload has been fully constructed it will be posted to the configured endpoint via the guzzle object.
The HTTPClient is also responsible for the `deploy` call.
### Breadcrumbs
Bugsnag tracks a series of actions manually dictated by the user to be sent to the Bugsnag notify endpoint along with an exception. These actions are stored as breadcrumb objects, and are stored in the recorder object in the `client`. The recorder acts like a circular array, storing up to 25 breadcrumb objects at a time before the oldest breadcrumbs get overwritten. This limit is imposed to ensure the size of the payload sent to Bugsnag does not exceed the API limits
The breadcrumb data is attached to the `report` payload by the BreadcrumbData callback, which is initiated by the `client` object in its construction.
When the `notify` function is called, manually or automatically, a breadcrumb is logged with details of the error or exception being sent.
# Other Bugsnag PHP Framework Libraries
This section covers the other available Bugsnag notifiers for PHP frameworks and how they are implemented and connected to the main Bugsnag-PHP libary and each other.
## [Bugsnag-PSR-Logger](https://github.com/bugsnag/bugsnag-psr-logger)
This library implements the [PSR-3 Logger Interface](http://www.php-fig.org/psr/psr-3/) specification to enable users to attach Bugsnag into a standardized logging system. It consists of three classes:
- `AbstractLogger` an abstract class which implements the PSR spec `LoggerInterface`, which requires a `log` method in its implementors
- `BugsnagLogger` a logger class which extends the above class. This logger will record `debug` or `info` logs as `breadcrumb` objects, and will notify Bugsnag of any other log type
- `MultiLogger` again extends the `AbstractLogger`, but accepts and array of loggers in its construction, allowing other PSR compliant loggers to be used simultaneously with the `BugsnagLogger`
## [Bugsnag-Wordpress](https://github.com/bugsnag/bugsnag-wordpress)
This plugin for wordpress enables Bugsnag through the plugins menu of the wordpress site. It requires an older version of this library (~ 2.2) and so some of the methods and features will likely have been refactored for the newer versions.
## [Bugsnag-Symfony](https://github.com/bugsnag/bugsnag-symfony)
This library provides an extended version of the base Bugsnag-PHP library customized for the [Symfony PHP framework](symfony.com).
### Dependency Management
The libary is bundled for inclusion in the Symfony app framework through the `RegisterBundle` function used for adding Symfony extensions as laid out [here](https://symfony.com/doc/current/bundles.html). It utilises the `DependencyInjection` folder to set up the extension through the `Configuration.php` file to define the necessary default configuration options, drawing the rest from the app's `config.yml`. This configuration is then read in and processed by the `BugsnagExtension.php` file, and the arguments are set on the container.
To build the client when requested, the `Configuration.php` defines a factory for the framework to use, `ClientFactory.php`. This factory wraps the creation methods for the `client` object and ensures that the client is configured correctly to get information from the framework and respond to events. The `ClientFactory` configuration can be found in the Bugsnag service definition in `Resources/services.yml`.
### Customizing the `Client` object
The configuration passed through to the `ClientFactory` will modify several of the `client` object's properties. In additional to the [configuration options](https://docs.bugsnag.com/platforms/php/symfony/configuration-options/) for the user to setup their particular configuration, it also defines the `resolver` object which gathers information that populates the `report` objects used in the notify method.
The `ClientFactory` registers the default callbacks to the `pipeline` to extract data for the report, but also adds an additional callback specific to Symfony to extract a user identifier from the user specific `Token` object.
Once the `client` has been created it is returned to the Symfony instance as a service, allowing it to be accessed through the application with the
appropriate Symfony service access methods.
### Listening for Events
The library does not utilise the basic PHP `Handler` object to listen to events, rather it connects an event listener `BugsnagListener.php` to the Symfony instance in the `services.yml` file, connecting specific events directly to a method via the `tags` descriptor as mentioned in the [official documentation](https://symfony.com/doc/current/event_dispatcher.html).
### Symfony Resolver and Request
As defined in `Configuration.php` the client will use the `SymfonyResolver` class as its default resolver. This resolver ensures that there is a Symfony specific `Request` object available before handing it off to a `SymfonyRequest` object. This object implements all the methods from the `RequestInterface`, allowing the default callbacks to extract and append data to reports whenever a notification occurs.
## [Bugsnag-Laravel](https://github.com/bugsnag/bugsnag-laravel)
The Bugsnag-Laravel library again extends the Bugsnag-PHP library and customizes its operation for the [Laravel application framework](https://laravel.com/).
### Dependency Management
Laravel uses a very similar dependency management system to Symfony, wrapping classes in `ServiceProviders` that can then be called later through an `Alias` or a `Facade`. The `BugsnagServiceProvider` class implements `boot` and `register` functions as described in the [service provider](https://laravel.com/docs/5.4/providers) documentation. The `boot` function intialises the configuration and options of the provider, while the `register` function returns a singleton accessible throught the application.
### Customizing the `Client` object
The `register` function mentioned above creates the `client` object with a base `configuration` and `guzzle`, as well as a `LaravelResolver` to handle retrieving data from created `LaravelRequest` objects in the `Resolver`-`Request` pattern.
It draws its configuration from the Laravel `config` object which the framework automatically populates from the `.env` file or a created `Bugsnag.php` configuration file.
The default callbacks are registered to the `pipeline` in the `setupCallbacks` function, along with customized callbacks to extract custom and user information from the framework to attach to the report.
### Listening for events
The Laravel notifier uses the [Bugsnag-PSR-Logger](https://github.com/bugsnag/bugsnag-psr-logger) in order to automatically receive error and exception events from the Laravel framework, which is the Laravel preferred method instead of directly registering an `error-handler`.
The notifier wraps the PSR logger in a pair of classes, the `LaravelLogger` and `MultiLogger` for singular and multi-logging setups respectively. These are added to the framework by aliasing the frameworks PSR-logger interface and class to the `bugsnag.logger` class or `bugsnag.multi` classes depending on the users setup. This must be done manually within the `AppServiceProvider`.
### Notifying of Deployments
While deployment notifications can be sent through the client object, Laravel library also provided a deploy command through the `DeployCommand` class. This must be registered through the `commands` array in the `Kernel.php` Laravel file.
## [Bugsnag-Silex](https://github.com/bugsnag/bugsnag-silex)
The Bugsnag-Silex library adds Silex-specific methods and data-gathering for the [Silex micro-framework](silex.symfony.com).
### Dependency Management
Being based on Symfony, Silex uses a similar service provider system to Laravel and Symfony, where the provider is passed to the app using the `register` function in the app's main startup file. The provider is an implementation of the `ServiceProviderInterface` class, with a `register` function that adds `client` and `resolver` objects to the Silex container.
The provider is split into three classes, `AbstractServiceProvider` as a base class and `Silex1ServiceProvider` and `Silex2ServiceProvider` as extensions of this base. The version specific provider classes operate in the same way except the `Silex1ServiceProvider` defines a required `boot` function that does nothing.
Both classes call into their base class method `makeClient` to produce the `client` being registered.
### Customizing the `Client` object
Silex configuration options are added in an environment-specific file in the `config` folder. These options are then automatically pulled into the app container when the environment is started. In the `makeClient` function this config is pulled in and used to setup a newly created client object, along with the earlier created `resolver`.
This `SilexResolver` is used to retrieve data for each report using the `SilexRequest` class in the previously mentioned `Resolver`-`Request` pattern.
Standard default callbacks are registered along with an additional callback to detect the user if one isn't already configured.
### Listening for events
The Silex framework requires manual registering of error and exception handlers, which requires the user add a `notifyException` call into an error handler registered to the app container's `error` function.
## [Bugsnag-Magento](https://github.com/bugsnag/bugsnag-magento)
The Bugsnag-Magento module enables Bugsnag functionality through the Magento admin panel. It uses an older version of the Bugsnag-PHP library packaged with the module and so some of the methods and features will likely have been refactored by later versions.

View File

@@ -11,21 +11,35 @@
"homepage": "https://bugsnag.com"
}],
"require": {
"php": ">=5.2.0"
"php": ">=5.5",
"composer/ca-bundle": "^1.0",
"guzzlehttp/guzzle": "^5.0|^6.0|^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.0"
"guzzlehttp/psr7": "^1.3",
"mtdowling/burgomaster": "dev-master#72151eddf5f0cf101502b94bf5031f9c53501a04",
"phpunit/phpunit": "^4.8.36|^7.5.15|^9.3.10",
"php-mock/php-mock-phpunit": "^1.1|^2.1",
"sebastian/version": ">=1.0.3"
},
"autoload": {
"psr-0" : {
"Bugsnag_" : "src/"
"psr-4" : {
"Bugsnag\\" : "src/"
}
},
"autoload-dev": {
"psr-4" : {
"Bugsnag\\Tests\\" : "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.9-dev"
"dev-master": "3.20-dev"
}
},
"scripts": {
"test": "vendor/bin/phpunit"
},
"minimum-stability": "dev",
"prefer-stable": true
}

5
vendor/bugsnag/bugsnag/packer.sh vendored Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
rm -rf build vendor composer.lock
composer install -o -n --prefer-dist
php -d phar.readonly=false packager.php

View File

@@ -0,0 +1,185 @@
<?php
namespace Bugsnag\Breadcrumbs;
use Bugsnag\DateTime\Date;
use InvalidArgumentException;
class Breadcrumb
{
/**
* The navigation type.
*
* @var string
*/
const NAVIGATION_TYPE = 'navigation';
/**
* The request type.
*
* @var string
*/
const REQUEST_TYPE = 'request';
/**
* The process type.
*
* @var string
*/
const PROCESS_TYPE = 'process';
/**
* The log type.
*
* @var string
*/
const LOG_TYPE = 'log';
/**
* The user type.
*
* @var string
*/
const USER_TYPE = 'user';
/**
* The state type.
*
* @var string
*/
const STATE_TYPE = 'state';
/**
* The error type.
*
* @var string
*/
const ERROR_TYPE = 'error';
/**
* The manual type.
*
* @var string
*/
const MANUAL_TYPE = 'manual';
/**
* The maximum size of the breadcrumb.
*
* @var int
*/
const MAX_SIZE = 4096;
/**
* The timestamp of the breadcrumb.
*
* @var string
*/
protected $timestamp;
/**
* The name of the breadcrumb.
*
* @var string
*/
protected $name;
/**
* The type of the breadcrumb.
*
* @var string
*/
protected $type;
/**
* The meta data of the breadcrumb.
*
* @var array
*/
protected $metaData;
/**
* Create a new breadcrumb instance.
*
* @param string $name the name of the breadcrumb
* @param string $type the type of breadcrumb
* @param array $metaData additional information about the breadcrumb
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function __construct($name, $type, array $metaData = [])
{
if (!is_string($name)) {
if (is_null($name)) {
$metaData['BreadcrumbError'] = 'NULL provided as the breadcrumb name';
$name = '<no name>';
} else {
$metaData['BreadcrumbError'] = 'Breadcrumb name must be a string - '.gettype($name).' provided instead';
$name = '<no name>';
}
} elseif ($name === '') {
$metaData['BreadcrumbError'] = 'Empty string provided as the breadcrumb name';
$name = '<no name>';
}
$types = static::getTypes();
if (!in_array($type, $types, true)) {
throw new InvalidArgumentException(sprintf('The breadcrumb type must be one of the set of %d standard types.', count($types)));
}
$this->timestamp = Date::now();
$this->name = $name;
$this->type = $type;
$this->metaData = $metaData;
}
/**
* Get the breadcrumb as an array.
*
* Note that this is without the meta data.
*
* @return array
*/
public function toArray()
{
return [
'timestamp' => $this->timestamp,
'name' => $this->name,
'type' => $this->type,
];
}
/**
* Get the breadcrumb meta data.
*
* Note that this still needs sanitizing before use.
*
* @return array
*/
public function getMetaData()
{
return $this->metaData;
}
/**
* Get the set of valid breadrum types.
*
* @return array
*/
public static function getTypes()
{
return [
static::NAVIGATION_TYPE,
static::REQUEST_TYPE,
static::PROCESS_TYPE,
static::LOG_TYPE,
static::USER_TYPE,
static::STATE_TYPE,
static::ERROR_TYPE,
static::MANUAL_TYPE,
];
}
}

View File

@@ -0,0 +1,167 @@
<?php
namespace Bugsnag\Breadcrumbs;
use Countable;
use Iterator;
/**
* @implements Iterator<int, Breadcrumb>
*/
class Recorder implements Countable, Iterator
{
/**
* The maximum number of breadcrumbs to store.
*
* @var int
*/
private $maxBreadcrumbs = 50;
/**
* The recorded breadcrumbs.
*
* @var \Bugsnag\Breadcrumbs\Breadcrumb[]
*/
private $breadcrumbs = [];
/**
* The iteration position.
*
* @var int
*/
private $position = 0;
/**
* Record a breadcrumb.
*
* @param \Bugsnag\Breadcrumbs\Breadcrumb $breadcrumb
*
* @return void
*/
public function record(Breadcrumb $breadcrumb)
{
$this->breadcrumbs[] = $breadcrumb;
// drop the oldest breadcrumb if we're over the max
if ($this->count() > $this->maxBreadcrumbs) {
array_shift($this->breadcrumbs);
}
}
/**
* Clear all recorded breadcrumbs.
*
* @return void
*/
public function clear()
{
$this->position = 0;
$this->breadcrumbs = [];
}
/**
* Set the maximum number of breadcrumbs that are allowed to be stored.
*
* This must be an integer between 0 and 100 (inclusive).
*
* @param int $maxBreadcrumbs
*
* @return void
*/
public function setMaxBreadcrumbs($maxBreadcrumbs)
{
if (!is_int($maxBreadcrumbs) || $maxBreadcrumbs < 0 || $maxBreadcrumbs > 100) {
error_log(
'Bugsnag Warning: maxBreadcrumbs should be an integer between 0 and 100 (inclusive)'
);
return;
}
$this->maxBreadcrumbs = $maxBreadcrumbs;
// drop the oldest breadcrumbs if we're over the max
if ($this->count() > $this->maxBreadcrumbs) {
$this->breadcrumbs = array_slice(
$this->breadcrumbs,
$this->count() - $this->maxBreadcrumbs
);
}
}
/**
* Get the maximum number of breadcrumbs that are allowed to be stored.
*
* @return int
*/
public function getMaxBreadcrumbs()
{
return $this->maxBreadcrumbs;
}
/**
* Get the number of stored breadcrumbs.
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->breadcrumbs);
}
/**
* Get the current item.
*
* @return \Bugsnag\Breadcrumbs\Breadcrumb
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->breadcrumbs[$this->position];
}
/**
* Get the current key.
*
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
}
/**
* Advance the key position.
*
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
$this->position++;
}
/**
* Rewind the key position.
*
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->position = 0;
}
/**
* Is the current key position set?
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->position < $this->count();
}
}

View File

@@ -1,15 +0,0 @@
<?php
// We used to have an autoloader, but it caused problems in some
// environments. So now we manually load the entire library upfront.
//
// The file is still called Autoload so that existing integration
// instructions continue to work.
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Client.php';
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Configuration.php';
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Diagnostics.php';
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Error.php';
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'ErrorTypes.php';
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Notification.php';
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Request.php';
require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'Stacktrace.php';

View File

@@ -1,679 +0,0 @@
<?php
class Bugsnag_Client
{
/**
* The config instance.
*
* @var Bugsnag_Configuration
*/
private $config;
/**
* The diagnostics instance.
*
* @var Bugsnag_Diagnostics
*/
private $diagnostics;
/**
* The notification instance.
*
* @var Bugsnag_Notification|null
*/
private $notification;
/**
* Create a new client instance.
*
* @param string $apiKey your Bugsnag API key
*
* @throws Exception
*
* @return void
*/
public function __construct($apiKey)
{
// Check API key has been passed
if (!is_string($apiKey)) {
throw new Exception('Bugsnag Error: Invalid API key');
}
// Create a configuration object
$this->config = new Bugsnag_Configuration();
$this->config->apiKey = $apiKey;
// Build a Diagnostics object
$this->diagnostics = new Bugsnag_Diagnostics($this->config);
// Register a shutdown function to check for fatal errors
// and flush any buffered errors
register_shutdown_function(array($this, 'shutdownHandler'));
}
/**
* Set your release stage, eg "production" or "development".
*
* @param string $releaseStage the app's current release stage
*
* @return $this
*/
public function setReleaseStage($releaseStage)
{
$this->config->releaseStage = $releaseStage;
return $this;
}
/**
* Set your app's semantic version, eg "1.2.3".
*
* @param string $appVersion the app's version
*
* @return $this
*/
public function setAppVersion($appVersion)
{
$this->config->appVersion = $appVersion;
return $this;
}
/**
* Set the host name.
*
* @param string $hostname the host name
*
* @return $this
*/
public function setHostname($hostname)
{
$this->config->hostname = $hostname;
return $this;
}
/**
* Adds new data fields to the device data collection.
*
* @param array $data an associative array containing the new data to be added
*
* @return $this
*/
public function mergeDeviceData($data)
{
return $this->diagnostics->mergeDeviceData($data);
}
/**
* Get the device data.
*
* @return array
*/
public function getDeviceData()
{
return $this->diagnostics->getDeviceData();
}
/**
* Set which release stages should be allowed to notify Bugsnag.
*
* Eg array('production', 'development').
*
* @param array $notifyReleaseStages array of release stages to notify for
*
* @return $this
*/
public function setNotifyReleaseStages(array $notifyReleaseStages)
{
$this->config->notifyReleaseStages = $notifyReleaseStages;
return $this;
}
/**
* Set which Bugsnag endpoint to send errors to.
*
* @param string $endpoint endpoint URL
*
* @return $this
*/
public function setEndpoint($endpoint)
{
$this->config->endpoint = $endpoint;
return $this;
}
/**
* Enable debug mode to help diagnose problems.
*
* @param bool $debug whether to enable debug mode
*
* @return $this
*/
public function setDebug($debug)
{
$this->config->debug = $debug;
return $this;
}
/**
* Set whether or not to use SSL when notifying bugsnag.
*
* @param bool $useSSL whether to use SSL
*
* @return $this
*
* @deprecated since version 2.5. Pass full URLs to setEndpoint.
*/
public function setUseSSL($useSSL)
{
$this->config->useSSL = $useSSL;
return $this;
}
/**
* Set the desired timeout for cURL connection when notifying bugsnag.
*
* @param int $timeout the desired timeout in seconds
*
* @return $this
*/
public function setTimeout($timeout)
{
$this->config->timeout = $timeout;
return $this;
}
/**
* Set the absolute path to the root of your application.
*
* We use this to help with error grouping and to highlight "in project"
* stacktrace lines.
*
* @param string $projectRoot the root path for your application
*
* @return $this
*/
public function setProjectRoot($projectRoot)
{
$this->config->setProjectRoot($projectRoot);
return $this;
}
/**
* Set the absolute split path.
*
* This is the path that should be stripped from the beginning of any
* stacktrace file line. This helps to normalise filenames for grouping
* and reduces the noise in stack traces.
*
* @param string $stripPath the path to strip from filenames
*
* @return $this
*/
public function setStripPath($stripPath)
{
$this->config->setStripPath($stripPath);
return $this;
}
/**
* Set the a regular expression for matching filenames in stacktrace lines.
*
* @param string $projectRootRegex regex matching paths belong to your project
*
* @return $this
*/
public function setProjectRootRegex($projectRootRegex)
{
$this->config->projectRootRegex = $projectRootRegex;
return $this;
}
/**
* Set the strings to filter out from metaData arrays before sending then.
*
* Eg. array('password', 'credit_card').
*
* @param array $filters an array of metaData filters
*
* @return $this
*/
public function setFilters(array $filters)
{
$this->config->filters = $filters;
return $this;
}
/**
* Set information about the current user of your app, including id, name and email.
*
* @param array $user an array of user information. Eg:
* array(
* 'name' => 'Bob Hoskins',
* 'email' => 'bob@hoskins.com'
* )
*
* @return $this
*/
public function setUser(array $user)
{
$this->config->user = $user;
return $this;
}
/**
* @param $userId
*
* @return $this
*
* @deprecated since version 2.1. Use setUser instead.
*/
public function setUserId($userId)
{
if (!is_array($this->config->user)) {
$this->config->user = array();
}
$this->config->user['id'] = $userId;
return $this;
}
/**
* Set a context representing the current type of request, or location in code.
*
* @param string $context the current context
*
* @return $this
*/
public function setContext($context)
{
$this->config->context = $context;
return $this;
}
/**
* Set the type of application executing the code.
*
* This is usually used to represent if you are running plain PHP code
* "php", via a framework, eg "laravel", or executing through delayed
* worker code, eg "resque".
*
* @param string $type the current type
*
* @return $this
*/
public function setType($type)
{
$this->config->type = $type;
return $this;
}
/**
* Set custom metadata to send to Bugsnag with every error.
*
* You can use this to add custom tabs of data to each error on your
* Bugsnag dashboard.
*
* @param array $metaData an array of arrays of custom data. Eg:
* array(
* 'user' => array(
* 'name' => 'James',
* 'email' => 'james@example.com'
* )
* )
* @param bool $merge optionally merge the meta data
*
* @return $this
*/
public function setMetaData(array $metaData, $merge = false)
{
if ($merge) {
$this->config->metaData = array_merge_recursive((array) $this->config->metaData, $metaData);
} else {
$this->config->metaData = $metaData;
}
return $this;
}
/**
* Set proxy configuration.
*
* @param array $proxySettings an array with proxy settings. Eg:
* array(
* 'host' => 'bugsnag.com',
* 'port' => 42,
* 'user' => 'username'
* 'password' => 'password123'
* )
*
* @return $this
*/
public function setProxySettings(array $proxySettings)
{
$this->config->proxySettings = $proxySettings;
return $this;
}
/**
* Set custom curl options.
*
* @param array $curlOptions an array with curl options. Eg:
* array(
* CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4
* )
*
* @return $this
*/
public function setCurlOptions(array $curlOptions)
{
$this->config->curlOptions = $curlOptions;
return $this;
}
/**
* Set a custom function to call before notifying Bugsnag of an error.
*
* You can use this to call your own error handling functions, or to add
* custom tabs of data to each error on your Bugsnag dashboard.
*
* // Adding meta-data example
* function before_bugsnag_notify($error) {
* $error->addMetaData(array(
* 'user' => array(
* 'name' => 'James'
* )
* ));
* }
* $bugsnag->setBeforeNotifyFunction('before_bugsnag_notify');
*
* @param callable $beforeNotifyFunction
*
* @return $this
*/
public function setBeforeNotifyFunction($beforeNotifyFunction)
{
$this->config->beforeNotifyFunction = $beforeNotifyFunction;
return $this;
}
/**
* Set Bugsnag's error reporting level.
*
* If this is not set, we'll use your current PHP error_reporting value
* from your ini file or error_reporting(...) calls.
*
* @param int $errorReportingLevel the error reporting level integer
* exactly as you would pass to PHP's error_reporting
*
* @return $this
*/
public function setErrorReportingLevel($errorReportingLevel)
{
$this->config->errorReportingLevel = $errorReportingLevel;
return $this;
}
/**
* Sets whether Bugsnag should be automatically notified of unhandled exceptions and errors.
*
* @param bool $autoNotify whether to auto notify or not
*
* @return $this
*/
public function setAutoNotify($autoNotify)
{
$this->config->autoNotify = $autoNotify;
return $this;
}
/**
* Sets whether errors should be batched together and send at the end of each request.
*
* @param bool $batchSending whether to batch together errors
*
* @return $this
*/
public function setBatchSending($batchSending)
{
$this->config->batchSending = $batchSending;
return $this;
}
/**
* Sets the notifier to report as to Bugsnag.
*
* This should only be set by other notifier libraries.
*
* @param array $notifier an array of name, version, url.
*
* @return $this
*/
public function setNotifier($notifier)
{
$this->config->notifier = $notifier;
return $this;
}
/**
* Sets whether Bugsnag should send $_ENV with each error.
*
* @param bool $sendEnvironment whether to send the environment
*
* @return $this
*/
public function setSendEnvironment($sendEnvironment)
{
$this->config->sendEnvironment = $sendEnvironment;
return $this;
}
/**
* Sets whether Bugsnag should send $_COOKIE with each error.
*
* @param bool $sendCookies whether to send the environment
*
* @return $this
*/
public function setSendCookies($sendCookies)
{
$this->config->sendCookies = $sendCookies;
return $this;
}
/**
* Sets whether Bugsnag should send $_SESSION with each error.
*
* @param bool $sendSession whether to send the environment
*
* @return $this
*/
public function setSendSession($sendSession)
{
$this->config->sendSession = $sendSession;
return $this;
}
/**
* Should we send a small snippet of the code that crashed.
*
* This can help you diagnose even faster from within your dashboard.
*
* @param bool $sendCode whether to send code to Bugsnag
*
* @return $this
*/
public function setSendCode($sendCode)
{
$this->config->sendCode = $sendCode;
return $this;
}
/**
* Notify Bugsnag of a non-fatal/handled throwable.
*
* @param Throwable $throwable the throwable to notify Bugsnag about
* @param array $metaData optional metaData to send with this error
* @param string $severity optional severity of this error (fatal/error/warning/info)
*
* @return void
*/
public function notifyException($throwable, array $metaData = null, $severity = null)
{
if (is_subclass_of($throwable, 'Throwable') || is_subclass_of($throwable, 'Exception') || get_class($throwable) == 'Exception') {
$error = Bugsnag_Error::fromPHPThrowable($this->config, $this->diagnostics, $throwable);
$error->setSeverity($severity);
$this->notify($error, $metaData);
}
}
/**
* Notify Bugsnag of a non-fatal/handled error.
*
* @param string $name the name of the error, a short (1 word) string
* @param string $message the error message
* @param array $metaData optional metaData to send with this error
* @param string $severity optional severity of this error (fatal/error/warning/info)
*
* @return void
*/
public function notifyError($name, $message, array $metaData = null, $severity = null)
{
$error = Bugsnag_Error::fromNamedError($this->config, $this->diagnostics, $name, $message);
$error->setSeverity($severity);
$this->notify($error, $metaData);
}
/**
* Exception handler callback.
*
* Should only be called internally by PHP's set_exception_handler.
*
* @param Throwable $throwable the exception was was thrown
*
* @return void
*/
public function exceptionHandler($throwable)
{
if (!$this->config->autoNotify) {
return;
}
$error = Bugsnag_Error::fromPHPThrowable($this->config, $this->diagnostics, $throwable);
$error->setSeverity('error');
$this->notify($error);
}
/**
* Error handler callback.
*
* Should only be called internally by PHP's set_error_handler.
*
* @param int $errno the level of the error raised
* @param string $errstr the error message
* @param string $errfile the filename that the error was raised in
* @param int $errline the line number the error was raised at
*
* @return void
*/
public function errorHandler($errno, $errstr, $errfile = '', $errline = 0)
{
if (!$this->config->autoNotify || $this->config->shouldIgnoreErrorCode($errno)) {
return;
}
$error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $errno, $errstr, $errfile, $errline);
$this->notify($error);
}
/**
* Shutdown handler callback.
*
* Called when the PHP process has finished running. Should only be called
* internally by PHP's register_shutdown_function.
*
* @return void
*/
public function shutdownHandler()
{
// Get last error
$lastError = error_get_last();
// Check if a fatal error caused this shutdown
if (!is_null($lastError) && Bugsnag_ErrorTypes::isFatal($lastError['type']) && $this->config->autoNotify && !$this->config->shouldIgnoreErrorCode($lastError['type'])) {
$error = Bugsnag_Error::fromPHPError($this->config, $this->diagnostics, $lastError['type'], $lastError['message'], $lastError['file'], $lastError['line'], true);
$error->setSeverity('error');
$this->notify($error);
}
// Flush any buffered errors
if ($this->notification) {
$this->notification->deliver();
$this->notification = null;
}
}
/**
* Batches up errors into notifications for later sending.
*
* @param Bugsnag_Error $error the error to batch up
* @param array $metaData optional meta data to send with the error
*
* @return void
*/
public function notify(Bugsnag_Error $error, $metaData = array())
{
// Queue or send the error
if ($this->sendErrorsOnShutdown()) {
// Create a batch notification unless we already have one
if (is_null($this->notification)) {
$this->notification = new Bugsnag_Notification($this->config);
}
// Add this error to the notification
$this->notification->addError($error, $metaData);
} else {
// Create and deliver notification immediately
$notif = new Bugsnag_Notification($this->config);
$notif->addError($error, $metaData);
$notif->deliver();
}
}
/**
* Should we send errors immediately, or on shutdown?
*
* @return bool
*/
private function sendErrorsOnShutdown()
{
return $this->config->batchSending && Bugsnag_Request::isRequest();
}
}

View File

@@ -1,145 +0,0 @@
<?php
class Bugsnag_Configuration
{
public static $DEFAULT_TIMEOUT = 10;
public static $DEFAULT_ENDPOINT = 'https://notify.bugsnag.com';
public static $DEFAULT_NON_SSL_ENDPOINT = 'http://notify.bugsnag.com';
public $apiKey;
public $autoNotify = true;
public $batchSending = true;
public $useSSL = true;
public $endpoint;
public $notifyReleaseStages;
public $filters = array('password');
public $projectRoot;
public $projectRootRegex;
public $proxySettings = array();
public $notifier = array(
'name' => 'Bugsnag PHP (Official)',
'version' => '2.10.1',
'url' => 'https://bugsnag.com',
);
public $sendEnvironment = false;
public $sendCookies = true;
public $sendSession = true;
public $sendCode = true;
public $stripPath;
public $stripPathRegex;
public $context;
public $type;
public $user;
public $releaseStage = 'production';
public $appVersion;
public $hostname;
public $metaData;
public $beforeNotifyFunction;
public $errorReportingLevel;
public $curlOptions = array();
public $debug = false;
/**
* Create a new config instance.
*
* @return void
*/
public function __construct()
{
$this->timeout = self::$DEFAULT_TIMEOUT;
}
/**
* Get the notify endpoint.
*
* @return string
*/
public function getNotifyEndpoint()
{
if (is_null($this->endpoint)) {
return $this->useSSL ? self::$DEFAULT_ENDPOINT : self::$DEFAULT_NON_SSL_ENDPOINT;
} elseif (preg_match('/^(http:\/\/|https:\/\/)/', $this->endpoint)) {
return $this->endpoint;
} else {
return ($this->useSSL ? 'https' : 'http').'://'.$this->endpoint;
}
}
/**
* Should we notify?
*
* @return bool
*/
public function shouldNotify()
{
return is_null($this->notifyReleaseStages) || (is_array($this->notifyReleaseStages) && in_array($this->releaseStage, $this->notifyReleaseStages));
}
/**
* Should we ignore the given error code?
*
* @param int $code the error code
*
* @return bool
*/
public function shouldIgnoreErrorCode($code)
{
if (isset($this->errorReportingLevel)) {
return !($this->errorReportingLevel & $code);
} else {
return !(error_reporting() & $code);
}
}
/**
* Set the project root.
*
* @param string $projectRoot the project root path
*
* @return void
*/
public function setProjectRoot($projectRoot)
{
$this->projectRoot = $projectRoot;
$this->projectRootRegex = '/'.preg_quote($projectRoot, '/').'[\\/]?/i';
if (is_null($this->stripPath)) {
$this->setStripPath($projectRoot);
}
}
/**
* Set the strip path.
*
* @param string $stripPath the absolute strip path
*
* @return void
*/
public function setStripPath($stripPath)
{
$this->stripPath = $stripPath;
$this->stripPathRegex = '/'.preg_quote($stripPath, '/').'[\\/]?/i';
}
/**
* Get the given configuration.
*
* @param string $prop the property to get
* @param mixed $default the value to fallback to
*
* @return mixed
*/
public function get($prop, $default = null)
{
$configured = $this->$prop;
if (is_array($configured) && is_array($default)) {
return array_merge($default, $configured);
} else {
return $configured ? $configured : $default;
}
}
}

View File

@@ -1,110 +0,0 @@
<?php
class Bugsnag_Diagnostics
{
/**
* The config instance.
*
* @var Bugsnag_Configuration
*/
private $config;
/**
* The device data.
*
* @var string[]
*/
private $deviceData = array();
/**
* Create a new diagnostics instance.
*
* @param Bugsnag_Configuration $config the configuration instance
*
* @return void
*/
public function __construct(Bugsnag_Configuration $config)
{
$this->config = $config;
$this->mergeDeviceData(array('runtimeVersions' => array('php' => phpversion())));
}
/**
* Get the application information.
*
* @return array
*/
public function getAppData()
{
$appData = array();
if (!is_null($this->config->appVersion)) {
$appData['version'] = $this->config->appVersion;
}
if (!is_null($this->config->releaseStage)) {
$appData['releaseStage'] = $this->config->releaseStage;
}
if (!is_null($this->config->type)) {
$appData['type'] = $this->config->type;
}
return $appData;
}
/**
* Merges new data fields to the device data collection.
*
* @param array $deviceData the data to add
*
* @return $this
*/
public function mergeDeviceData($deviceData)
{
$this->deviceData = array_merge_recursive($this->deviceData, $deviceData);
return $this;
}
/**
* Get the device information.
*
* @return array
*/
public function getDeviceData()
{
return array_merge(
array('hostname' => $this->config->get('hostname', php_uname('n'))),
array_filter($this->deviceData)
);
}
/**
* Get the error context.
*
* @return array
*/
public function getContext()
{
return $this->config->get('context', Bugsnag_Request::getContext());
}
/**
* Get the current user.
*
* @return array
*/
public function getUser()
{
$defaultUser = array();
$userId = Bugsnag_Request::getUserId();
if (!is_null($userId)) {
$defaultUser['id'] = $userId;
}
return $this->config->get('user', $defaultUser);
}
}

View File

@@ -1,415 +0,0 @@
<?php
class Bugsnag_Error
{
private static $VALID_SEVERITIES = array(
'error',
'warning',
'info',
);
public $name;
public $payloadVersion = '2';
public $message;
public $severity = 'warning';
/** @var Bugsnag_Stacktrace */
public $stacktrace;
public $metaData = array();
public $user;
public $config;
public $diagnostics;
/** @var Bugsnag_Error|null */
public $previous;
public $groupingHash;
/**
* Create a new error from a PHP error.
*
* @param Bugsnag_Configuration $config the config instance
* @param Bugsnag_Diagnostics $diagnostics the diagnostics instance
* @param int $code the error code
* @param string $message the error message
* @param string $file the error file
* @param int $line the error line
* @param bool $fatal if the error was fatal
*
* @return self
*/
public static function fromPHPError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $code, $message, $file, $line, $fatal = false)
{
$error = new self($config, $diagnostics);
$error->setPHPError($code, $message, $file, $line, $fatal);
return $error;
}
/**
* Create a new error from a PHP throwable.
*
* @param Bugsnag_Configuration $config the config instance
* @param Bugsnag_Diagnostics $diagnostics the diagnostics instance
* @param Throwable $throwable te he throwable instance
*
* @return self
*/
public static function fromPHPThrowable(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $throwable)
{
$error = new self($config, $diagnostics);
$error->setPHPThrowable($throwable);
return $error;
}
/**
* Create a new error from a named error.
*
* @param Bugsnag_Configuration $config the config instance
* @param Bugsnag_Diagnostics $diagnostics the diagnostics instance
* @param string $name the error name
* @param string|null $message the error message
*
* @return self
*/
public static function fromNamedError(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics, $name, $message = null)
{
$error = new self($config, $diagnostics);
$error->setName($name)
->setMessage($message)
->setStacktrace(Bugsnag_Stacktrace::generate($config));
return $error;
}
/**
* Create a new error instance.
*
* This is only for for use only by the static methods above.
*
* @param Bugsnag_Configuration $config the config instance
* @param Bugsnag_Diagnostics $diagnostics the diagnostics instance
*
* @return void
*/
private function __construct(Bugsnag_Configuration $config, Bugsnag_Diagnostics $diagnostics)
{
$this->config = $config;
$this->diagnostics = $diagnostics;
}
/**
* Set the error name.
*
* @param string $name the error name
*
* @throws InvalidArgumentException
*
* @return $this
*/
public function setName($name)
{
if (is_scalar($name) || method_exists($name, '__toString')) {
$this->name = (string) $name;
} else {
throw new InvalidArgumentException('Name must be a string.');
}
return $this;
}
/**
* Set the error message.
*
* @param string|null $message the error message
*
* @throws InvalidArgumentException
*
* @return $this
*/
public function setMessage($message)
{
if ($message === null) {
$this->message = null;
} elseif (is_scalar($message) || method_exists($message, '__toString')) {
$this->message = (string) $message;
} else {
throw new InvalidArgumentException('Message must be a string.');
}
return $this;
}
/**
* Set the grouping hash.
*
* @param string $groupingHash the grouping hash
*
* @return $this
*/
public function setGroupingHash($groupingHash)
{
$this->groupingHash = $groupingHash;
return $this;
}
/**
* Set the bugsnag stacktrace.
*
* @param Bugsnag_Stacktrace $stacktrace the stacktrace instance
*
* @return $this
*/
public function setStacktrace(Bugsnag_Stacktrace $stacktrace)
{
$this->stacktrace = $stacktrace;
return $this;
}
/**
* Set the error severity.
*
* @param int|null $severity the error severity
*
* @return $this
*/
public function setSeverity($severity)
{
if (!is_null($severity)) {
if (in_array($severity, self::$VALID_SEVERITIES)) {
$this->severity = $severity;
} else {
error_log('Bugsnag Warning: Tried to set error severity to '.$severity.' which is not allowed.');
}
}
return $this;
}
/**
* Set the PHP exception.
*
* @param Throwable $exception the throwable instance
*
* @return $this
*
* @deprecated since version 2.9. Use setPHPThrowable instead.
*/
public function setPHPException($exception)
{
return $this->setPHPThrowable($exception);
}
/**
* Set the PHP throwable.
*
* @param Throwable $exception the throwable instance
*
* @return $this
*/
public function setPHPThrowable($exception)
{
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
if (!$exception instanceof Throwable) {
error_log('Bugsnag Warning: The exception must implement Throwable.');
return $this;
}
} else {
if (!$exception instanceof Exception) {
error_log('Bugsnag Warning: The exception must be an Exception.');
return $this;
}
}
$this->setName(get_class($exception))
->setMessage($exception->getMessage())
->setStacktrace(Bugsnag_Stacktrace::fromBacktrace($this->config, $exception->getTrace(), $exception->getFile(), $exception->getLine()));
if (method_exists($exception, 'getPrevious')) {
$this->setPrevious($exception->getPrevious());
}
return $this;
}
/**
* Set the PHP error.
*
* @param int $code the error code
* @param string $message the error message
* @param string $file the error file
* @param int $line the error line
* @param bool $fatal if the error was fatal
*
* @return $this
*/
public function setPHPError($code, $message, $file, $line, $fatal = false)
{
if ($fatal) {
// Generating stacktrace for PHP fatal errors is not possible,
// since this code executes when the PHP process shuts down,
// rather than at the time of the crash.
//
// In these situations, we generate a "stacktrace" containing only
// the line and file number where the crash occurred.
$stacktrace = Bugsnag_Stacktrace::fromFrame($this->config, $file, $line);
} else {
$stacktrace = Bugsnag_Stacktrace::generate($this->config);
}
$this->setName(Bugsnag_ErrorTypes::getName($code))
->setMessage($message)
->setSeverity(Bugsnag_ErrorTypes::getSeverity($code))
->setStacktrace($stacktrace);
return $this;
}
/**
* Set the error meta data.
*
* @param array $metaData the error meta data
*
* @return $this
*/
public function setMetaData($metaData)
{
if (is_array($metaData)) {
$this->metaData = array_merge_recursive($this->metaData, $metaData);
}
return $this;
}
/**
* Set the current user.
*
* @param array|null $user the current user
*
* @return $this
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Set the previous throwable.
*
* @param Throwable $exception the previous throwable
*
* @return $this
*/
public function setPrevious($exception)
{
if ($exception) {
$this->previous = self::fromPHPThrowable($this->config, $this->diagnostics, $exception);
}
return $this;
}
/**
* Get the array representation.
*
* @return array
*/
public function toArray()
{
$errorArray = array(
'app' => $this->diagnostics->getAppData(),
'device' => $this->diagnostics->getDeviceData(),
'user' => is_null($this->user) ? $this->diagnostics->getUser() : $this->user,
'context' => $this->diagnostics->getContext(),
'payloadVersion' => $this->payloadVersion,
'severity' => $this->severity,
'exceptions' => $this->exceptionArray(),
'metaData' => $this->cleanupObj($this->metaData, true),
);
if (isset($this->groupingHash)) {
$errorArray['groupingHash'] = $this->groupingHash;
}
return $errorArray;
}
/**
* Get the exception array.
*
* @return array
*/
public function exceptionArray()
{
if ($this->previous) {
$exceptionArray = $this->previous->exceptionArray();
} else {
$exceptionArray = array();
}
$exceptionArray[] = array(
'errorClass' => $this->name,
'message' => $this->message,
'stacktrace' => $this->stacktrace->toArray(),
);
return $this->cleanupObj($exceptionArray, false);
}
/**
* Cleanup the given object.
*
* @param mixed $obj the data to cleanup
* @param bool $isMetaData if it is meta data
*
* @return array|null
*/
private function cleanupObj($obj, $isMetaData)
{
if (is_null($obj)) {
return;
}
if (is_array($obj)) {
$cleanArray = array();
foreach ($obj as $key => $value) {
// Check if this key should be filtered
$shouldFilter = false;
// Apply filters to metadata if required
if ($isMetaData && is_array($this->config->filters)) {
foreach ($this->config->filters as $filter) {
if (strpos($key, $filter) !== false) {
$shouldFilter = true;
break;
}
}
}
// Apply filter
if ($shouldFilter) {
$cleanArray[$key] = '[FILTERED]';
} else {
$cleanArray[$key] = $this->cleanupObj($value, $isMetaData);
}
}
return $cleanArray;
} elseif (is_string($obj)) {
// UTF8-encode if not already encoded
if (function_exists('mb_detect_encoding') && !mb_detect_encoding($obj, 'UTF-8', true)) {
return utf8_encode($obj);
} else {
return $obj;
}
} elseif (is_object($obj)) {
// json_encode -> json_decode trick turns an object into an array
return $this->cleanupObj(json_decode(json_encode($obj), true), $isMetaData);
} else {
return $obj;
}
}
}

View File

@@ -1,146 +0,0 @@
<?php
class Bugsnag_ErrorTypes
{
private static $ERROR_TYPES = array(
E_ERROR => array(
'name' => 'PHP Fatal Error',
'severity' => 'error',
),
E_WARNING => array(
'name' => 'PHP Warning',
'severity' => 'warning',
),
E_PARSE => array(
'name' => 'PHP Parse Error',
'severity' => 'error',
),
E_NOTICE => array(
'name' => 'PHP Notice',
'severity' => 'info',
),
E_CORE_ERROR => array(
'name' => 'PHP Core Error',
'severity' => 'error',
),
E_CORE_WARNING => array(
'name' => 'PHP Core Warning',
'severity' => 'warning',
),
E_COMPILE_ERROR => array(
'name' => 'PHP Compile Error',
'severity' => 'error',
),
E_COMPILE_WARNING => array(
'name' => 'PHP Compile Warning',
'severity' => 'warning',
),
E_USER_ERROR => array(
'name' => 'User Error',
'severity' => 'error',
),
E_USER_WARNING => array(
'name' => 'User Warning',
'severity' => 'warning',
),
E_USER_NOTICE => array(
'name' => 'User Notice',
'severity' => 'info',
),
E_STRICT => array(
'name' => 'PHP Strict',
'severity' => 'info',
),
E_RECOVERABLE_ERROR => array(
'name' => 'PHP Recoverable Error',
'severity' => 'error',
),
// E_DEPRECATED (Since PHP 5.3.0)
8192 => array(
'name' => 'PHP Deprecated',
'severity' => 'info',
),
// E_USER_DEPRECATED (Since PHP 5.3.0)
16384 => array(
'name' => 'User Deprecated',
'severity' => 'info',
),
);
/**
* Is the given error code fatal?
*
* @param int $code the error code
*
* @return bool
*/
public static function isFatal($code)
{
return self::getSeverity($code) == 'error';
}
/**
* Get the name of the given error code.
*
* @param int $code the error code
*
* @return string
*/
public static function getName($code)
{
if (array_key_exists($code, self::$ERROR_TYPES)) {
return self::$ERROR_TYPES[$code]['name'];
} else {
return 'Unknown';
}
}
/**
* Get the severity of the given error code.
*
* @param int $code the error code
*
* @return string
*/
public static function getSeverity($code)
{
if (array_key_exists($code, self::$ERROR_TYPES)) {
return self::$ERROR_TYPES[$code]['severity'];
} else {
return 'error';
}
}
/**
* Get the the levels for the given severity.
*
* @param string $severity the given severity
*
* @return int
*/
public static function getLevelsForSeverity($severity)
{
$levels = 0;
foreach (self::$ERROR_TYPES as $level => $info) {
if ($info['severity'] == $severity) {
$levels |= $level;
}
}
return $levels;
}
}

View File

@@ -1,317 +0,0 @@
<?php
class Bugsnag_Notification
{
private static $CONTENT_TYPE_HEADER = 'Content-type: application/json';
/**
* The config instance.
*
* @var Bugsnag_Configuration
*/
private $config;
/**
* The queue of errors to send to Bugsnag.
*
* @var Bugsnag_Error[]
*/
private $errorQueue = array();
/**
* Create a new notification instance.
*
* @param Bugsnag_Configuration $config the configuration instance
*
* @return void
*/
public function __construct(Bugsnag_Configuration $config)
{
$this->config = $config;
}
/**
* Add an error to the queue.
*
* @param Bugsnag_Error $config the bugsnag error instance
* @param array $passedMetaData the associated meta data
*
* @return bool
*/
public function addError(Bugsnag_Error $error, $passedMetaData = array())
{
// Check if this error should be sent to Bugsnag
if (!$this->config->shouldNotify()) {
return false;
}
// Add global meta-data to error
$error->setMetaData($this->config->metaData);
// Add request meta-data to error
if (Bugsnag_Request::isRequest()) {
$error->setMetaData(Bugsnag_Request::getRequestMetaData());
}
// Session Tab
if ($this->config->sendSession && !empty($_SESSION)) {
$error->setMetaData(array('session' => $_SESSION));
}
// Cookies Tab
if ($this->config->sendCookies && !empty($_COOKIE)) {
$error->setMetaData(array('cookies' => $_COOKIE));
}
// Add environment meta-data to error
if ($this->config->sendEnvironment && !empty($_ENV)) {
$error->setMetaData(array('Environment' => $_ENV));
}
// Add user-specified meta-data to error
$error->setMetaData($passedMetaData);
// Run beforeNotify function (can cause more meta-data to be merged)
if (isset($this->config->beforeNotifyFunction) && is_callable($this->config->beforeNotifyFunction)) {
$beforeNotifyReturn = call_user_func($this->config->beforeNotifyFunction, $error);
}
// Skip this error if the beforeNotify function returned FALSE
if (!isset($beforeNotifyReturn) || $beforeNotifyReturn !== false) {
$this->errorQueue[] = $error;
return true;
} else {
return false;
}
}
/**
* Get the array representation.
*
* @return array
*/
public function toArray()
{
$events = array();
foreach ($this->errorQueue as $error) {
$errorArray = $error->toArray();
if (!is_null($errorArray)) {
$events[] = $errorArray;
}
}
return array(
'apiKey' => $this->config->apiKey,
'notifier' => $this->config->notifier,
'events' => $events,
);
}
/**
* Deliver everything on the queue to Bugsnag.
*
* @return void
*/
public function deliver()
{
if (empty($this->errorQueue)) {
return;
}
// Post the request to bugsnag
$this->postJSON($this->config->getNotifyEndpoint(), $this->toArray());
// Clear the error queue
$this->errorQueue = array();
}
/**
* Post the given data to Bugsnag in json form.
*
* @param string $url the url to hit
* @param array $data the data send
*
* @return void
*/
public function postJSON($url, $data)
{
// Try to send the whole lot, or without the meta data for the first
// event. If failed, try to send the first event, and then the rest of
// them, revursively. Decrease by a constant and concquer if you like.
// Note that the base case is satisfied as soon as the payload is small
// enought to send, or when it's simply discarded.
try {
$body = $this->encode($data);
} catch (RuntimeException $e) {
if (count($data['events']) > 1) {
$event = array_shift($data['events']);
$this->postJSON($url, array_merge($data, array('events' => array($event))));
$this->postJSON($url, $data);
} else {
error_log('Bugsnag Warning: '.$e->getMessage());
}
return;
}
// Prefer cURL if it is installed, otherwise fall back to fopen()
// cURL supports both timeouts and proxies
try {
if (function_exists('curl_version')) {
$this->postWithCurl($url, $body);
} elseif (ini_get('allow_url_fopen')) {
$this->postWithFopen($url, $body);
} else {
error_log('Bugsnag Warning: Couldn\'t notify (neither cURL or allow_url_fopen are available on your PHP installation)');
}
} catch (Exception $e) {
error_log('Bugsnag Warning: Couldn\'t notify. '.$e->getMessage());
}
}
/**
* Json encode the given data.
*
* We will also strip out the meta data if it's too large.
*
* @param array $data the data to encode
*
* @throws RuntimeException
*
* @return string
*/
private function encode(array $data)
{
$body = json_encode($data);
if ($this->length($body) > 500000) {
unset($data['events'][0]['metaData']);
}
$body = json_encode($data);
if ($this->length($body) > 500000) {
throw new RuntimeException('Payload too large');
}
return $body;
}
/**
* Get the length of the given string in bytes.
*
* @param string $str the string to get the length of
*
* @return int
*/
private function length($str)
{
return function_exists('mb_strlen') ? mb_strlen($str, '8bit') : strlen($str);
}
/**
* Post the given info to Bugsnag using cURL.
*
* @param string $url the url to hit
* @param string $body the request body
*
* @return void
*/
private function postWithCurl($url, $body)
{
$http = curl_init($url);
// Default curl settings
curl_setopt($http, CURLOPT_HEADER, false);
curl_setopt($http, CURLOPT_RETURNTRANSFER, true);
curl_setopt($http, CURLOPT_POST, true);
curl_setopt($http, CURLOPT_HTTPHEADER, array(self::$CONTENT_TYPE_HEADER, 'Expect:'));
curl_setopt($http, CURLOPT_POSTFIELDS, $body);
curl_setopt($http, CURLOPT_CONNECTTIMEOUT, $this->config->timeout);
curl_setopt($http, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($http, CURLOPT_VERBOSE, false);
curl_setopt($http, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
if (!empty($this->config->curlOptions)) {
foreach ($this->config->curlOptions as $option => $value) {
curl_setopt($http, $option, $value);
}
}
// Apply proxy settings (if present)
if (count($this->config->proxySettings)) {
if (isset($this->config->proxySettings['host'])) {
curl_setopt($http, CURLOPT_PROXY, $this->config->proxySettings['host']);
}
if (isset($this->config->proxySettings['port'])) {
curl_setopt($http, CURLOPT_PROXYPORT, $this->config->proxySettings['port']);
}
if (isset($this->config->proxySettings['user'])) {
$userPassword = $this->config->proxySettings['user'].':';
$userPassword .= isset($this->config->proxySettings['password']) ? $this->config->proxySettings['password'] : '';
curl_setopt($http, CURLOPT_PROXYUSERPWD, $userPassword);
}
}
// Execute the request and fetch the response
$responseBody = curl_exec($http);
$statusCode = curl_getinfo($http, CURLINFO_HTTP_CODE);
if ($statusCode > 200) {
error_log('Bugsnag Warning: Couldn\'t notify ('.$responseBody.')');
if ($this->config->debug) {
error_log('Bugsnag Debug: Attempted to post to URL - "'.$url.'"');
error_log('Bugsnag Debug: Attempted to post payload - "'.$body.'"');
}
}
if (curl_errno($http)) {
error_log('Bugsnag Warning: Couldn\'t notify ('.curl_error($http).')');
}
curl_close($http);
}
/**
* Post the given info to Bugsnag using fopen.
*
* @param string $url the url to hit
* @param string $body the request body
*
* @return void
*/
private function postWithFopen($url, $body)
{
// Warn about lack of proxy support if we are using fopen()
if (count($this->config->proxySettings)) {
error_log('Bugsnag Warning: Can\'t use proxy settings unless cURL is installed');
}
// Create the request context
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => self::$CONTENT_TYPE_HEADER.'\r\n',
'content' => $body,
'timeout' => $this->config->timeout,
),
'ssl' => array(
'verify_peer' => false,
),
));
// Execute the request and fetch the response
if ($stream = fopen($url, 'rb', false, $context)) {
$response = stream_get_contents($stream);
if (!$response) {
error_log('Bugsnag Warning: Couldn\'t notify (no response)');
}
} else {
error_log('Bugsnag Warning: Couldn\'t notify (fopen failed)');
}
}
}

View File

@@ -1,136 +0,0 @@
<?php
class Bugsnag_Request
{
/**
* Are we currently processing a request?
*
* @return bool
*/
public static function isRequest()
{
return isset($_SERVER['REQUEST_METHOD']);
}
/**
* Get the request formatted as meta data.
*
* @return array
*/
public static function getRequestMetaData()
{
static $requestData;
if ($requestData !== null) {
return $requestData;
}
$requestData = array();
$methodsWithPayload = array('PUT');
// Request Tab
$requestData['request'] = array();
$requestData['request']['url'] = self::getCurrentUrl();
if (isset($_SERVER['REQUEST_METHOD'])) {
$requestData['request']['httpMethod'] = $_SERVER['REQUEST_METHOD'];
}
if (!empty($_POST)) {
$requestData['request']['params'] = $_POST;
} else {
$input = file_get_contents('php://input');
if (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'], 'application/json') === 0) {
$requestData['request']['params'] = json_decode($input, true);
}
if (isset($_SERVER['REQUEST_METHOD']) && in_array(strtoupper($_SERVER['REQUEST_METHOD']), $methodsWithPayload)) {
parse_str($input, $params);
if (isset($requestData['request']['params']) && is_array($requestData['request']['params'])) {
$requestData['request']['params'] = array_merge($requestData['request']['params'], $params);
} else {
$requestData['request']['params'] = $params;
}
}
}
$requestData['request']['clientIp'] = self::getRequestIp();
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$requestData['request']['userAgent'] = $_SERVER['HTTP_USER_AGENT'];
}
$headers = self::getRequestHeaders();
if (!empty($headers)) {
$requestData['request']['headers'] = $headers;
}
return $requestData;
}
/**
* Get the request context.
*
* @return string|null
*/
public static function getContext()
{
if (self::isRequest() && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER['REQUEST_URI'])) {
return $_SERVER['REQUEST_METHOD'].' '.strtok($_SERVER['REQUEST_URI'], '?');
}
}
/**
* Get the request id.
*
* @return string|null
*/
public static function getUserId()
{
if (self::isRequest()) {
return self::getRequestIp();
}
}
/**
* Get the request url.
*
* @return string
*/
public static function getCurrentUrl()
{
$schema = ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)) ? 'https://' : 'http://';
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
return $schema.$host.$_SERVER['REQUEST_URI'];
}
/**
* Get the request ip.
*
* @return string
*/
public static function getRequestIp()
{
return isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
}
/**
* Get the request headers.
*
* @return array
*/
public static function getRequestHeaders()
{
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}

View File

@@ -1,209 +0,0 @@
<?php
class Bugsnag_Stacktrace
{
private static $DEFAULT_NUM_LINES = 7;
private static $MAX_LINE_LENGTH = 200;
public $frames = array();
private $config;
/**
* Generate a new stacktrace using the given config.
*
* @param Bugsnag_Configuration $config the configuration instance
*
* @return self
*/
public static function generate($config)
{
// Reduce memory usage by omitting args and objects from backtrace
if (version_compare(PHP_VERSION, '5.3.6') >= 0) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS & ~DEBUG_BACKTRACE_PROVIDE_OBJECT);
} elseif (version_compare(PHP_VERSION, '5.2.5') >= 0) {
$backtrace = debug_backtrace(false);
} else {
$backtrace = debug_backtrace();
}
return self::fromBacktrace($config, $backtrace, '[generator]', 0);
}
/**
* Create a new stacktrace instance from a frame.
*
* @param Bugsnag_Configuration $config the configuration instance
* @param string $file the associated file
* @param int $line the line number
*
* @return self
*/
public static function fromFrame($config, $file, $line)
{
$stacktrace = new self($config);
$stacktrace->addFrame($file, $line, '[unknown]');
return $stacktrace;
}
/**
* Create a new stacktrace instance from a backtrace.
*
* @param Bugsnag_Configuration $config the configuration instance
* @param array $backtrace the associated backtrace
* @param int $topFile the top file to use
* @param int $topLine the top line to use
*
* @return self
*/
public static function fromBacktrace($config, $backtrace, $topFile, $topLine)
{
$stacktrace = new self($config);
// PHP backtrace's are misaligned, we need to shift the file/line down a frame
foreach ($backtrace as $frame) {
if (!self::frameInsideBugsnag($frame)) {
$stacktrace->addFrame(
$topFile,
$topLine,
isset($frame['function']) ? $frame['function'] : null,
isset($frame['class']) ? $frame['class'] : null
);
}
if (isset($frame['file']) && isset($frame['line'])) {
$topFile = $frame['file'];
$topLine = $frame['line'];
} else {
$topFile = '[internal]';
$topLine = 0;
}
}
// Add a final stackframe for the "main" method
$stacktrace->addFrame($topFile, $topLine, '[main]');
return $stacktrace;
}
/**
* Does the given frame internally belong to bugsnag.
*
* @param array $frame the given frame to check
*
* @return bool
*/
public static function frameInsideBugsnag($frame)
{
return isset($frame['class']) && strpos($frame['class'], 'Bugsnag_') === 0;
}
/**
* Create a new stacktrace instance.
*
* @param Bugsnag_Configuration $config the configuration instance
*
* @return void
*/
public function __construct($config)
{
$this->config = $config;
}
/**
* Get the array representation.
*
* @return array
*/
public function toArray()
{
return $this->frames;
}
/**
* Add the given frame to the stacktrace.
*
* @param string $file the associated file
* @param int $line the line number
* @param string $method the method called
* @param string|null $class the associated class
*
* @return void
*/
public function addFrame($file, $line, $method, $class = null)
{
// Account for special "filenames" in eval'd code
$matches = array();
if (preg_match("/^(.*?)\((\d+)\) : (?:eval\(\)'d code|runtime-created function)$/", $file, $matches)) {
$file = $matches[1];
$line = $matches[2];
}
// Construct the frame
$frame = array(
'lineNumber' => (int) $line,
'method' => $class ? "$class::$method" : $method,
);
// Attach some lines of code for context
if ($this->config->sendCode) {
$frame['code'] = $this->getCode($file, $line, self::$DEFAULT_NUM_LINES);
}
// Check if this frame is inProject
$frame['inProject'] = !is_null($this->config->projectRootRegex) && preg_match($this->config->projectRootRegex, $file);
// Strip out projectRoot from start of file path
if (is_null($this->config->stripPathRegex)) {
$frame['file'] = $file;
} else {
$frame['file'] = preg_replace($this->config->stripPathRegex, '', $file);
}
$this->frames[] = $frame;
}
/**
* Extract the code for the given file and lines.
*
* @param string $path the path to the file
* @param int $line the line to centre about
* @param string $numLines the number of lines to fetch
*
* @return string[]|null
*/
private function getCode($path, $line, $numLines)
{
if (empty($path) || empty($line) || !file_exists($path)) {
return;
}
try {
// Get the number of lines in the file
$file = new SplFileObject($path);
$file->seek(PHP_INT_MAX);
$totalLines = $file->key() + 1;
// Work out which lines we should fetch
$start = max($line - floor($numLines / 2), 1);
$end = $start + ($numLines - 1);
if ($end > $totalLines) {
$end = $totalLines;
$start = max($end - ($numLines - 1), 1);
}
// Get the code for this range
$code = array();
$file->seek($start - 1);
while ($file->key() < $end) {
$code[$file->key() + 1] = rtrim(substr($file->current(), 0, self::$MAX_LINE_LENGTH));
$file->next();
}
return $code;
} catch (RuntimeException $ex) {
return;
}
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Report;
use Exception;
class CustomUser
{
/**
* The user resolver.
*
* @var callable
*/
protected $resolver;
/**
* Create a new custom user callback instance.
*
* @param callable $resolver the user resolver
*
* @return void
*/
public function __construct(callable $resolver)
{
$this->resolver = $resolver;
}
/**
* Execute the user data callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
$resolver = $this->resolver;
try {
if ($user = $resolver()) {
$report->setUser($user);
}
} catch (Exception $e) {
// Ignore any errors.
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Report;
class EnvironmentData
{
/**
* Execute the environment data callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
if (!empty($_ENV)) {
$report->setMetaData(['Environment' => $_ENV]);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Configuration;
use Bugsnag\Report;
class GlobalMetaData
{
/**
* The config instance.
*
* @var \Bugsnag\Configuration
*/
protected $config;
/**
* Create a new global meta data callback instance.
*
* @param \Bugsnag\Configuration $config the configuration instance
*
* @return void
*/
public function __construct(Configuration $config)
{
$this->config = $config;
}
/**
* Execute the global meta data callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
if ($data = $this->config->getMetaData()) {
$report->setMetaData($data);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Report;
use Bugsnag\Request\ResolverInterface;
class RequestContext
{
/**
* The request resolver instance.
*
* @var \Bugsnag\Request\ResolverInterface
*/
protected $resolver;
/**
* Create a new request context callback instance.
*
* @param \Bugsnag\Request\ResolverInterface $resolver the request resolver instance
*
* @return void
*/
public function __construct(ResolverInterface $resolver)
{
$this->resolver = $resolver;
}
/**
* Execute the request context callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
if ($context = $this->resolver->resolve()->getContext()) {
$report->setContext($context);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Report;
use Bugsnag\Request\ResolverInterface;
class RequestCookies
{
/**
* The request resolver instance.
*
* @var \Bugsnag\Request\ResolverInterface
*/
protected $resolver;
/**
* Create a new request cookies callback instance.
*
* @param \Bugsnag\Request\ResolverInterface $resolver the request resolver instance
*
* @return void
*/
public function __construct(ResolverInterface $resolver)
{
$this->resolver = $resolver;
}
/**
* Execute the request cookies callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
if ($data = $this->resolver->resolve()->getCookies()) {
$report->setMetaData(['cookies' => $data]);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Report;
use Bugsnag\Request\ResolverInterface;
class RequestMetaData
{
/**
* The request resolver instance.
*
* @var \Bugsnag\Request\ResolverInterface
*/
protected $resolver;
/**
* Create a new request meta data callback instance.
*
* @param \Bugsnag\Request\ResolverInterface $resolver the request resolver instance
*
* @return void
*/
public function __construct(ResolverInterface $resolver)
{
$this->resolver = $resolver;
}
/**
* Execute the request meta data callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
if ($data = $this->resolver->resolve()->getMetaData()) {
$report->setMetaData($data);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Report;
use Bugsnag\Request\ResolverInterface;
class RequestSession
{
/**
* The request resolver instance.
*
* @var \Bugsnag\Request\ResolverInterface
*/
protected $resolver;
/**
* Create a new request session callback instance.
*
* @param \Bugsnag\Request\ResolverInterface $resolver the request resolver instance
*
* @return void
*/
public function __construct(ResolverInterface $resolver)
{
$this->resolver = $resolver;
}
/**
* Execute the request session callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
if ($data = $this->resolver->resolve()->getSession()) {
$report->setMetaData(['session' => $data]);
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Bugsnag\Callbacks;
use Bugsnag\Report;
use Bugsnag\Request\ResolverInterface;
class RequestUser
{
/**
* The request resolver instance.
*
* @var \Bugsnag\Request\ResolverInterface
*/
protected $resolver;
/**
* Create a new request user callback instance.
*
* @param \Bugsnag\Request\ResolverInterface $resolver the request resolver instance
*
* @return void
*/
public function __construct(ResolverInterface $resolver)
{
$this->resolver = $resolver;
}
/**
* Execute the request user callback.
*
* @param \Bugsnag\Report $report the bugsnag report instance
*
* @return void
*/
public function __invoke(Report $report)
{
if ($id = $this->resolver->resolve()->getUserId()) {
$report->setUser(['id' => $id]);
}
}
}

1089
vendor/bugsnag/bugsnag/src/Client.php vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,945 @@
<?php
namespace Bugsnag;
use Bugsnag\Internal\FeatureFlagDelegate;
use InvalidArgumentException;
class Configuration implements FeatureDataStore
{
/**
* The default endpoint for event notifications.
*/
const NOTIFY_ENDPOINT = 'https://notify.bugsnag.com';
/**
* The default endpoint for session tracking.
*/
const SESSION_ENDPOINT = 'https://sessions.bugsnag.com';
/**
* The default endpoint for build notifications.
*/
const BUILD_ENDPOINT = 'https://build.bugsnag.com';
/**
* @var string
*/
protected $apiKey;
/**
* If batch sending is enabled.
*
* @var bool
*/
protected $batchSending = true;
/**
* Which release stages should be allowed to notify.
*
* @var string[]|null
*/
protected $notifyReleaseStages;
/**
* The strings to filter out from metaData.
*
* @deprecated Use redactedKeys instead
*
* @var string[]
*/
protected $filters = [
'password',
'cookie',
'authorization',
'php-auth-user',
'php-auth-pw',
'php-auth-digest',
];
/**
* The project root regex.
*
* @var string
*/
protected $projectRootRegex;
/**
* The strip path regex.
*
* @var string
*/
protected $stripPathRegex;
/**
* If code sending is enabled.
*
* @var bool
*/
protected $sendCode = true;
/**
* The notifier to report as.
*
* @var string[]
*/
protected $notifier = [
'name' => 'Bugsnag PHP (Official)',
'version' => '3.29.0',
'url' => 'https://bugsnag.com',
];
/**
* The fallback app type.
*
* @var string|null
*/
protected $fallbackType;
/**
* The application data.
*
* @var string[]
*/
protected $appData = [];
/**
* The device data.
*
* @var string[]
*/
protected $deviceData = [];
/**
* The meta data.
*
* @var array[]
*/
protected $metaData = [];
/**
* The associated feature flags.
*
* @var FeatureFlagDelegate
*/
private $featureFlags;
/**
* The error reporting level.
*
* @var int|null
*/
protected $errorReportingLevel;
/**
* Whether to track sessions.
*
* @var bool
*/
protected $autoCaptureSessions = false;
/**
* A client to use to send sessions.
*
* @var \GuzzleHttp\ClientInterface|null
*
* @deprecated This will be removed in the next major version.
*/
protected $sessionClient;
/**
* @var string
*/
protected $notifyEndpoint = self::NOTIFY_ENDPOINT;
/**
* @var string
*/
protected $sessionEndpoint = self::SESSION_ENDPOINT;
/**
* @var string
*/
protected $buildEndpoint = self::BUILD_ENDPOINT;
/**
* The amount to increase the memory_limit to handle an OOM.
*
* The default is 5MiB and can be disabled by setting it to 'null'
*
* @var int|null
*/
protected $memoryLimitIncrease = 5242880;
/**
* An array of classes that should not be sent to Bugsnag.
*
* This can contain both fully qualified class names and regular expressions.
*
* @var array
*/
protected $discardClasses = [];
/**
* An array of metadata keys that should be redacted.
*
* @var string[]
*/
protected $redactedKeys = [];
/**
* Create a new config instance.
*
* @param string $apiKey your bugsnag api key
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function __construct($apiKey)
{
if (!is_string($apiKey)) {
throw new InvalidArgumentException('Invalid API key');
}
$this->apiKey = $apiKey;
$this->fallbackType = php_sapi_name();
$this->featureFlags = new FeatureFlagDelegate();
// Add PHP runtime version to device data
$this->mergeDeviceData(['runtimeVersions' => ['php' => phpversion()]]);
}
/**
* Get the Bugsnag API Key.
*
* @return string
*/
public function getApiKey()
{
return $this->apiKey;
}
/**
* Sets whether errors should be batched together and send at the end of each request.
*
* @param bool $batchSending whether to batch together errors
*
* @return $this
*/
public function setBatchSending($batchSending)
{
$this->batchSending = $batchSending;
return $this;
}
/**
* Is batch sending is enabled?
*
* @return bool
*/
public function isBatchSending()
{
return $this->batchSending;
}
/**
* Set which release stages should be allowed to notify Bugsnag.
*
* Eg ['production', 'development'].
*
* @param string[]|null $notifyReleaseStages array of release stages to notify for
*
* @return $this
*/
public function setNotifyReleaseStages(array $notifyReleaseStages = null)
{
$this->notifyReleaseStages = $notifyReleaseStages;
return $this;
}
/**
* Should we notify Bugsnag based on the current release stage?
*
* @return bool
*/
public function shouldNotify()
{
if (!$this->notifyReleaseStages) {
return true;
}
return in_array($this->getAppData()['releaseStage'], $this->notifyReleaseStages, true);
}
/**
* Set the strings to filter out from metaData arrays before sending then.
*
* Eg. ['password', 'credit_card'].
*
* @deprecated Use redactedKeys instead
*
* @param string[] $filters an array of metaData filters
*
* @return $this
*/
public function setFilters(array $filters)
{
$this->filters = $filters;
return $this;
}
/**
* Get the array of metaData filters.
*
* @deprecated Use redactedKeys instead
*
* @return string[]
*/
public function getFilters()
{
return $this->filters;
}
/**
* Set the project root.
*
* @param string|null $projectRoot the project root path
*
* @return void
*/
public function setProjectRoot($projectRoot)
{
$projectRootRegex = $projectRoot ? '/^'.preg_quote($projectRoot, '/').'[\\/]?/i' : null;
$this->setProjectRootRegex($projectRootRegex);
}
/**
* Set the project root regex.
*
* @param string|null $projectRootRegex the project root path
*
* @return void
*/
public function setProjectRootRegex($projectRootRegex)
{
if ($projectRootRegex && @preg_match($projectRootRegex, '') === false) {
throw new InvalidArgumentException('Invalid project root regex: '.$projectRootRegex);
}
$this->projectRootRegex = $projectRootRegex;
$this->setStripPathRegex($projectRootRegex);
}
/**
* Is the given file in the project?
*
* @param string $file
*
* @return bool
*/
public function isInProject($file)
{
return $this->projectRootRegex && preg_match($this->projectRootRegex, $file);
}
/**
* Set the strip path.
*
* @param string|null $stripPath the absolute strip path
*
* @return void
*/
public function setStripPath($stripPath)
{
$stripPathRegex = $stripPath ? '/^'.preg_quote($stripPath, '/').'[\\/]?/i' : null;
$this->setStripPathRegex($stripPathRegex);
}
/**
* Set the regular expression used to strip paths from stacktraces.
*
* @param string|null $stripPathRegex
*
* @return void
*/
public function setStripPathRegex($stripPathRegex)
{
if ($stripPathRegex && @preg_match($stripPathRegex, '') === false) {
throw new InvalidArgumentException('Invalid strip path regex: '.$stripPathRegex);
}
$this->stripPathRegex = $stripPathRegex;
}
/**
* Set the stripped file path.
*
* @param string $file
*
* @return string
*/
public function getStrippedFilePath($file)
{
return $this->stripPathRegex ? preg_replace($this->stripPathRegex, '', $file) : $file;
}
/**
* Set if we should we send a small snippet of the code that crashed.
*
* This can help you diagnose even faster from within your dashboard.
*
* @param bool $sendCode whether to send code to Bugsnag
*
* @return $this
*/
public function setSendCode($sendCode)
{
$this->sendCode = $sendCode;
return $this;
}
/**
* Should we send a small snippet of the code that crashed?
*
* @return bool
*/
public function shouldSendCode()
{
return $this->sendCode;
}
/**
* Sets the notifier to report as to Bugsnag.
*
* This should only be set by other notifier libraries.
*
* @param string[] $notifier an array of name, version, url.
*
* @return $this
*/
public function setNotifier(array $notifier)
{
$this->notifier = $notifier;
return $this;
}
/**
* Get the notifier to report as to Bugsnag.
*
* @return string[]
*/
public function getNotifier()
{
return $this->notifier;
}
/**
* Set your app's semantic version, eg "1.2.3".
*
* @param string|null $appVersion the app's version
*
* @return $this
*/
public function setAppVersion($appVersion)
{
$this->appData['version'] = $appVersion;
return $this;
}
/**
* Set your release stage, eg "production" or "development".
*
* @param string|null $releaseStage the app's current release stage
*
* @return $this
*/
public function setReleaseStage($releaseStage)
{
$this->appData['releaseStage'] = $releaseStage;
return $this;
}
/**
* Set the type of application executing the code.
*
* This is usually used to represent if you are running plain PHP code
* "php", via a framework, eg "laravel", or executing through delayed
* worker code, eg "resque".
*
* @param string|null $type the current type
*
* @return $this
*/
public function setAppType($type)
{
$this->appData['type'] = $type;
return $this;
}
/**
* Set the fallback application type.
*
* This is should be used only by libraries to set an fallback app type.
*
* @param string|null $type the fallback type
*
* @return $this
*/
public function setFallbackType($type)
{
$this->fallbackType = $type;
return $this;
}
/**
* Get the application data.
*
* @return array
*/
public function getAppData()
{
return array_merge(array_filter(['type' => $this->fallbackType, 'releaseStage' => 'production']), array_filter($this->appData));
}
/**
* Set the hostname.
*
* @param string|null $hostname the hostname
*
* @return $this
*/
public function setHostname($hostname)
{
$this->deviceData['hostname'] = $hostname;
return $this;
}
/**
* Adds new data fields to the device data collection.
*
* @param array $data an associative array containing the new data to be added
*
* @return $this
*/
public function mergeDeviceData($data)
{
$this->deviceData = array_merge_recursive($this->deviceData, $data);
return $this;
}
/**
* Get the device data.
*
* @return array
*/
public function getDeviceData()
{
return array_merge($this->getHostname(), array_filter($this->deviceData));
}
/**
* Get the hostname if possible.
*
* @return array
*/
protected function getHostname()
{
$disabled = explode(',', ini_get('disable_functions'));
if (function_exists('php_uname') && !in_array('php_uname', $disabled, true)) {
return ['hostname' => php_uname('n')];
}
if (function_exists('gethostname') && !in_array('gethostname', $disabled, true)) {
return ['hostname' => gethostname()];
}
return [];
}
/**
* Set custom metadata to send to Bugsnag.
*
* You can use this to add custom tabs of data to each error on your
* Bugsnag dashboard.
*
* @param array[] $metaData an array of arrays of custom data
* @param bool $merge should we merge the meta data
*
* @return $this
*/
public function setMetaData(array $metaData, $merge = true)
{
$this->metaData = $merge ? array_merge_recursive($this->metaData, $metaData) : $metaData;
return $this;
}
/**
* Get the custom metadata to send to Bugsnag.
*
* @return array[]
*/
public function getMetaData()
{
return $this->metaData;
}
/**
* Add a single feature flag to all future reports.
*
* @param string $name
* @param string|null $variant
*
* @return void
*/
public function addFeatureFlag($name, $variant = null)
{
$this->featureFlags->add($name, $variant);
}
/**
* Add multiple feature flags to all future reports.
*
* @param FeatureFlag[] $featureFlags
* @phpstan-param list<FeatureFlag> $featureFlags
*
* @return void
*/
public function addFeatureFlags(array $featureFlags)
{
$this->featureFlags->merge($featureFlags);
}
/**
* Remove the feature flag with the given name from all future reports.
*
* @param string $name
*
* @return void
*/
public function clearFeatureFlag($name)
{
$this->featureFlags->remove($name);
}
/**
* Remove all feature flags from all future reports.
*
* @return void
*/
public function clearFeatureFlags()
{
$this->featureFlags->clear();
}
/**
* @internal
*
* @return FeatureFlagDelegate
*/
public function getFeatureFlagsCopy()
{
return clone $this->featureFlags;
}
/**
* Set Bugsnag's error reporting level.
*
* If this is not set, we'll use your current PHP error_reporting value
* from your ini file or error_reporting(...) calls.
*
* @param int|null $errorReportingLevel the error reporting level integer
*
* @return $this
*/
public function setErrorReportingLevel($errorReportingLevel)
{
if (!$this->isSubsetOfErrorReporting($errorReportingLevel)) {
$missingLevels = implode(', ', $this->getMissingErrorLevelNames($errorReportingLevel));
$message =
'Bugsnag Warning: errorReportingLevel cannot contain values that are not in error_reporting. '.
"Any errors of these levels will be ignored: {$missingLevels}.";
error_log($message);
}
$this->errorReportingLevel = $errorReportingLevel;
return $this;
}
/**
* Check if the given error reporting level is a subset of error_reporting.
*
* For example, if $level contains E_WARNING then error_reporting must too.
*
* @param int|null $level
*
* @return bool
*/
private function isSubsetOfErrorReporting($level)
{
if (!is_int($level)) {
return true;
}
$errorReporting = error_reporting();
// If all of the bits in $level are also in $errorReporting, ORing them
// together will result in the same value as $errorReporting because
// there are no new bits to add
return ($errorReporting | $level) === $errorReporting;
}
/**
* Get a list of error level names that are in $level but not error_reporting.
*
* For example, if error_reporting is E_NOTICE and $level is E_ERROR then
* this will return ['E_ERROR']
*
* @param int $level
*
* @return string[]
*/
private function getMissingErrorLevelNames($level)
{
$missingLevels = [];
$errorReporting = error_reporting();
foreach (ErrorTypes::getAllCodes() as $code) {
// $code is "missing" if it's in $level but not in $errorReporting
if (($code & $level) && !($code & $errorReporting)) {
$missingLevels[] = ErrorTypes::codeToString($code);
}
}
return $missingLevels;
}
/**
* Should we ignore the given error code?
*
* @param int $code the error code
*
* @return bool
*/
public function shouldIgnoreErrorCode($code)
{
// If the code is not in error_reporting then it is either totally
// disabled or is being suppressed with '@'
if (!(error_reporting() & $code)) {
return true;
}
// Filter the error code further against our error reporting level, which
// can be lower than error_reporting
if (isset($this->errorReportingLevel)) {
return !($this->errorReportingLevel & $code);
}
return false;
}
/**
* Set event notification endpoint.
*
* @param string $endpoint
*
* @return $this
*/
public function setNotifyEndpoint($endpoint)
{
$this->notifyEndpoint = $endpoint;
return $this;
}
/**
* Get event notification endpoint.
*
* @return string
*/
public function getNotifyEndpoint()
{
return $this->notifyEndpoint;
}
/**
* Set session delivery endpoint.
*
* @param string $endpoint
*
* @return $this
*/
public function setSessionEndpoint($endpoint)
{
$this->sessionEndpoint = $endpoint;
return $this;
}
/**
* Get session delivery endpoint.
*
* @return string
*/
public function getSessionEndpoint()
{
return $this->sessionEndpoint;
}
/**
* Set the build endpoint.
*
* @param string $endpoint the build endpoint
*
* @return $this
*/
public function setBuildEndpoint($endpoint)
{
$this->buildEndpoint = $endpoint;
return $this;
}
/**
* Get the build endpoint.
*
* @return string
*/
public function getBuildEndpoint()
{
return $this->buildEndpoint;
}
/**
* Set session tracking state.
*
* @param bool $track whether to track sessions
*
* @return $this
*/
public function setAutoCaptureSessions($track)
{
$this->autoCaptureSessions = $track;
return $this;
}
/**
* Whether should be auto-capturing sessions.
*
* @return bool
*/
public function shouldCaptureSessions()
{
return $this->autoCaptureSessions;
}
/**
* Get the session client.
*
* @return \GuzzleHttp\ClientInterface
*
* @deprecated This will be removed in the next major version.
*/
public function getSessionClient()
{
if (is_null($this->sessionClient)) {
$this->sessionClient = Client::makeGuzzle($this->sessionEndpoint);
}
return $this->sessionClient;
}
/**
* Set the amount to increase the memory_limit when an OOM is triggered.
*
* This is an amount of bytes or 'null' to disable increasing the limit.
*
* @param int|null $value
*
* @return $this
*/
public function setMemoryLimitIncrease($value)
{
$this->memoryLimitIncrease = $value;
return $this;
}
/**
* Get the amount to increase the memory_limit when an OOM is triggered.
*
* This will return 'null' if this feature is disabled.
*
* @return int|null
*/
public function getMemoryLimitIncrease()
{
return $this->memoryLimitIncrease;
}
/**
* Set the array of classes that should not be sent to Bugsnag.
*
* @param array $discardClasses
*
* @return $this
*/
public function setDiscardClasses(array $discardClasses)
{
$this->discardClasses = $discardClasses;
return $this;
}
/**
* Get the array of classes that should not be sent to Bugsnag.
*
* This can contain both fully qualified class names and regular expressions.
*
* @return array
*/
public function getDiscardClasses()
{
return $this->discardClasses;
}
/**
* Set the array of metadata keys that should be redacted.
*
* @param string[] $redactedKeys
*
* @return $this
*/
public function setRedactedKeys(array $redactedKeys)
{
$this->redactedKeys = $redactedKeys;
return $this;
}
/**
* Get the array of metadata keys that should be redacted.
*
* @return string[]
*/
public function getRedactedKeys()
{
return $this->redactedKeys;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Bugsnag\DateTime;
use DateTimeImmutable;
final class Clock implements ClockInterface
{
/**
* @return DateTimeImmutable
*/
public function now()
{
return new DateTimeImmutable();
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Bugsnag\DateTime;
use DateTimeImmutable;
interface ClockInterface
{
/**
* @return DateTimeImmutable
*/
public function now();
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Bugsnag\DateTime;
use DateTimeImmutable;
final class Date
{
/**
* @return string
*/
public static function now(ClockInterface $clock = null)
{
if ($clock === null) {
$clock = new Clock();
}
$date = $clock->now();
return self::format($date);
}
/**
* @param DateTimeImmutable $date
*
* @return string
*/
private static function format(DateTimeImmutable $date)
{
$dateTime = $date->format('Y-m-d\TH:i:s');
// The milliseconds format character ("v") was introduced in PHP 7.0, so
// we need to take microseconds (PHP 5.2+) and convert to milliseconds
$microseconds = $date->format('u');
$milliseconds = substr($microseconds, 0, 3);
$offset = $date->format('P');
return "{$dateTime}.{$milliseconds}{$offset}";
}
}

70
vendor/bugsnag/bugsnag/src/Env.php vendored Normal file
View File

@@ -0,0 +1,70 @@
<?php
namespace Bugsnag;
class Env
{
/**
* Reads an environment variable from $_ENV, $_SERVER or via getenv(). Supports a thread-safe read via the
* superglobals, but falls back on getenv() to allow for other methods of setting environment data. See this article
* for more background context: https://mattallan.me/posts/how-php-environment-variables-actually-work/.
*
* Copied from phpdotenv: https://github.com/vlucas/phpdotenv/blob/2.6/src/Loader.php#L291. BSD 3-Clause license
* provided at this bottom of this file.
*
* @param string $name
*
* @return mixed
*/
public function get($name)
{
switch (true) {
case array_key_exists($name, $_ENV):
return $_ENV[$name];
case array_key_exists($name, $_SERVER):
return $_SERVER[$name];
default:
$value = getenv($name);
return $value === false ? null : $value; // switch getenv default to null
}
}
}
/*
Referring to Env::get() function above:
The BSD 3-Clause License
http://opensource.org/licenses/BSD-3-Clause
Copyright (c) 2013, Vance Lucas
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Vance Lucas nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

View File

@@ -0,0 +1,224 @@
<?php
namespace Bugsnag;
class ErrorTypes
{
/**
* The error types map.
*
* @var array[]
*/
protected static $ERROR_TYPES = [
E_ERROR => [
'name' => 'PHP Fatal Error',
'severity' => 'error',
],
E_WARNING => [
'name' => 'PHP Warning',
'severity' => 'warning',
],
E_PARSE => [
'name' => 'PHP Parse Error',
'severity' => 'error',
],
E_NOTICE => [
'name' => 'PHP Notice',
'severity' => 'info',
],
E_CORE_ERROR => [
'name' => 'PHP Core Error',
'severity' => 'error',
],
E_CORE_WARNING => [
'name' => 'PHP Core Warning',
'severity' => 'warning',
],
E_COMPILE_ERROR => [
'name' => 'PHP Compile Error',
'severity' => 'error',
],
E_COMPILE_WARNING => [
'name' => 'PHP Compile Warning',
'severity' => 'warning',
],
E_USER_ERROR => [
'name' => 'User Error',
'severity' => 'error',
],
E_USER_WARNING => [
'name' => 'User Warning',
'severity' => 'warning',
],
E_USER_NOTICE => [
'name' => 'User Notice',
'severity' => 'info',
],
E_STRICT => [
'name' => 'PHP Strict',
'severity' => 'info',
],
E_RECOVERABLE_ERROR => [
'name' => 'PHP Recoverable Error',
'severity' => 'error',
],
E_DEPRECATED => [
'name' => 'PHP Deprecated',
'severity' => 'info',
],
E_USER_DEPRECATED => [
'name' => 'User Deprecated',
'severity' => 'info',
],
];
/**
* Is the given error code fatal?
*
* @param int $code the error code
*
* @return bool
*/
public static function isFatal($code)
{
return static::getSeverity($code) === 'error';
}
/**
* Get the name of the given error code.
*
* @param int $code the error code
*
* @return string
*/
public static function getName($code)
{
if (array_key_exists($code, static::$ERROR_TYPES)) {
return static::$ERROR_TYPES[$code]['name'];
}
return 'Unknown';
}
/**
* Get the severity of the given error code.
*
* @param int $code the error code
*
* @return string
*/
public static function getSeverity($code)
{
if (array_key_exists($code, static::$ERROR_TYPES)) {
return static::$ERROR_TYPES[$code]['severity'];
}
return 'error';
}
/**
* Get the the levels for the given severity.
*
* @param string $severity the given severity
*
* @return int
*/
public static function getLevelsForSeverity($severity)
{
$levels = 0;
foreach (static::$ERROR_TYPES as $level => $info) {
if ($info['severity'] == $severity) {
$levels |= $level;
}
}
return $levels;
}
/**
* Get a list of all PHP error codes.
*
* @return int[]
*/
public static function getAllCodes()
{
return array_keys(self::$ERROR_TYPES);
}
/**
* Convert the given error code to a string representation.
*
* For example, E_ERROR => 'E_ERROR'.
*
* @param int $code
*
* @return string
*/
public static function codeToString($code)
{
switch ($code) {
case E_ERROR:
return 'E_ERROR';
case E_WARNING:
return 'E_WARNING';
case E_PARSE:
return 'E_PARSE';
case E_NOTICE:
return 'E_NOTICE';
case E_CORE_ERROR:
return 'E_CORE_ERROR';
case E_CORE_WARNING:
return 'E_CORE_WARNING';
case E_COMPILE_ERROR:
return 'E_COMPILE_ERROR';
case E_COMPILE_WARNING:
return 'E_COMPILE_WARNING';
case E_USER_ERROR:
return 'E_USER_ERROR';
case E_USER_WARNING:
return 'E_USER_WARNING';
case E_USER_NOTICE:
return 'E_USER_NOTICE';
case E_STRICT:
return 'E_STRICT';
case E_RECOVERABLE_ERROR:
return 'E_RECOVERABLE_ERROR';
case E_DEPRECATED:
return 'E_DEPRECATED';
case E_USER_DEPRECATED:
return 'E_USER_DEPRECATED';
default:
return 'Unknown';
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Bugsnag;
interface FeatureDataStore
{
/**
* Add a single feature flag.
*
* @param string $name
* @param string|null $variant
*
* @return void
*/
public function addFeatureFlag($name, $variant = null);
/**
* Add multiple feature flags.
*
* The new flags will be merged with any existing feature flags, with the
* newer variant values taking precedence
*
* @param array $featureFlags
* @phpstan-param list<FeatureFlag> $featureFlags
*
* @return void
*/
public function addFeatureFlags(array $featureFlags);
/**
* Remove a single feature flag by name.
*
* @param string $name
*
* @return void
*/
public function clearFeatureFlag($name);
/**
* Remove all feature flags.
*
* @return void
*/
public function clearFeatureFlags();
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Bugsnag;
final class FeatureFlag
{
/**
* A name that identifies this feature flag.
*
* @var string
*/
private $name;
/**
* An optional variant for this feature flag.
*
* @var string|null
*/
private $variant;
/**
* @param string $name a name that identifies this feature flag
* @param string|null $variant an optional variant for this feature flag.
*/
public function __construct($name, $variant = null)
{
$this->name = $name;
// ensure the variant can only be null or a string as the API only
// accepts strings (null values will be omitted from the payload)
if ($variant !== null && !is_string($variant)) {
$json = json_encode($variant);
// if JSON encoding fails, omit the variant
$variant = $json === false ? null : $json;
}
$this->variant = $variant;
}
/**
* Get the feature flag's name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Get the feature flag's variant.
*
* @return string|null
*/
public function getVariant()
{
return $this->variant;
}
/**
* Convert this feature flag into the format used by the Bugsnag Event API.
*
* This has two forms, either with a variant:
* { "featureFlag": "name", "variant": "variant" }
*
* or if the feature flag has no variant:
* { "featureFlag": "no variant" }
*
* @return array[]
* @phpstan-return array{featureFlag: string, variant?: string}
*/
public function toArray()
{
if (is_string($this->variant)) {
return ['featureFlag' => $this->name, 'variant' => $this->variant];
}
return ['featureFlag' => $this->name];
}
}

342
vendor/bugsnag/bugsnag/src/Handler.php vendored Normal file
View File

@@ -0,0 +1,342 @@
<?php
namespace Bugsnag;
use Exception;
use Throwable;
class Handler
{
/**
* The client instance.
*
* @var \Bugsnag\Client
*/
protected $client;
/**
* The previously registered error handler.
*
* @var callable|null
*/
protected $previousErrorHandler;
/**
* The previously registered exception handler.
*
* @var callable|null
*/
protected $previousExceptionHandler;
/**
* A bit of reserved memory to ensure we are able to increase the memory
* limit on an OOM.
*
* We can't reserve all of the memory that we need to send OOM reports
* because this would have a big overhead on every request, instead of just
* on shutdown in requests with errors.
*
* @var string|null
*/
private $reservedMemory;
/**
* A regex that matches PHP OOM errors.
*
* @var string
*/
private $oomRegex = '/^Allowed memory size of (\d+) bytes exhausted \(tried to allocate \d+ bytes\)/';
/**
* Whether the shutdown handler will run.
*
* This is used to disable the shutdown handler in order to avoid double
* reporting exceptions when trying to run the native PHP exception handler.
*
* @var bool
*/
private static $enableShutdownHandler = true;
/**
* Register our handlers.
*
* @param \Bugsnag\Client|string|null $client client instance or api key
*
* @return static
*/
public static function register($client = null)
{
if (!$client instanceof Client) {
$client = Client::make($client);
}
// @phpstan-ignore-next-line
$handler = new static($client);
$handler->registerBugsnagHandlers(true);
return $handler;
}
/**
* Register our handlers and preserve those previously registered.
*
* @param \Bugsnag\Client|string|null $client client instance or api key
*
* @return static
*
* @deprecated Use {@see Handler::register} instead.
*/
public static function registerWithPrevious($client = null)
{
return self::register($client);
}
/**
* Register our handlers, optionally saving those previously registered.
*
* @param bool $callPrevious whether or not to call the previous handlers
*
* @return void
*/
protected function registerBugsnagHandlers($callPrevious)
{
$this->registerErrorHandler($callPrevious);
$this->registerExceptionHandler($callPrevious);
$this->registerShutdownHandler();
}
/**
* Register the bugsnag error handler and save the returned value.
*
* @param bool $callPrevious whether or not to call the previous handler
*
* @return void
*/
public function registerErrorHandler($callPrevious)
{
$previous = set_error_handler([$this, 'errorHandler']);
if ($callPrevious) {
$this->previousErrorHandler = $previous;
}
}
/**
* Register the bugsnag exception handler and save the returned value.
*
* @param bool $callPrevious whether or not to call the previous handler
*
* @return void
*/
public function registerExceptionHandler($callPrevious)
{
$previous = set_exception_handler([$this, 'exceptionHandler']);
if (!$callPrevious) {
return;
}
// If there is no previous exception handler, we create one that re-raises
// the exception in order to trigger PHP's default exception handler
if (!is_callable($previous)) {
$previous = static function ($throwable) {
throw $throwable;
};
}
$this->previousExceptionHandler = $previous;
}
/**
* Register our shutdown handler.
*
* PHP will call shutdown functions in the order they were registered.
*
* @return void
*/
public function registerShutdownHandler()
{
// Reserve some memory that we can free in the shutdown handler
$this->reservedMemory = str_repeat(' ', 1024 * 32);
register_shutdown_function([$this, 'shutdownHandler']);
}
/**
* Create a new exception handler instance.
*
* @param \Bugsnag\Client $client
*
* @return void
*/
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* Exception handler callback.
*
* @param Throwable $throwable the exception was was thrown
*
* @return void
*/
public function exceptionHandler($throwable)
{
$this->notifyThrowable($throwable);
// If we don't have a previous handler to call, there's nothing left to do
if (!$this->previousExceptionHandler) {
return;
}
// These empty catches exist to set $exceptionFromPreviousHandler — we
// support both PHP 5 & 7 so can't have a single Throwable catch
try {
call_user_func($this->previousExceptionHandler, $throwable);
return;
} catch (Throwable $exceptionFromPreviousHandler) {
// TODO: if we drop support for PHP 5, we can remove this catch, which
// fixes the PHPStan issue here
// @phpstan-ignore-next-line
} catch (Exception $exceptionFromPreviousHandler) {
}
// If the previous handler threw the same exception that we are currently
// handling then it's trying to force PHP's native exception handler to run
// In this case we disable our shutdown handler (to avoid reporting it
// twice) and re-throw the exception
if ($throwable === $exceptionFromPreviousHandler) {
self::$enableShutdownHandler = false;
throw $throwable;
}
// The previous handler raised a new exception so send a notification
// for it too. We don't want the previous handler to run for this
// exception, as it may keep throwing new exceptions
$this->notifyThrowable($exceptionFromPreviousHandler);
}
/**
* Send a notification for the given throwable.
*
* @param Throwable $throwable
*
* @return void
*/
private function notifyThrowable($throwable)
{
$report = Report::fromPHPThrowable(
$this->client->getConfig(),
$throwable
);
$report->setSeverity('error');
$report->setUnhandled(true);
$report->setSeverityReason(['type' => 'unhandledException']);
$this->client->notify($report);
}
/**
* Error handler callback.
*
* @param int $errno the level of the error raised
* @param string $errstr the error message
* @param string $errfile the filename that the error was raised in
* @param int $errline the line number the error was raised at
*
* @return bool
*/
public function errorHandler($errno, $errstr, $errfile = '', $errline = 0)
{
if (!$this->client->getConfig()->shouldIgnoreErrorCode($errno)) {
$report = Report::fromPHPError(
$this->client->getConfig(),
$errno,
$errstr,
$errfile,
$errline,
false
);
$report->setUnhandled(true);
$report->setSeverityReason([
'type' => 'unhandledError',
'attributes' => [
'errorType' => ErrorTypes::getName($errno),
],
]);
$this->client->notify($report);
}
if ($this->previousErrorHandler) {
return call_user_func(
$this->previousErrorHandler,
$errno,
$errstr,
$errfile,
$errline
);
}
return false;
}
/**
* Shutdown handler callback.
*
* @return void
*/
public function shutdownHandler()
{
// Free the reserved memory to give ourselves some room to work
$this->reservedMemory = null;
// If we're disabled, do nothing. This avoids reporting twice if the
// exception handler is forcing the native PHP handler to run
if (!self::$enableShutdownHandler) {
return;
}
$lastError = error_get_last();
// If this is an OOM and memory increase is enabled, bump the memory
// limit so we can report it
if ($lastError !== null
&& $this->client->getMemoryLimitIncrease() !== null
&& preg_match($this->oomRegex, $lastError['message'], $matches) === 1
) {
$currentMemoryLimit = (int) $matches[1];
$newMemoryLimit = $currentMemoryLimit + $this->client->getMemoryLimitIncrease();
ini_set('memory_limit', (string) $newMemoryLimit);
}
// Check if a fatal error caused this shutdown
if (!is_null($lastError) && ErrorTypes::isFatal($lastError['type']) && !$this->client->getConfig()->shouldIgnoreErrorCode($lastError['type'])) {
$report = Report::fromPHPError(
$this->client->getConfig(),
$lastError['type'],
$lastError['message'],
$lastError['file'],
$lastError['line'],
true
);
$report->setSeverity('error');
$report->setUnhandled(true);
$report->setSeverityReason([
'type' => 'unhandledException',
]);
$this->client->notify($report);
}
// Flush any buffered errors
$this->client->flush();
}
}

View File

@@ -0,0 +1,378 @@
<?php
namespace Bugsnag;
use Bugsnag\DateTime\Date;
use Bugsnag\Internal\GuzzleCompat;
use Exception;
use GuzzleHttp\ClientInterface;
use RuntimeException;
class HttpClient
{
/**
* @var \Bugsnag\Configuration
*/
protected $config;
/**
* @var \GuzzleHttp\ClientInterface
*/
protected $guzzle;
/**
* The queue of reports to send.
*
* @var \Bugsnag\Report[]
*/
protected $queue = [];
/**
* The maximum payload size. A whole megabyte (1024 * 1024).
*
* @var int
*/
const MAX_SIZE = 1048576;
/**
* The payload version for the error notification API.
*/
const NOTIFY_PAYLOAD_VERSION = '4.0';
/**
* The payload version for the session API.
*/
const SESSION_PAYLOAD_VERSION = '1.0';
/**
* The payload version for the error notification API.
*
* @deprecated Use {self::NOTIFY_PAYLOAD_VERSION} instead.
*/
const PAYLOAD_VERSION = self::NOTIFY_PAYLOAD_VERSION;
/**
* @param \Bugsnag\Configuration $config
* @param \GuzzleHttp\ClientInterface $guzzle
*/
public function __construct(Configuration $config, ClientInterface $guzzle)
{
$this->config = $config;
$this->guzzle = $guzzle;
}
/**
* Add a report to the queue.
*
* @param \Bugsnag\Report $report
*
* @return void
*/
public function queue(Report $report)
{
$this->queue[] = $report;
}
/**
* Notify Bugsnag of a deployment.
*
* @param array $data the deployment information
*
* @return void
*
* @deprecated Use {@see self::sendBuildReport} instead.
*/
public function deploy(array $data)
{
$app = $this->config->getAppData();
$data['releaseStage'] = $app['releaseStage'];
if (isset($app['version'])) {
$data['appVersion'] = $app['version'];
}
$data['apiKey'] = $this->config->getApiKey();
$uri = rtrim($this->config->getNotifyEndpoint(), '/').'/deploy';
$this->post($uri, ['json' => $data]);
}
/**
* Notify Bugsnag of a build.
*
* @param array $buildInfo the build information
*
* @return void
*/
public function sendBuildReport(array $buildInfo)
{
$app = $this->config->getAppData();
if (!isset($app['version'])) {
error_log('Bugsnag Warning: App version is not set. Unable to send build report.');
return;
}
$data = ['appVersion' => $app['version']];
$sourceControl = [];
if (isset($buildInfo['repository'])) {
$sourceControl['repository'] = $buildInfo['repository'];
}
if (isset($buildInfo['provider'])) {
$sourceControl['provider'] = $buildInfo['provider'];
}
if (isset($buildInfo['revision'])) {
$sourceControl['revision'] = $buildInfo['revision'];
}
if (!empty($sourceControl)) {
$data['sourceControl'] = $sourceControl;
}
if (isset($buildInfo['builder'])) {
$data['builderName'] = $buildInfo['builder'];
} else {
$data['builderName'] = Utils::getBuilderName();
}
if (isset($buildInfo['buildTool'])) {
$data['buildTool'] = $buildInfo['buildTool'];
} else {
$data['buildTool'] = 'bugsnag-php';
}
$data['releaseStage'] = $app['releaseStage'];
$data['apiKey'] = $this->config->getApiKey();
$this->post($this->config->getBuildEndpoint(), ['json' => $data]);
}
/**
* Deliver everything on the queue to Bugsnag.
*
* @return void
*
* @deprecated Use {HttpClient::sendEvents} instead.
*/
public function send()
{
$this->sendEvents();
}
/**
* Deliver everything on the queue to Bugsnag.
*
* @return void
*/
public function sendEvents()
{
if (!$this->queue) {
return;
}
$this->deliverEvents(
$this->config->getNotifyEndpoint(),
$this->getEventPayload()
);
$this->queue = [];
}
/**
* Build the request data to send.
*
* @return array
*
* @deprecated Use {@see HttpClient::getEventPayload} instead.
*/
protected function build()
{
return $this->getEventPayload();
}
/**
* Get the event payload to send.
*
* @return array
*/
protected function getEventPayload()
{
$events = [];
foreach ($this->queue as $report) {
$event = $report->toArray();
if ($event) {
$events[] = $event;
}
}
return [
'apiKey' => $this->config->getApiKey(),
'notifier' => $this->config->getNotifier(),
'events' => $events,
];
}
/**
* Send a session data payload to Bugsnag.
*
* @param array $payload
*
* @return void
*/
public function sendSessions(array $payload)
{
$this->post(
$this->config->getSessionEndpoint(),
[
'json' => $payload,
'headers' => $this->getHeaders(self::SESSION_PAYLOAD_VERSION),
]
);
}
/**
* Builds the array of headers to send.
*
* @param string $version The payload version to use. This defaults to the
* notify payload version if not given. The default
* value should not be relied upon and will be removed
* in the next major release.
*
* @return array
*/
protected function getHeaders($version = self::NOTIFY_PAYLOAD_VERSION)
{
return [
'Bugsnag-Api-Key' => $this->config->getApiKey(),
'Bugsnag-Sent-At' => Date::now(),
'Bugsnag-Payload-Version' => $version,
'Content-Type' => 'application/json',
];
}
/**
* Send a POST request to Bugsnag.
*
* @param string $uri the uri to hit
* @param array $options the request options
*
* @return void
*/
protected function post($uri, array $options = [])
{
if (GuzzleCompat::isUsingGuzzle5()) {
// TODO: validate this by running PHPStan with Guzzle 5
// @phpstan-ignore-next-line
$this->guzzle->post($uri, $options);
} else {
$this->guzzle->request('POST', $uri, $options);
}
}
/**
* Deliver the given events to the notification API.
*
* @param string $uri the uri to hit
* @param array $data the data send
*
* @return void
*
* @deprecated Use {HttpClient::deliverEvents} instead
*/
protected function postJson($uri, array $data)
{
$this->deliverEvents($uri, $data);
}
/**
* Deliver the given events to the notification API.
*
* @param string $uri the uri to hit
* @param array $data the data send
*
* @return void
*/
protected function deliverEvents($uri, array $data)
{
// Try to send the whole lot, or without the meta data for the first
// event. If failed, try to send the first event, and then the rest of
// them, recursively. Decrease by a constant and concquer if you like.
// Note that the base case is satisfied as soon as the payload is small
// enought to send, or when it's simply discarded.
try {
$normalized = $this->normalize($data);
} catch (RuntimeException $e) {
if (count($data['events']) > 1) {
$event = array_shift($data['events']);
$this->deliverEvents($uri, array_merge($data, ['events' => [$event]]));
$this->deliverEvents($uri, $data);
} else {
error_log('Bugsnag Warning: '.$e->getMessage());
}
return;
}
try {
$this->post(
$uri,
[
'body' => $normalized,
'headers' => $this->getHeaders(self::NOTIFY_PAYLOAD_VERSION),
]
);
} catch (Exception $e) {
error_log('Bugsnag Warning: Couldn\'t notify. '.$e->getMessage());
}
}
/**
* Normalize the given data to ensure it's the correct size.
*
* @param array $data the data to normalize
*
* @throws RuntimeException
*
* @return string the JSON encoded data after normalization
*/
protected function normalize(array $data)
{
$body = json_encode($data);
if ($this->length($body) <= static::MAX_SIZE) {
return $body;
}
unset($data['events'][0]['metaData']);
$body = json_encode($data);
if ($this->length($body) > static::MAX_SIZE) {
throw new RuntimeException('Payload too large');
}
return $body;
}
/**
* Get the length of the given string in bytes.
*
* @param string $str the string to get the length of
*
* @return int
*/
protected function length($str)
{
return function_exists('mb_strlen') ? mb_strlen($str, '8bit') : strlen($str);
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Bugsnag\Internal;
use Bugsnag\FeatureFlag;
/**
* @internal
*/
final class FeatureFlagDelegate
{
/**
* @var FeatureFlag[]
* @phpstan-var list<FeatureFlag>
*/
private $storage = [];
/**
* @param string $name
* @param string|null $variant
*
* @return void
*/
public function add($name, $variant)
{
// ensure we're not about to add a duplicate flag
$this->remove($name);
$this->storage[] = new FeatureFlag($name, $variant);
}
/**
* @param FeatureFlag[] $featureFlags
* @phpstan-param list<FeatureFlag> $featureFlags
*
* @return void
*/
public function merge(array $featureFlags)
{
foreach ($featureFlags as $flag) {
if ($flag instanceof FeatureFlag) {
$this->remove($flag->getName());
$this->storage[] = $flag;
}
}
}
/**
* @param string $name
*
* @return void
*/
public function remove($name)
{
foreach ($this->storage as $index => $flag) {
if ($flag->getName() === $name) {
unset($this->storage[$index]);
// reindex the array to prevent holes
$this->storage = array_values($this->storage);
break;
}
}
}
/**
* @return void
*/
public function clear()
{
$this->storage = [];
}
/**
* Get the list of stored feature flags as an array.
*
* @return \Bugsnag\FeatureFlag[]
*/
public function toArray()
{
return $this->storage;
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Bugsnag\Internal;
use GuzzleHttp;
/**
* @internal
*/
final class GuzzleCompat
{
/**
* @return bool
*/
public static function isUsingGuzzle5()
{
if (defined(GuzzleHttp\ClientInterface::class.'::VERSION')) {
$version = constant(GuzzleHttp\ClientInterface::class.'::VERSION');
return version_compare($version, '5.0.0', '>=')
&& version_compare($version, '6.0.0', '<');
}
return false;
}
/**
* Get the base URL/URI option name, which depends on the Guzzle version.
*
* @return string
*/
public static function getBaseUriOptionName()
{
return self::isUsingGuzzle5() ? 'base_url' : 'base_uri';
}
/**
* Get the base URL/URI, which depends on the Guzzle version.
*
* @param GuzzleHttp\ClientInterface $guzzle
*
* @return mixed
*/
public static function getBaseUri(GuzzleHttp\ClientInterface $guzzle)
{
// TODO: validate this by running PHPStan with Guzzle 5
return self::isUsingGuzzle5()
? $guzzle->getBaseUrl() // @phpstan-ignore-line
: $guzzle->getConfig(self::getBaseUriOptionName());
}
/**
* Apply the given $requestOptions to the Guzzle $options array, if they are
* not already set.
*
* The layout of request options differs in Guzzle 5 to 6/7; in Guzzle 5
* request options live in a 'defaults' array, but in 6/7 they are in the
* top level
*
* @param array $options
* @param array $requestOptions
*
* @return array
*/
public static function applyRequestOptions(array $options, array $requestOptions)
{
if (self::isUsingGuzzle5()) {
if (!isset($options['defaults'])) {
$options['defaults'] = [];
}
foreach ($requestOptions as $key => $value) {
if (!isset($options['defaults'][$key])) {
$options['defaults'][$key] = $value;
}
}
return $options;
}
foreach ($requestOptions as $key => $value) {
if (!isset($options[$key])) {
$options[$key] = $value;
}
}
return $options;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Bugsnag\Middleware;
use Bugsnag\Breadcrumbs\Recorder;
use Bugsnag\Report;
class BreadcrumbData
{
/**
* The recorder instance.
*
* @var \Bugsnag\Breadcrumbs\Recorder
*/
protected $recorder;
/**
* Create a new breadcrumb data middleware instance.
*
* @param \Bugsnag\Breadcrumbs\Recorder $recorder the recorder instance
*
* @return void
*/
public function __construct(Recorder $recorder)
{
$this->recorder = $recorder;
}
/**
* Execute the breadcrumb data middleware.
*
* @param \Bugsnag\Report $report the bugsnag report instance
* @param callable $next the next stage callback
*
* @return void
*/
public function __invoke(Report $report, callable $next)
{
foreach ($this->recorder as $breadcrumb) {
$report->addBreadcrumb($breadcrumb);
}
$next($report);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Bugsnag\Middleware;
use Bugsnag\Report;
class CallbackBridge
{
/**
* The callback to run.
*
* @var callable
*/
protected $callback;
/**
* Create a new callback bridge middleware instance.
*
* @param callable $callback the callback to run
*
* @return void
*/
public function __construct(callable $callback)
{
$this->callback = $callback;
}
/**
* Execute the add callback bridge middleware.
*
* @param \Bugsnag\Report $report the bugsnag report instance
* @param callable $next the next stage callback
*
* @return void
*/
public function __invoke(Report $report, callable $next)
{
$initialUnhandled = $report->getUnhandled();
$initialSeverity = $report->getSeverity();
$initialReason = $report->getSeverityReason();
$callback = $this->callback;
if ($callback($report) !== false) {
$report->setUnhandled($initialUnhandled);
if ($report->getSeverity() != $initialSeverity) {
// Severity has been changed via callbacks -> severity reason should be userCallbackSetSeverity
$report->setSeverityReason([
'type' => 'userCallbackSetSeverity',
]);
} else {
// Otherwise we ensure the original severity reason is preserved
$report->setSeverityReason($initialReason);
}
$next($report);
}
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Bugsnag\Middleware;
use Bugsnag\Configuration;
use Bugsnag\Report;
class DiscardClasses
{
/**
* @var \Bugsnag\Configuration
*/
protected $config;
/**
* @param \Bugsnag\Configuration $config
*/
public function __construct(Configuration $config)
{
$this->config = $config;
}
/**
* @param \Bugsnag\Report $report
* @param callable $next
*
* @return void
*/
public function __invoke(Report $report, callable $next)
{
$errors = $report->getErrors();
foreach ($this->config->getDiscardClasses() as $discardClass) {
foreach ($errors as $error) {
if ($error['errorClass'] === $discardClass
|| @preg_match($discardClass, $error['errorClass']) === 1
) {
syslog(LOG_INFO, sprintf(
'Discarding event because error class "%s" matched discardClasses configuration',
$error['errorClass']
));
return;
}
}
}
$next($report);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Bugsnag\Middleware;
use Bugsnag\Configuration;
use Bugsnag\Report;
class NotificationSkipper
{
/**
* The config instance.
*
* @var \Bugsnag\Configuration
*/
protected $config;
/**
* Create a new notification skipper middleware instance.
*
* @param \Bugsnag\Configuration $config the configuration instance
*
* @return void
*/
public function __construct(Configuration $config)
{
$this->config = $config;
}
/**
* Execute the notification skipper middleware.
*
* @param \Bugsnag\Report $report the bugsnag report instance
* @param callable $next the next stage callback
*
* @return void
*/
public function __invoke(Report $report, callable $next)
{
if (!$this->config->shouldNotify()) {
return;
}
$next($report);
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Bugsnag\Middleware;
use Bugsnag\Client;
use Bugsnag\Report;
use Bugsnag\SessionTracker;
class SessionData
{
/**
* @var \Bugsnag\Client
*
* @deprecated This will be removed in the next major version.
* The constructor parameter will also change to {@see SessionTracker}
*/
protected $client;
/**
* @var \Bugsnag\SessionTracker
*/
private $sessionTracker;
/**
* @param \Bugsnag\Client $client
*/
public function __construct(Client $client)
{
$this->client = $client;
$this->sessionTracker = $client->getSessionTracker();
}
/**
* Attaches session information to the Report, if the SessionTracker has a
* current session. Note that this is not the same as the PHP session, but
* refers to the current request.
*
* If the SessionTracker does not have a current session, the report will
* not be changed.
*
* @param \Bugsnag\Report $report
* @param callable $next
*
* @return void
*/
public function __invoke(Report $report, callable $next)
{
$session = $this->sessionTracker->getCurrentSession();
if (isset($session['events'])) {
if ($report->getUnhandled()) {
$session['events']['unhandled'] += 1;
} else {
$session['events']['handled'] += 1;
}
$report->setSessionData($session);
$this->sessionTracker->setCurrentSession($session);
}
$next($report);
}
}

99
vendor/bugsnag/bugsnag/src/Pipeline.php vendored Normal file
View File

@@ -0,0 +1,99 @@
<?php
namespace Bugsnag;
class Pipeline
{
/**
* The array of pipes to pass through.
*
* @var callable[]
*/
protected $pipes;
/**
* Create a new basic pipeline instance.
*
* @param callable[] $pipes the array of pipes to pass through
*
* @return void
*/
public function __construct(array $pipes = [])
{
$this->pipes = $pipes;
}
/**
* Append the given pipe to the pipeline.
*
* @param callable $pipe a new pipe to pass through
*
* @return $this
*/
public function pipe(callable $pipe)
{
$this->pipes[] = $pipe;
return $this;
}
/**
* Add a pipe to the pipeline before a given class.
*
* @param callable $pipe a new pipe to pass through
* @param string $beforeClass to class to insert the pipe before
*
* @return $this
*/
public function insertBefore(callable $pipe, $beforeClass)
{
$beforePosition = null;
foreach ($this->pipes as $index => $callable) {
$class = get_class($callable);
if ($class === $beforeClass) {
$beforePosition = $index;
break;
}
}
if ($beforePosition === null) {
$this->pipes[] = $pipe;
} else {
array_splice($this->pipes, $beforePosition, 0, [$pipe]);
}
return $this;
}
/**
* Run the pipeline.
*
* @param mixed $passable the item to send through the pipeline
* @param callable $destination the final distination callback
*
* @return mixed
*/
public function execute($passable, callable $destination)
{
$first = function ($passable) use ($destination) {
return call_user_func($destination, $passable);
};
$pipes = array_reverse($this->pipes);
return call_user_func(array_reduce($pipes, $this->getSlice(), $first), $passable);
}
/**
* Get the closure that represents a slice.
*
* @return \Closure
*/
protected function getSlice()
{
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
return call_user_func($pipe, $passable, $stack);
};
};
}
}

935
vendor/bugsnag/bugsnag/src/Report.php vendored Normal file
View File

@@ -0,0 +1,935 @@
<?php
namespace Bugsnag;
use BackedEnum;
use Bugsnag\Breadcrumbs\Breadcrumb;
use Bugsnag\DateTime\Date;
use Bugsnag\Internal\FeatureFlagDelegate;
use Exception;
use InvalidArgumentException;
use Throwable;
use UnitEnum;
class Report implements FeatureDataStore
{
/**
* The payload version for the error notification API.
*
* @deprecated Use {HttpClient::NOTIFY_PAYLOAD_VERSION} instead.
*/
const PAYLOAD_VERSION = HttpClient::NOTIFY_PAYLOAD_VERSION;
/**
* The config object.
*
* @var \Bugsnag\Configuration
*/
protected $config;
/**
* The original error.
*
* @var \Throwable|array|null
*/
protected $originalError;
/**
* The associated stacktrace.
*
* @var \Bugsnag\Stacktrace
*/
protected $stacktrace;
/**
* The previous report.
*
* @var \Bugsnag\Report|null
*/
protected $previous;
/**
* The error name.
*
* @var string
*/
protected $name;
/**
* The error message.
*
* @var string|null
*/
protected $message;
/**
* The error severity.
*
* @var string|null
*/
protected $severity;
/**
* The associated context.
*
* @var string|null
*/
protected $context;
/**
* The grouping hash.
*
* @var string|null
*/
protected $groupingHash;
/**
* The associated meta data.
*
* @var array[]
*/
protected $metaData = [];
/**
* The associated feature flags.
*
* @var FeatureFlagDelegate
*/
private $featureFlags;
/**
* The associated user.
*
* @var array
*/
protected $user = [];
/**
* The associated breadcrumbs.
*
* @var array[]
*/
protected $breadcrumbs = [];
/**
* The error time.
*
* @var string
*/
protected $time;
/**
* Whether the error is handled or unhandled.
*
* @var bool
*/
protected $unhandled = false;
/**
* Identifies cause for severity.
*
* @var array[]
*/
protected $severityReason = [];
/**
* Attached session from SessionTracking.
*
* @var array|null
*/
protected $session;
/**
* Create a new report from a PHP error.
*
* @param \Bugsnag\Configuration $config the config instance
* @param int $code the error code
* @param string|null $message the error message
* @param string $file the error file
* @param int $line the error line
* @param bool $fatal if the error was fatal
*
* @return static
*/
public static function fromPHPError(Configuration $config, $code, $message, $file, $line, $fatal = false)
{
// @phpstan-ignore-next-line
$report = new static($config);
$report->setPHPError($code, $message, $file, $line, $fatal)
->setUnhandled(false)
->setSeverityReason(['type' => 'handledError']);
return $report;
}
/**
* Create a new report from a PHP throwable.
*
* @param \Bugsnag\Configuration $config the config instance
* @param \Throwable $throwable the throwable instance
*
* @return static
*/
public static function fromPHPThrowable(Configuration $config, $throwable)
{
// @phpstan-ignore-next-line
$report = new static($config);
$report->setPHPThrowable($throwable)
->setUnhandled(false)
->setSeverityReason(['type' => 'handledException']);
return $report;
}
/**
* Create a new report from a named error.
*
* @param \Bugsnag\Configuration $config the config instance
* @param string $name the error name
* @param string|null $message the error message
*
* @return static
*/
public static function fromNamedError(Configuration $config, $name, $message = null)
{
// @phpstan-ignore-next-line
$report = new static($config);
$report->setName($name)
->setMessage($message)
->setStacktrace(Stacktrace::generate($config))
->setUnhandled(false)
->setSeverityReason(['type' => 'handledError']);
return $report;
}
/**
* Create a new report instance.
*
* This is only for for use only by the static methods above.
*
* @param \Bugsnag\Configuration $config the config instance
*
* @return void
*/
protected function __construct(Configuration $config)
{
$this->config = $config;
$this->time = Date::now();
$this->featureFlags = $config->getFeatureFlagsCopy();
}
/**
* Get the original error.
*
* @return \Throwable|array|null
*/
public function getOriginalError()
{
return $this->originalError;
}
/**
* Set the PHP throwable.
*
* @param \Throwable $throwable the throwable instance
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setPHPThrowable($throwable)
{
// TODO: if we drop support for PHP 5, we can remove this check for
// 'Exception', which fixes the PHPStan issue here
// @phpstan-ignore-next-line
if (!$throwable instanceof Throwable && !$throwable instanceof Exception) {
throw new InvalidArgumentException('The throwable must implement Throwable or extend Exception.');
}
$this->originalError = $throwable;
$this->setName(get_class($throwable))
->setMessage($throwable->getMessage())
->setStacktrace(Stacktrace::fromBacktrace($this->config, $throwable->getTrace(), $throwable->getFile(), $throwable->getLine()));
if (method_exists($throwable, 'getPrevious')) {
$this->setPrevious($throwable->getPrevious());
}
return $this;
}
/**
* Set the PHP error.
*
* @param int $code the error code
* @param string|null $message the error message
* @param string $file the error file
* @param int $line the error line
* @param bool $fatal if the error was fatal
*
* @return $this
*/
public function setPHPError($code, $message, $file, $line, $fatal = false)
{
$this->originalError = [
'code' => $code,
'message' => $message,
'file' => $file,
'line' => $line,
'fatal' => $fatal,
];
if ($fatal) {
// Generating stacktrace for PHP fatal errors is not possible,
// since this code executes when the PHP process shuts down,
// rather than at the time of the crash.
//
// In these situations, we generate a "stacktrace" containing only
// the line and file number where the crash occurred.
$stacktrace = Stacktrace::fromFrame($this->config, $file, $line);
} else {
$stacktrace = Stacktrace::generate($this->config);
}
$this->setName(ErrorTypes::getName($code))
->setMessage($message)
->setSeverity(ErrorTypes::getSeverity($code))
->setStacktrace($stacktrace);
return $this;
}
/**
* Set the bugsnag stacktrace.
*
* @param \Bugsnag\Stacktrace $stacktrace the stacktrace instance
*
* @return $this
*/
protected function setStacktrace(Stacktrace $stacktrace)
{
$this->stacktrace = $stacktrace;
return $this;
}
/**
* Gets the severity reason.
*
* @return array
*/
public function getSeverityReason()
{
if (!array_key_exists('type', $this->severityReason)) {
syslog(LOG_WARNING, 'Severity reason should always have a "type" set');
$this->severityReason['type'] = 'userSpecifiedSeverity';
}
return $this->severityReason;
}
/**
* Sets the unhandled payload.
*
* @return $this
*/
public function setSeverityReason(array $severityReason)
{
$this->severityReason = $severityReason;
return $this;
}
/**
* Sets the unhandled flag.
*
* @param bool $unhandled
*
* @return $this
*/
public function setUnhandled($unhandled)
{
$this->unhandled = $unhandled;
return $this;
}
/**
* Returns the unhandled flag.
*
* @return bool
*/
public function getUnhandled()
{
return $this->unhandled;
}
/**
* Get the bugsnag stacktrace.
*
* @return \Bugsnag\Stacktrace
*/
public function getStacktrace()
{
return $this->stacktrace;
}
/**
* Set the previous throwable.
*
* @param \Throwable $throwable the previous throwable
*
* @return $this
*/
protected function setPrevious($throwable)
{
if ($throwable) {
$this->previous = static::fromPHPThrowable($this->config, $throwable);
}
return $this;
}
/**
* Set the error name.
*
* @param string $name the error name
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setName($name)
{
if (is_scalar($name) || (is_object($name) && method_exists($name, '__toString'))) {
$this->name = (string) $name;
} else {
throw new InvalidArgumentException('The name must be a string.');
}
if ($this->name === '') {
$this->name = 'Error';
}
return $this;
}
/**
* Get the error name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set the error message.
*
* @param string|null $message the error message
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setMessage($message)
{
if ($message === null) {
$this->message = null;
} elseif (
is_scalar($message)
|| (is_object($message) && method_exists($message, '__toString'))
) {
$this->message = (string) $message;
} else {
throw new InvalidArgumentException('The message must be a string.');
}
return $this;
}
/**
* Get the error message.
*
* @return string|null
*/
public function getMessage()
{
return $this->message;
}
/**
* Set the error severity.
*
* @param string|null $severity the error severity
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setSeverity($severity)
{
if (in_array($severity, ['error', 'warning', 'info', null], true)) {
$this->severity = $severity;
} else {
throw new InvalidArgumentException('The severity must be either "error", "warning", or "info".');
}
return $this;
}
/**
* Get the error severity.
*
* @return string
*/
public function getSeverity()
{
return $this->severity ?: 'warning';
}
/**
* Set a context representing the current type of request, or location in code.
*
* @param string|null $context the current context
*
* @return $this
*/
public function setContext($context)
{
$this->context = $context;
return $this;
}
/**
* Get the error context.
*
* @return string|null
*/
public function getContext()
{
return $this->context;
}
/**
* Set the grouping hash.
*
* @param string|null $groupingHash the grouping hash
*
* @return $this
*/
public function setGroupingHash($groupingHash)
{
$this->groupingHash = $groupingHash;
return $this;
}
/**
* Get the grouping hash.
*
* @return string|null
*/
public function getGroupingHash()
{
return $this->groupingHash;
}
/**
* Set the error meta data.
*
* @param array[] $metaData an array of arrays of custom data
* @param bool $merge should we merge the meta data
*
* @return $this
*/
public function setMetaData(array $metaData, $merge = true)
{
$this->metaData = $merge ? array_merge_recursive($this->metaData, $metaData) : $metaData;
return $this;
}
/**
* Adds a tab to the meta data.
* Conflicting keys will be merged if able, otherwise the new values will be accepted.
* Null values will be deleted from the metadata.
*
* @param array[] $metadata an array of custom data to attach to the report
*
* @return $this
*/
public function addMetaData(array $metadata)
{
$this->metaData = array_replace_recursive($this->metaData, $metadata);
$this->metaData = $this->removeNullElements($this->metaData);
return $this;
}
/**
* Get the error meta data.
*
* @return array[]
*/
public function getMetaData()
{
return $this->metaData;
}
/**
* Add a single feature flag to this report.
*
* @param string $name
* @param string|null $variant
*
* @return void
*/
public function addFeatureFlag($name, $variant = null)
{
$this->featureFlags->add($name, $variant);
}
/**
* Add multiple feature flags to this report.
*
* @param FeatureFlag[] $featureFlags
* @phpstan-param list<FeatureFlag> $featureFlags
*
* @return void
*/
public function addFeatureFlags(array $featureFlags)
{
$this->featureFlags->merge($featureFlags);
}
/**
* Remove the feature flag with the given name from this report.
*
* @param string $name
*
* @return void
*/
public function clearFeatureFlag($name)
{
$this->featureFlags->remove($name);
}
/**
* Remove all feature flags from this report.
*
* @return void
*/
public function clearFeatureFlags()
{
$this->featureFlags->clear();
}
/**
* Get the list of feature flags for this report.
*
* @return \Bugsnag\FeatureFlag[]
*/
public function getFeatureFlags()
{
return $this->featureFlags->toArray();
}
/**
* Set the current user.
*
* @param array $user the current user
*
* @return $this
*/
public function setUser(array $user)
{
$this->user = $user;
return $this;
}
/**
* Get the current user.
*
* @return array
*/
public function getUser()
{
return $this->user;
}
/**
* Add a breadcrumb to the report.
*
* @param \Bugsnag\Breadcrumbs\Breadcrumb $breadcrumb
*
* @return void
*/
public function addBreadcrumb(Breadcrumb $breadcrumb)
{
$data = $breadcrumb->toArray();
if ($metaData = $this->cleanupObj($breadcrumb->getMetaData(), true)) {
$data['metaData'] = $metaData;
if (strlen(json_encode($data)) > Breadcrumb::MAX_SIZE) {
unset($data['metaData']);
}
}
$this->breadcrumbs[] = $data;
}
/**
* Get the report summary.
*
* @return string[]
*/
public function getSummary()
{
$summary = [];
$name = $this->getName();
$message = $this->getMessage();
if ($name !== $message) {
$summary['name'] = $name;
}
$summary['message'] = $message;
$summary['severity'] = $this->getSeverity();
return array_filter($summary);
}
/**
* Sets the session data.
*
* @return void
*/
public function setSessionData(array $session)
{
$this->session = $session;
}
/**
* Get a list of all errors in a fixed format of:
* - 'errorClass'
* - 'errorMessage'
* - 'type' (always 'php').
*
* @return array
*/
public function getErrors()
{
$errors = [$this->toError()];
$previous = $this->previous;
while ($previous) {
$errors[] = $previous->toError();
$previous = $previous->previous;
}
return $errors;
}
/**
* @return array
*/
private function toError()
{
return [
'errorClass' => $this->name,
'errorMessage' => $this->message,
'type' => 'php',
];
}
/**
* Get the array representation.
*
* @return array
*/
public function toArray()
{
$event = [
'app' => $this->config->getAppData(),
'device' => array_merge(['time' => $this->time], $this->config->getDeviceData()),
'user' => $this->getUser(),
'context' => $this->getContext(),
'payloadVersion' => HttpClient::NOTIFY_PAYLOAD_VERSION,
'severity' => $this->getSeverity(),
'exceptions' => $this->exceptionArray(),
'breadcrumbs' => $this->breadcrumbs,
'metaData' => $this->cleanupObj($this->getMetaData(), true),
'unhandled' => $this->getUnhandled(),
'severityReason' => $this->getSeverityReason(),
'featureFlags' => array_map(
function (FeatureFlag $flag) {
return $flag->toArray();
},
$this->featureFlags->toArray()
),
];
if ($hash = $this->getGroupingHash()) {
$event['groupingHash'] = $hash;
}
if (isset($this->session)) {
$event['session'] = $this->session;
}
return $event;
}
/**
* Get the exception array.
*
* @return array
*/
protected function exceptionArray()
{
$exceptionArray = [$this->exceptionObject()];
$previous = $this->previous;
while ($previous) {
$exceptionArray[] = $previous->exceptionObject();
$previous = $previous->previous;
}
return $this->cleanupObj($exceptionArray, false);
}
/**
* Get serializable representation of the exception causing this report.
*
* @return array
*/
protected function exceptionObject()
{
return [
'errorClass' => $this->name,
'message' => $this->message,
'stacktrace' => $this->stacktrace->toArray(),
];
}
/**
* Cleanup the given object.
*
* @param mixed $obj the data to cleanup
* @param bool $isMetaData if it is meta data
*
* @return mixed
*/
protected function cleanupObj($obj, $isMetaData)
{
if (is_null($obj)) {
return null;
}
if (is_array($obj)) {
$clean = [];
foreach ($obj as $key => $value) {
$clean[$key] = $this->shouldFilter($key, $isMetaData) ? '[FILTERED]' : $this->cleanupObj($value, $isMetaData);
}
return $clean;
}
if (is_string($obj)) {
return (function_exists('mb_detect_encoding') && !mb_detect_encoding($obj, 'UTF-8', true)) ? utf8_encode($obj) : $obj;
}
if (is_object($obj)) {
if ($obj instanceof UnitEnum) {
return $this->enumToString($obj);
}
return $this->cleanupObj(json_decode(json_encode($obj), true), $isMetaData);
}
return $obj;
}
/**
* Should we filter the given element.
*
* @param string $key the associated key
* @param bool $isMetaData if it is meta data
*
* @return bool
*/
protected function shouldFilter($key, $isMetaData)
{
if (!$isMetaData) {
return false;
}
foreach ($this->config->getFilters() as $filter) {
if (stripos($key, $filter) !== false) {
return true;
}
}
foreach ($this->config->getRedactedKeys() as $redactedKey) {
if (@preg_match($redactedKey, $key) === 1) {
return true;
} elseif (Utils::stringCaseEquals($redactedKey, $key)) {
return true;
}
}
return false;
}
/**
* Recursively remove null elements.
*
* @param array $array the array to remove null elements from
*
* @return array
*/
protected function removeNullElements($array)
{
foreach ($array as $key => $val) {
if (is_array($val)) {
$array[$key] = $this->removeNullElements($val);
} elseif (is_null($val)) {
unset($array[$key]);
}
}
return $array;
}
/**
* Convert the given enum to a string.
*
* @param UnitEnum $enum
*
* @return string
*/
private function enumToString(UnitEnum $enum)
{
// e.g. My\Enum::SomeCase
$string = sprintf('%s::%s', get_class($enum), $enum->name);
// add the value, if there is one
if ($enum instanceof BackedEnum) {
$string .= sprintf(' (%s)', $enum->value);
}
return $string;
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace Bugsnag\Request;
class BasicResolver implements ResolverInterface
{
/**
* Resolve the current request.
*
* @return \Bugsnag\Request\RequestInterface
*/
public function resolve()
{
if (isset($_SERVER['REQUEST_METHOD'])) {
if (strtoupper($_SERVER['REQUEST_METHOD']) === 'GET') {
$params = static::getInputParams($_SERVER, $_GET, false);
} else {
$params = static::getInputParams($_SERVER, $_POST, true);
}
return new PhpRequest(
$_SERVER,
empty($_SESSION) ? [] : $_SESSION,
empty($_COOKIE) ? [] : $_COOKIE,
static::getRequestHeaders($_SERVER),
$params
);
}
if (PHP_SAPI === 'cli' && isset($_SERVER['argv'])) {
return new ConsoleRequest($_SERVER['argv']);
}
return new NullRequest();
}
/**
* Get the request headers.
*
* Note how we're caching this result for ever, across all instances.
*
* This is because PHP is natively only designed to process one request,
* then shutdown. Some applications can be designed to handle multiple
* requests using their own request objects, thus will need to implement
* their own bugsnag request resolver.
*
* @param array $server the server variables
*
* @return array
*/
protected static function getRequestHeaders(array $server)
{
static $headers;
if ($headers !== null) {
return $headers;
}
if (function_exists('getallheaders')) {
return getallheaders();
}
$headers = [];
foreach ($server as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
} elseif ($name === 'CONTENT_TYPE') {
$headers['Content-Type'] = $value;
} elseif ($name === 'CONTENT_LENGTH') {
$headers['Content-Length'] = $value;
}
}
return $headers;
}
/**
* Get the input params.
*
* Note how we're caching this result for ever, across all instances.
*
* This is because the input stream can only be read once on PHP 5.5, and
* PHP is natively only designed to process one request, then shutdown.
* Some applications can be designed to handle multiple requests using
* their own request objects, thus will need to implement their own bugsnag
* request resolver.
*
* @param array $server the server variables
* @param array $params the array of parameters for this request type
* @param bool $fallbackToInput if true, uses input when params is null
*
* @return array|null
*/
protected static function getInputParams(array $server, array $params, $fallbackToInput = false)
{
static $result;
if ($result !== null) {
return $result ?: null;
}
$result = $params;
if ($fallbackToInput === true) {
$result = $result ?: static::parseInput($server, static::readInput());
}
return $result ?: null;
}
/**
* Read the PHP input stream.
*
* @return string|false
*/
protected static function readInput()
{
return file_get_contents('php://input') ?: false;
}
/**
* Parse the given input string.
*
* @param array $server the server variables
* @param string|null $input the http request input
*
* @return array|null
*/
protected static function parseInput(array $server, $input)
{
if (!$input) {
return null;
}
if (isset($server['CONTENT_TYPE']) && stripos($server['CONTENT_TYPE'], 'application/json') === 0) {
return (array) json_decode($input, true) ?: null;
}
if (strtoupper($server['REQUEST_METHOD']) === 'PUT') {
parse_str($input, $params);
return (array) $params ?: null;
}
return null;
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace Bugsnag\Request;
class ConsoleRequest implements RequestInterface
{
/**
* The unformated console command.
*
* @var string[]
*/
protected $command;
/**
* Create a new console request instance.
*
* @param string[] $command an array of the console command input
*
* @return void
*/
public function __construct(array $command)
{
$this->command = $command;
}
/**
* Are we currently processing a request?
*
* @return bool
*/
public function isRequest()
{
return false;
}
/**
* Get the session data.
*
* @return array
*/
public function getSession()
{
return [];
}
/**
* Get the cookies.
*
* @return array
*/
public function getCookies()
{
return [];
}
/**
* Get the request formatted as meta data.
*
* @return array
*/
public function getMetaData()
{
if (count($this->command) == 0) {
return ['console' => [
'Command' => 'Command could not be retrieved', ],
];
}
$commandString = implode(' ', $this->command);
$primaryCommand = $this->command[0];
$arguments = [];
$options = [];
foreach (array_slice($this->command, 1) as $arg) {
if (isset($arg[0]) && $arg[0] === '-') {
$options[] = $arg;
} else {
$arguments[] = $arg;
}
}
$data = [
'Input' => $commandString,
'Command' => $primaryCommand,
'Arguments' => $arguments,
'Options' => $options,
];
return ['console' => $data];
}
/**
* Get the request context.
*
* @return string|null
*/
public function getContext()
{
return implode(' ', array_slice($this->command, 0, 4));
}
/**
* Get the request user id.
*
* @return string|null
*/
public function getUserId()
{
return null;
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Bugsnag\Request;
class NullRequest implements RequestInterface
{
/**
* Are we currently processing a request?
*
* @return bool
*/
public function isRequest()
{
return false;
}
/**
* Get the session data.
*
* @return array
*/
public function getSession()
{
return [];
}
/**
* Get the cookies.
*
* @return array
*/
public function getCookies()
{
return [];
}
/**
* Get the request formatted as meta data.
*
* @return array
*/
public function getMetaData()
{
return [];
}
/**
* Get the request context.
*
* @return string|null
*/
public function getContext()
{
return null;
}
/**
* Get the request user id.
*
* @return string|null
*/
public function getUserId()
{
return null;
}
}

View File

@@ -0,0 +1,177 @@
<?php
namespace Bugsnag\Request;
class PhpRequest implements RequestInterface
{
/**
* The server variables.
*
* @var array
*/
protected $server;
/**
* The session variables.
*
* @var array
*/
protected $session;
/**
* The cookie variables.
*
* @var array
*/
protected $cookies;
/**
* The http headers.
*
* @var array
*/
protected $headers;
/**
* The input params.
*
* @var array|null
*/
protected $input;
/**
* Create a new php request instance.
*
* @param array $server the server variables
* @param array $session the session variables
* @param array $cookies the cookie variables
* @param array $headers the http headers
* @param array|null $input the input params
*
* @return void
*/
public function __construct(array $server, array $session, array $cookies, array $headers, array $input = null)
{
$this->server = $server;
$this->session = $session;
$this->cookies = $cookies;
$this->headers = $headers;
$this->input = $input;
}
/**
* Are we currently processing a request?
*
* @return bool
*/
public function isRequest()
{
return true;
}
/**
* Get the session data.
*
* @return array
*/
public function getSession()
{
return $this->session;
}
/**
* Get the cookies.
*
* @return array
*/
public function getCookies()
{
return $this->cookies;
}
/**
* Get the request formatted as meta data.
*
* @return array
*/
public function getMetaData()
{
$data = [];
$data['url'] = $this->getCurrentUrl();
if (isset($this->server['REQUEST_METHOD'])) {
$data['httpMethod'] = $this->server['REQUEST_METHOD'];
}
$data['params'] = $this->input;
$data['clientIp'] = $this->getRequestIp();
if (isset($this->server['HTTP_USER_AGENT'])) {
$data['userAgent'] = $this->server['HTTP_USER_AGENT'];
}
if ($this->headers) {
$data['headers'] = $this->headers;
}
return ['request' => $data];
}
/**
* Get the request context.
*
* @return string|null
*/
public function getContext()
{
if (isset($this->server['REQUEST_METHOD']) && isset($this->server['REQUEST_URI'])) {
return $this->server['REQUEST_METHOD'].' '.strtok($this->server['REQUEST_URI'], '?');
}
return null;
}
/**
* Get the request user id.
*
* @return string|null
*/
public function getUserId()
{
return $this->getRequestIp();
}
/**
* Get the request url.
*
* @return string
*/
protected function getCurrentUrl()
{
$schema = ((!empty($this->server['HTTPS']) && $this->server['HTTPS'] !== 'off') || (!empty($this->server['SERVER_PORT']) && $this->server['SERVER_PORT'] == 443)) ? 'https://' : 'http://';
$host = isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost';
return $schema.$host.$this->server['REQUEST_URI'];
}
/**
* Get the request ip.
*
* @return string|null
*/
protected function getRequestIp()
{
if (isset($this->server['HTTP_X_FORWARDED_FOR'])) {
return $this->server['HTTP_X_FORWARDED_FOR'];
}
if (isset($this->server['REMOTE_ADDR'])) {
return $this->server['REMOTE_ADDR'];
}
return null;
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Bugsnag\Request;
interface RequestInterface
{
/**
* Are we currently processing a request?
*
* @return bool
*/
public function isRequest();
/**
* Get the session data.
*
* @return array
*/
public function getSession();
/**
* Get the cookies.
*
* @return array
*/
public function getCookies();
/**
* Get the request formatted as meta data.
*
* @return array
*/
public function getMetaData();
/**
* Get the request context.
*
* @return string|null
*/
public function getContext();
/**
* Get the request user id.
*
* @return string|null
*/
public function getUserId();
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Bugsnag\Request;
interface ResolverInterface
{
/**
* Resolve the current request.
*
* @return \Bugsnag\Request\RequestInterface
*/
public function resolve();
}

View File

@@ -0,0 +1,453 @@
<?php
namespace Bugsnag;
use Exception;
use InvalidArgumentException;
class SessionTracker
{
/**
* The current session payload version.
*
* @deprecated Use {HttpClient::SESSION_PAYLOAD_VERSION} instead.
*
* @var string
*/
protected static $SESSION_PAYLOAD_VERSION = HttpClient::SESSION_PAYLOAD_VERSION;
/**
* The amount of time between each sending attempt.
*
* @var int
*/
protected static $DELIVERY_INTERVAL = 30;
/**
* The maximum amount of sessions to hold onto.
*
* @var int
*/
protected static $MAX_SESSION_COUNT = 50;
/**
* The key for storing session counts.
*
* @var string
*/
protected static $SESSION_COUNTS_KEY = 'bugsnag-session-counts';
/**
* The key for storing last sent data.
*
* @var string
*/
protected static $SESSIONS_LAST_SENT_KEY = 'bugsnag-sessions-last-sent';
/**
* @var Configuration
*/
protected $config;
/**
* @var HttpClient
*/
protected $http;
/**
* An array of session counts.
*
* @var array
*/
protected $sessionCounts = [];
/**
* A locking function for synchronisation.
*
* @var callable|null
*/
protected $lockFunction = null;
/**
* An unlocking function for synchronisation.
*
* @var callable|null
*/
protected $unlockFunction = null;
/**
* A function to use when retrying a failed delivery.
*
* @var callable|null
*/
protected $retryFunction = null;
/**
* A function to store/get data.
*
* @var callable|null
*/
protected $storageFunction = null;
/**
* A function to store/get sessions.
*
* @var callable|null
*/
protected $sessionFunction = null;
/**
* The last time the sessions were delivered.
*
* @var int
*/
protected $lastSent = 0;
/**
* The current session.
*
* @var array
*/
protected $currentSession = [];
/**
* @param Configuration $config
* @param HttpClient|null $http A HttpClient instance to use. Passing null
* is deprecated and $http will be required
* in the next major version.
*/
public function __construct(Configuration $config, HttpClient $http = null)
{
$this->config = $config;
$this->http = $http === null
? new HttpClient($config, $config->getSessionClient())
: $http;
}
/**
* @param Configuration $config
*
* @return void
*
* @deprecated Change the Configuration via the Client object instead.
*/
public function setConfig(Configuration $config)
{
$this->config = $config;
}
/**
* @return void
*/
public function startSession()
{
$currentTime = date('Y-m-d\TH:i:00');
$session = [
'id' => uniqid('', true),
'startedAt' => $currentTime,
'events' => [
'handled' => 0,
'unhandled' => 0,
],
];
$this->setCurrentSession($session);
$this->incrementSessions($currentTime);
}
/**
* @param array $session
*
* @return void
*/
public function setCurrentSession(array $session)
{
if (is_callable($this->sessionFunction)) {
call_user_func($this->sessionFunction, $session);
} else {
$this->currentSession = $session;
}
}
/**
* @return array
*/
public function getCurrentSession()
{
if (is_callable($this->sessionFunction)) {
$currentSession = call_user_func($this->sessionFunction);
if (is_array($currentSession)) {
return $currentSession;
}
return [];
}
return $this->currentSession;
}
/**
* @return void
*/
public function sendSessions()
{
$locked = false;
if (is_callable($this->lockFunction) && is_callable($this->unlockFunction)) {
call_user_func($this->lockFunction);
$locked = true;
}
try {
$this->deliverSessions();
} finally {
if ($locked) {
call_user_func($this->unlockFunction);
}
}
}
/**
* @param callable $lock
* @param callable $unlock
*
* @return void
*/
public function setLockFunctions($lock, $unlock)
{
if (!is_callable($lock) || !is_callable($unlock)) {
throw new InvalidArgumentException('Both lock and unlock functions must be callable');
}
$this->lockFunction = $lock;
$this->unlockFunction = $unlock;
}
/**
* @param callable $function
*
* @return void
*/
public function setRetryFunction($function)
{
if (!is_callable($function)) {
throw new InvalidArgumentException('The retry function must be callable');
}
$this->retryFunction = $function;
}
/**
* @param callable $function
*
* @return void
*/
public function setStorageFunction($function)
{
if (!is_callable($function)) {
throw new InvalidArgumentException('Storage function must be callable');
}
$this->storageFunction = $function;
}
/**
* @param callable $function
*
* @return void
*/
public function setSessionFunction($function)
{
if (!is_callable($function)) {
throw new InvalidArgumentException('Session function must be callable');
}
$this->sessionFunction = $function;
}
/**
* @param string $minute
* @param int $count
* @param bool $deliver
*
* @return void
*/
protected function incrementSessions($minute, $count = 1, $deliver = true)
{
$locked = false;
if (is_callable($this->lockFunction) && is_callable($this->unlockFunction)) {
call_user_func($this->lockFunction);
$locked = true;
}
try {
$sessionCounts = $this->getSessionCounts();
if (array_key_exists($minute, $sessionCounts)) {
$sessionCounts[$minute] += $count;
} else {
$sessionCounts[$minute] = $count;
}
$this->setSessionCounts($sessionCounts);
if (count($sessionCounts) > self::$MAX_SESSION_COUNT) {
$this->trimOldestSessions();
}
$lastSent = $this->getLastSent();
if ($deliver && ((time() - $lastSent) > self::$DELIVERY_INTERVAL)) {
$this->deliverSessions();
}
} finally {
if ($locked) {
call_user_func($this->unlockFunction);
}
}
}
/**
* @return array
*/
protected function getSessionCounts()
{
if (is_callable($this->storageFunction)) {
$sessionCounts = call_user_func($this->storageFunction, self::$SESSION_COUNTS_KEY);
if (is_array($sessionCounts)) {
return $sessionCounts;
}
return [];
}
return $this->sessionCounts;
}
/**
* @param array $sessionCounts
*
* @return void
*/
protected function setSessionCounts(array $sessionCounts)
{
if (is_callable($this->storageFunction)) {
call_user_func($this->storageFunction, self::$SESSION_COUNTS_KEY, $sessionCounts);
}
$this->sessionCounts = $sessionCounts;
}
/**
* @return void
*/
protected function trimOldestSessions()
{
$sessions = $this->getSessionCounts();
// Sort the session counts so that the oldest minutes are first
// i.e. '2000-01-01T00:00:00' should be after '2000-01-01T00:01:00'
uksort($sessions, function ($a, $b) {
return strtotime($b) - strtotime($a);
});
$sessionCounts = array_slice($sessions, 0, self::$MAX_SESSION_COUNT);
$this->setSessionCounts($sessionCounts);
}
/**
* @param array $sessions
*
* @return array
*/
protected function constructPayload(array $sessions)
{
$formattedSessions = [];
foreach ($sessions as $minute => $count) {
$formattedSessions[] = ['startedAt' => $minute, 'sessionsStarted' => $count];
}
return [
'notifier' => $this->config->getNotifier(),
'device' => $this->config->getDeviceData(),
'app' => $this->config->getAppData(),
'sessionCounts' => $formattedSessions,
];
}
/**
* @return void
*/
protected function deliverSessions()
{
$sessions = $this->getSessionCounts();
$this->setSessionCounts([]);
if (count($sessions) === 0) {
return;
}
if (!$this->config->shouldNotify()) {
return;
}
$payload = $this->constructPayload($sessions);
$this->setLastSent();
try {
$this->http->sendSessions($payload);
} catch (Exception $e) {
error_log('Bugsnag Warning: Couldn\'t notify. '.$e->getMessage());
if (is_callable($this->retryFunction)) {
call_user_func($this->retryFunction, $sessions);
} else {
foreach ($sessions as $minute => $count) {
$this->incrementSessions($minute, $count, false);
}
}
}
}
/**
* @return void
*/
protected function setLastSent()
{
$time = time();
if (is_callable($this->storageFunction)) {
call_user_func($this->storageFunction, self::$SESSIONS_LAST_SENT_KEY, $time);
} else {
$this->lastSent = $time;
}
}
/**
* @return int
*/
protected function getLastSent()
{
if (is_callable($this->storageFunction)) {
$lastSent = call_user_func($this->storageFunction, self::$SESSIONS_LAST_SENT_KEY);
// $lastSent may be a string despite us storing an integer because
// some storage backends will convert all values into strings
// note: some invalid integers pass 'is_numeric' (e.g. bigger than
// PHP_INT_MAX) but these get cast to '0', which is the default anyway
if (is_numeric($lastSent)) {
return (int) $lastSent;
}
return 0;
}
return $this->lastSent;
}
}

Some files were not shown because too many files have changed in this diff Show More