├── .dockerignore
├── .github
├── pull_request_template.md
└── workflows
│ ├── ci-1.x.yml
│ ├── ci-2.x.yml
│ ├── ci-3.x.yml
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── EXAMPLES.md
├── LICENSE
├── POST_FORMAT.md
├── README.md
├── THANKS.md
├── TODO.md
├── composer.json
├── data
└── rollbar.snippet.js
├── phpunit.xml
├── psalm.baseline
├── psalm.xml
├── src
├── Config.php
├── DataBuilder.php
├── DataBuilderInterface.php
├── Defaults.php
├── ErrorWrapper.php
├── FilterInterface.php
├── Handlers
│ ├── AbstractHandler.php
│ ├── ErrorHandler.php
│ ├── ExceptionHandler.php
│ └── FatalHandler.php
├── LevelFactory.php
├── Payload
│ ├── Body.php
│ ├── ContentInterface.php
│ ├── Context.php
│ ├── Data.php
│ ├── EncodedPayload.php
│ ├── ExceptionInfo.php
│ ├── Frame.php
│ ├── Level.php
│ ├── Message.php
│ ├── Notifier.php
│ ├── Payload.php
│ ├── Person.php
│ ├── Request.php
│ ├── Server.php
│ ├── TelemetryBody.php
│ ├── TelemetryEvent.php
│ ├── Trace.php
│ └── TraceChain.php
├── Response.php
├── ResponseHandlerInterface.php
├── Rollbar.php
├── RollbarJsHelper.php
├── RollbarLogger.php
├── Scrubber.php
├── ScrubberInterface.php
├── Senders
│ ├── AgentSender.php
│ ├── CurlSender.php
│ ├── FluentSender.php
│ └── SenderInterface.php
├── SerializerInterface.php
├── Telemetry
│ ├── EventLevel.php
│ ├── EventType.php
│ ├── Telemeter.php
│ └── TelemetryFilterInterface.php
├── TransformerInterface.php
├── Truncation
│ ├── AbstractStrategy.php
│ ├── FramesStrategy.php
│ ├── MinBodyStrategy.php
│ ├── RawStrategy.php
│ ├── StrategyInterface.php
│ ├── StringsStrategy.php
│ ├── TelemetryStrategy.php
│ └── Truncation.php
├── Utilities.php
└── UtilitiesTrait.php
└── tests
├── AgentTest.php
├── BaseRollbarTest.php
├── BodyTest.php
├── ConfigTest.php
├── ContextTest.php
├── CurlSenderTest.php
├── DataBuilderTest.php
├── DataTest.php
├── DefaultsTest.php
├── ErrorWrapperTest.php
├── ExceptionInfoTest.php
├── FakeDataBuilder.php
├── FluentTest.php
├── FrameTest.php
├── Handlers
├── ErrorHandlerTest.php
└── ExceptionHandlerTest.php
├── JsHelperTest.php
├── LevelFactoryTest.php
├── MessageTest.php
├── NotifierTest.php
├── Payload
├── EncodedPayloadTest.php
├── LevelTest.php
├── TelemetryBodyTest.php
└── TelemetryEventTest.php
├── PayloadTest.php
├── Performance
├── MassivePayload.php
├── TestHelpers
│ ├── EncodedPayload.php
│ └── Truncation.php
└── TruncationTest.php
├── PersonTest.php
├── ReadmeTest.php
├── RequestTest.php
├── ResponseTest.php
├── RollbarLoggerTest.php
├── RollbarTest.php
├── ScrubberTest.php
├── ServerTest.php
├── Telemetry
└── TelemeterTest.php
├── TestHelpers
├── ArrayLogger.php
├── CustomSerializable.php
├── CustomTruncation.php
├── CycleCheck
│ ├── ChildCycleCheck.php
│ ├── ChildCycleCheckSerializable.php
│ ├── ParentCycleCheck.php
│ └── ParentCycleCheckSerializable.php
├── DeprecatedSerializable.php
├── Exceptions
│ ├── FiftyFiftyExceptionSampleRate.php
│ ├── FiftyFityChildExceptionSampleRate.php
│ ├── MidExceptionSampleRate.php
│ ├── QuarterExceptionSampleRate.php
│ ├── SilentExceptionSampleRate.php
│ └── VerboseExceptionSampleRate.php
├── MalformedPayloadDataTransformer.php
├── MockPhpStream.php
├── StdOutLogger.php
└── TestTelemetryFilter.php
├── TraceChainTest.php
├── TraceTest.php
├── Truncation
├── FramesStrategyTest.php
├── MinBodyStrategyTest.php
├── RawStrategyTest.php
├── StringsStrategyTest.php
├── TelemetryStrategyTest.php
└── TruncationTest.php
├── UtilitiesTest.php
├── VerbosityTest.php
└── bootstrap.php
/.dockerignore:
--------------------------------------------------------------------------------
1 | .git
2 | vendor
3 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description of the change
2 |
3 | > Please include a summary of the change and which issues are fixed.
4 | > Please also include relevant motivation and context.
5 |
6 | ## Type of change
7 |
8 | - [ ] Bug fix (non-breaking change that fixes an issue)
9 | - [ ] New feature (non-breaking change that adds functionality)
10 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
11 | - [ ] Maintenance
12 | - [ ] New release
13 |
14 | ## Related issues
15 |
16 | > Shortcut stories and GitHub issues (delete irrelevant)
17 |
18 | - Fix [SC-]
19 | - Fix #1
20 |
21 | ## Checklists
22 |
23 | ### Development
24 |
25 | - [ ] Lint rules pass locally
26 | - [ ] The code changed/added as part of this pull request has been covered with tests
27 | - [ ] All tests related to the changed code pass in development
28 |
29 | ### Code review
30 |
31 | - [ ] This pull request has a descriptive title and information useful to a reviewer. There may be a screenshot or screencast attached
32 | - [ ] "Ready for review" label attached to the PR and reviewers assigned
33 | - [ ] Issue from task tracker has a link to this pull request
34 | - [ ] Changes have been reviewed by at least one other engineer
35 |
--------------------------------------------------------------------------------
/.github/workflows/ci-1.x.yml:
--------------------------------------------------------------------------------
1 | # CI checks for Rollbar-PHP, version 1.x
2 | #
3 | # Test with act:
4 | # brew install act
5 | # act -P ubuntu-latest=shivammathur/node:latest
6 | #
7 | # @see https://github.com/nektos/act/issues/329
8 | name: CI for Rollbar-PHP, version 1.x
9 |
10 | # Fire this action on pushes to 1.x development branches (the official one, as
11 | # well as development branches within it -- hence the wildcard), and also 1.x
12 | # tags. Also, run every day at 02:42 GMT to catch failures from dependencies
13 | # that update independently.
14 | on:
15 | push:
16 | branches:
17 | - next/1.x/**
18 | tags:
19 | - v1.*
20 | pull_request:
21 | branches:
22 | - next/1.x/**
23 | schedule:
24 | - cron: '42 2 * * *'
25 |
26 | jobs:
27 | # Check that this runs on PHP on all versions we claim to support, on both
28 | # UNIX-like and Windows environments, and that use both the lowest possible
29 | # compatible version as well as the most-recent stable version. This will
30 | # fail-fast by default, so we include our edgiest versions (currently 7.4)
31 | # first as they're most likely to fail.
32 | # @see https://freek.dev/1546
33 | # @see https://www.dereuromark.de/2019/01/04/test-composer-dependencies-with-prefer-lowest/
34 | # @see https://github.com/actions/starter-workflows/blob/main/ci/php.yml
35 | php-tests:
36 | strategy:
37 | matrix:
38 | # All the versions, OS, and dependency levels we want to support
39 | php: [5.6, 5.5]
40 | dependency: [stable] # TODO: lowest
41 | os: [ubuntu] # TODO: windows, macos
42 |
43 | name: PHP ${{ matrix.php }} on ${{ matrix.os }}, ${{ matrix.dependency }} dependencies preferred
44 | runs-on: ${{ matrix.os }}-latest
45 | steps:
46 | - name: Checkout the next/1.x/main branch during scheduled builds
47 | if: github.ref == 'refs/heads/master'
48 | uses: actions/checkout@v3
49 | with:
50 | ref: 'next/1.x/main'
51 |
52 | - name: Checkout the pushed branch
53 | if: github.ref != 'refs/heads/master'
54 | uses: actions/checkout@v3
55 |
56 | - name: Install PHP and composer environment
57 | uses: shivammathur/setup-php@v2
58 | with:
59 | php-version: ${{ matrix.php }}
60 | extensions: curl
61 | ini-values:
62 | coverage: xdebug
63 |
64 | - name: Get composer cache directory
65 | id: composer-cache
66 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
67 |
68 | - name: Cache dependencies
69 | uses: actions/cache@v3
70 | with:
71 | path: ${{ steps.composer-cache.outputs.dir }}
72 | key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ matrix.dependency }}-
73 | restore-keys: ${{ matrix.os }}-composer-${{ matrix.dependency }}-
74 |
75 | - name: Install dependencies
76 | run: composer update --prefer-${{ matrix.dependency }} --prefer-dist --no-interaction
77 |
78 | - name: Execute tests
79 | run: composer test
80 |
--------------------------------------------------------------------------------
/.github/workflows/ci-2.x.yml:
--------------------------------------------------------------------------------
1 | # CI checks for Rollbar-PHP, version 2.x
2 | #
3 | # Test with act:
4 | # brew install act
5 | # act -P ubuntu-latest=shivammathur/node:latest
6 | #
7 | # @see https://github.com/nektos/act/issues/329
8 | name: CI for Rollbar-PHP, version 2.x
9 |
10 | # Fire this action on pushes to 2.x development branches (the official one, as
11 | # well as development branches within it -- hence the wildcard), and also 2.x
12 | # tags. Also, run every day at 02:42 GMT to catch failures from dependencies
13 | # that update independently.
14 | on:
15 | push:
16 | branches:
17 | - next/2.x/**
18 | tags:
19 | - v2.*
20 | pull_request:
21 | branches:
22 | - next/2.x/**
23 | schedule:
24 | - cron: '42 2 * * *'
25 |
26 | jobs:
27 | # Check that this runs on PHP on all versions we claim to support, on both
28 | # UNIX-like and Windows environments, and that use both the lowest possible
29 | # compatible version as well as the most-recent stable version. This will
30 | # fail-fast by default, so we include our edgiest versions (currently 7.4)
31 | # first as they're most likely to fail.
32 | # @see https://freek.dev/1546
33 | # @see https://www.dereuromark.de/2019/01/04/test-composer-dependencies-with-prefer-lowest/
34 | # @see https://github.com/actions/starter-workflows/blob/main/ci/php.yml
35 | php-tests:
36 | strategy:
37 | matrix:
38 | # All the versions, OS, and dependency levels we want to support
39 | php: [7.4, 7.3, 7.2, 7.1, 7.0]
40 | dependency: [stable] # TODO: lowest
41 | os: [ubuntu] # TODO: windows, macos
42 | # In XDebug 2 and earlier, if XDebug extension was present, the function
43 | # xdebug_get_function_stack() was unconditionally available. Now in XDebug 3
44 | # that function is present only when xdebug.mode includes "develop". Our code
45 | # has paths for with- and without- XDebug, and we want to test both of them.
46 | # However, we require XDebug for code coverage, so before XDebug 3 there was
47 | # no way to test the without-XDebug code path. Now we can, so we do.
48 | # Note: INI values with embedded commas need to have their value quoted.
49 | # @see https://xdebug.org/docs/all_settings#mode
50 | xdebug3-mode: ["xdebug.mode='develop,coverage'", "xdebug.mode=coverage"]
51 | include:
52 | # 7.4 introduced an engine wide flag that disables arguments in
53 | # backtraces. The default in 7.4 is On. However, we have tests that
54 | # rely on arguments being available, so we turn it Off.
55 | - php: 7.4
56 | ini: zend.exception_ignore_args=Off
57 | exclude:
58 | # We only have XDebug 3 in PHP > 7.3, and the ini value will be ignored in
59 | # all earlier versions. We can prune out one of them, because the result of
60 | # both runs in these earlier versions are the same (because the ini directive
61 | # is completely ignored, no matter its value).
62 | # @see https://xdebug.org/docs/compat
63 | - php: 7.2
64 | xdebug3-mode: "xdebug.mode=develop,coverage"
65 | - php: 7.1
66 | xdebug3-mode: "xdebug.mode=develop,coverage"
67 |
68 | name: PHP ${{ matrix.php }} on ${{ matrix.os }}, ${{ matrix.dependency }} dependencies preferred
69 | runs-on: ${{ matrix.os }}-latest
70 | steps:
71 | - name: Checkout the next/2.x/main branch during scheduled builds
72 | if: github.ref == 'refs/heads/master'
73 | uses: actions/checkout@v3
74 | with:
75 | ref: 'next/2.x/main'
76 |
77 | - name: Checkout the pushed branch
78 | if: github.ref != 'refs/heads/master'
79 | uses: actions/checkout@v3
80 |
81 | - name: Install PHP and composer environment
82 | uses: shivammathur/setup-php@v2
83 | with:
84 | php-version: ${{ matrix.php }}
85 | extensions: curl
86 | ini-values: ${{ matrix.ini }}, ${{ matrix.xdebug3-mode }}
87 | coverage: xdebug
88 |
89 | - name: Get composer cache directory
90 | id: composer-cache
91 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
92 |
93 | - name: Cache dependencies
94 | uses: actions/cache@v3
95 | with:
96 | path: ${{ steps.composer-cache.outputs.dir }}
97 | key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ matrix.dependency }}-
98 | restore-keys: ${{ matrix.os }}-composer-${{ matrix.dependency }}-
99 |
100 | - name: Install dependencies
101 | run: composer update --prefer-${{ matrix.dependency }} --prefer-dist --no-interaction
102 |
103 | - name: Execute tests
104 | run: composer test
105 |
--------------------------------------------------------------------------------
/.github/workflows/ci-3.x.yml:
--------------------------------------------------------------------------------
1 | # CI checks for Rollbar-PHP, master branch.
2 | #
3 | # Test with act:
4 | # brew install act
5 | # act -P ubuntu-latest=shivammathur/node:latest
6 | #
7 | # @see https://github.com/nektos/act/issues/329
8 | name: CI for Rollbar-PHP, version 3.x
9 |
10 | # Fire this action on pushes to all branches except the development branches
11 | # for older versions of PHP. Thus, all branches assume to target master (and
12 | # will be checked accordingly) unless they begin with next/. Also, run every
13 | # day at 02:42 GMT to catch failures from dependencies that update
14 | # independently.
15 | on:
16 | push:
17 | branches:
18 | - next/3.x/**
19 | tags:
20 | - v3.*
21 | pull_request:
22 | branches:
23 | - next/3.x/**
24 | schedule:
25 | # Every day at 02:42
26 | - cron: '42 2 * * *'
27 |
28 | jobs:
29 | # Check that this runs on PHP on all versions we claim to support, on both
30 | # UNIX-like and Windows environments, and that use both the lowest possible
31 | # compatible version as well as the most-recent stable version. This will
32 | # fail-fast by default, so we include our edgiest versions first as they're
33 | # most likely to fail.
34 | # @see https://freek.dev/1546
35 | # @see https://www.dereuromark.de/2019/01/04/test-composer-dependencies-with-prefer-lowest/
36 | php-tests:
37 | strategy:
38 | fail-fast: false
39 | matrix:
40 | # All the versions, OS, and dependency levels we want to support
41 | os: [ ubuntu ] # TODO: windows, macos
42 | php: [ '8.0', '8.1', '8.2' ]
43 | dependency: [ stable ]
44 | # Our code has paths for with- and without- XDebug, and we want to test
45 | # both of them.
46 | # @see https://xdebug.org/docs/all_settings#mode
47 | xdebug3-mode: ['develop,coverage', 'coverage']
48 | include:
49 | - php: '8.0'
50 | os: 'ubuntu'
51 | dependency: 'lowest'
52 | xdebug3-mode: 'develop,coverage'
53 |
54 | name: PHP ${{ matrix.php }} on ${{ matrix.os }}, ${{ matrix.dependency }} dependencies preferred, ${{ matrix.xdebug3-mode }}
55 | runs-on: ${{ matrix.os }}-latest
56 | steps:
57 | - name: Checkout the next/3.x/main branch during scheduled builds
58 | if: github.ref == 'refs/heads/master'
59 | uses: actions/checkout@v3
60 | with:
61 | ref: 'next/3.x/main'
62 |
63 | - name: Checkout the pushed branch
64 | if: github.ref != 'refs/heads/master'
65 | uses: actions/checkout@v3
66 |
67 | - name: Install PHP and composer environment
68 | uses: shivammathur/setup-php@v2
69 | with:
70 | php-version: ${{ matrix.php }}
71 | extensions: curl
72 | ini-values: zend.exception_ignore_args=Off, xdebug3-mode="${{ matrix.xdebug3-mode }}"
73 | coverage: xdebug
74 |
75 | - name: Get composer cache directory
76 | id: composer-cache
77 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
78 |
79 | - name: Cache dependencies
80 | uses: actions/cache@v3
81 | with:
82 | path: ${{ steps.composer-cache.outputs.dir }}
83 | key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ matrix.dependency }}-
84 | restore-keys: ${{ matrix.os }}-composer-${{ matrix.dependency }}-
85 |
86 | - name: Install dependencies
87 | run: composer update --prefer-${{ matrix.dependency }} --prefer-dist --no-interaction
88 |
89 | - name: Execute tests
90 | run: composer test
91 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # CI checks for Rollbar-PHP, master branch.
2 | #
3 | # Test with act:
4 | # brew install act
5 | # act -P ubuntu-latest=shivammathur/node:latest
6 | #
7 | # @see https://github.com/nektos/act/issues/329
8 | name: CI for Rollbar-PHP, master
9 |
10 | # Fire this action on pushes to all branches except the development branches
11 | # for older versions of PHP. Thus, all branches assume to target master (and
12 | # will be checked accordingly) unless they begin with next/. Also, run every
13 | # day at 02:42 GMT to catch failures from dependencies that update
14 | # independently.
15 | on:
16 | push:
17 | branches-ignore:
18 | - next/**
19 | pull_request:
20 | branches-ignore:
21 | - next/**
22 | schedule:
23 | - cron: '42 2 * * *'
24 |
25 | jobs:
26 | # Check that this runs on PHP on all versions we claim to support, on both
27 | # UNIX-like and Windows environments, and that use both the lowest possible
28 | # compatible version as well as the most-recent stable version. This will
29 | # fail-fast by default, so we include our edgiest versions (currently 7.4)
30 | # first as they're most likely to fail.
31 | # @see https://freek.dev/1546
32 | # @see https://www.dereuromark.de/2019/01/04/test-composer-dependencies-with-prefer-lowest/
33 | php-tests:
34 | strategy:
35 | fail-fast: false
36 | matrix:
37 | # All the versions, OS, and dependency levels we want to support
38 | os: [ubuntu] # TODO: windows, macos
39 | php: [ '8.1', '8.2', '8.3', '8.4' ]
40 | dependency: [ stable ]
41 | # Our code has paths for with- and without- XDebug, and we want to test
42 | # both of them.
43 | # @see https://xdebug.org/docs/all_settings#mode
44 | xdebug3-mode: ['develop,coverage', 'coverage']
45 | include:
46 | - php: '8.1'
47 | os: 'ubuntu'
48 | dependency: 'lowest'
49 | xdebug3-mode: 'develop,coverage'
50 |
51 | name: PHP ${{ matrix.php }} on ${{ matrix.os }}, ${{ matrix.dependency }} dependencies preferred, ${{ matrix.xdebug3-mode }}
52 | runs-on: ${{ matrix.os }}-latest
53 | steps:
54 | - name: Checkout the code
55 | uses: actions/checkout@v3
56 |
57 | - name: Install PHP and composer environment
58 | uses: shivammathur/setup-php@v2
59 | with:
60 | php-version: ${{ matrix.php }}
61 | extensions: curl
62 | ini-values: zend.exception_ignore_args=Off, xdebug3-mode="${{ matrix.xdebug3-mode }}"
63 | coverage: xdebug
64 |
65 | - name: Get composer cache directory
66 | id: composer-cache
67 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
68 |
69 | - name: Cache dependencies
70 | uses: actions/cache@v3
71 | with:
72 | path: ${{ steps.composer-cache.outputs.dir }}
73 | key: ${{ matrix.os }}-composer-${{ hashFiles('**/composer.json') }}-${{ matrix.dependency }}-
74 | restore-keys: ${{ matrix.os }}-composer-${{ matrix.dependency }}-
75 |
76 | - name: Install dependencies PHP < 8.4
77 | if: matrix.php != '8.4'
78 | run: composer update --prefer-${{ matrix.dependency }} --prefer-dist --no-interaction
79 |
80 | - name: Execute tests PHP < 8.4
81 | if: matrix.php != '8.4'
82 | run: composer test
83 |
84 | # This is a temporary workaround until vimeo/psalm is updated to support PHP 8.4.
85 | # See https://github.com/vimeo/psalm/issues/11107
86 | - name: Install dependencies PHP 8.4
87 | if: matrix.php == '8.4'
88 | run: composer update --prefer-${{ matrix.dependency }} --prefer-dist --no-interaction --ignore-platform-reqs
89 |
90 | - name: Execute tests PHP 8.4
91 | if: matrix.php == '8.4'
92 | run: |
93 | ./vendor/bin/phpcs --standard=PSR2 ./src ./tests
94 | ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
95 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 | build
4 | .phpunit.result.cache
5 | .phpunit.cache
6 |
7 | # Files editors may leave around that aren't related to any kind of
8 | # build artifacts
9 | *.swp
10 | *.swo
11 | .idea
12 | .vscode
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM shivammathur/node:focal
2 |
3 | VOLUME [ "/opt/rollbar/rollbar-php" ]
4 | WORKDIR /opt/rollbar/rollbar-php
5 | ENTRYPOINT [ "/bin/bash" ]
6 |
7 | RUN apt-get update \
8 | && apt-get install -y ca-certificates git vim tree
9 |
10 | RUN spc --php-version "8.0" --extensions "curl" --coverage "xdebug"
11 |
--------------------------------------------------------------------------------
/EXAMPLES.md:
--------------------------------------------------------------------------------
1 | A range of examples of using Rollbar PHP is available here:
2 | [Rollbar PHP Examples](https://github.com/rollbar/rollbar-php-examples).
3 |
4 | A Wordpress Plugin is available through Wordpress Admin Panel or through Wordpress
5 | Plugin directory: [Rollbar Wordpress](https://wordpress.org/plugins/rollbar/)
6 |
7 | A Laravel-specific package is available for integrating with Laravel:
8 | [Rollbar Laravel](https://github.com/rollbar/rollbar-php-laravel)
9 |
10 | A CakePHP-specific package is avaliable for integrating with CakePHP 2.x:
11 | [CakeRollbar](https://github.com/tranfuga25s/CakeRollbar)
12 |
13 | A Flow-specific package is available for integrating with Neos Flow:
14 | [m12/flow-rollbar](https://packagist.org/packages/m12/flow-rollbar)
15 |
16 | Yii package: [baibaratsky/yii-rollbar](https://github.com/baibaratsky/yii-rollbar)
17 |
18 | Yii2 package: [baibaratsky/yii2-rollbar](https://github.com/baibaratsky/yii2-rollbar)
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Rollbar, Inc.
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Rollbar PHP SDK
6 |
7 |
8 | Proactively discover, predict, and resolve errors in real-time with Rollbar’s error monitoring platform. Start tracking errors today!
9 |
10 |
11 |
12 | [](https://packagist.org/packages/rollbar/rollbar)
13 | [](https://github.com/rollbar/rollbar-php/actions/workflows/ci.yml)
14 | [](https://packagist.org/packages/rollbar/rollbar)
15 |
16 | ---
17 |
18 | ## Key benefits of using Rollbar PHP SDK are:
19 | - **Frameworks:** Rollbar php supports many popular php frameworks such as Laravel, CodeIgniter, Symfony and many more!
20 | - **Plugins:** Rollbar php has plugin support for Heroku, Wordpress, Rollbar.js and more.
21 | - **Automatic error grouping:** Rollbar aggregates Occurrences caused by the same error into Items that represent application issues. Learn more about reducing log noise.
22 | - **Advanced search:** Filter items by many different properties. Learn more about search.
23 | - **Customizable notifications:** Rollbar supports several messaging and incident management tools where your team can get notified about errors and important events by real-time alerts. Learn more about Rollbar notifications.
24 |
25 |
26 |
27 | # Quickstart
28 |
29 | If you've never used Rollbar before, [sign up for a Rollbar account][signup]
30 | and follow the simple, three-step tour. In no time, you'll be capturing errors
31 | and exceptions thrown in your code.
32 |
33 | If you already have a Rollbar account, [log in to your Rollbar account][login].
34 | From the Settings > Project Access Token menu, click Create New Access Token.
35 | Copy the `post_client_item` value and paste it into the code below.
36 |
37 | ```php
38 | require 'vendor/autoload.php'; // composer require rollbar/rollbar
39 |
40 | \Rollbar\Rollbar::init([
41 | 'access_token' => '***',
42 | 'environment' => 'development',
43 | ]);
44 | ```
45 |
46 | For detailed usage instructions and configuration reference, refer to our
47 | [PHP SDK docs][sdkdoc].
48 |
49 | [login]: https://rollbar.com/login/
50 | [sdkdoc]:https://docs.rollbar.com/docs/php
51 | [signup]: https://rollbar.com/signup
52 |
53 | # Getting Help
54 |
55 | * If you have a question, ask in our [Discussion Q&A][q-a]
56 | * To report a bug, raise [an issue][issue]
57 | * For account service, reach out to [support@rollbar.com][support]
58 |
59 | [issue]:https://github.com/rollbar/rollbar-php/issues
60 | [q-a]:https://github.com/rollbar/rollbar-php/discussions/categories/q-a
61 | [support]:mailto:support@rollbar.com
62 |
63 | # Releases, Versions, and PHP Compatibility
64 |
65 | Major releases of this library support major versions of PHP, as follows:
66 |
67 | * For PHP 8, choose the `4.x` or `3.x` branch.
68 | * For PHP 7, choose a `2.x` release.
69 | * For PHP 5, choose a `1.x` release.
70 |
71 | To obtain a release, download an archive from the [Releases] page or use
72 | composer:
73 |
74 | ```sh
75 | # for PHP 8 compatibility
76 | $ composer require rollbar/rollbar:^4
77 | # or
78 | $ composer require rollbar/rollbar:^3
79 |
80 | # for PHP 7 compatibility
81 | $ composer require rollbar/rollbar:^2
82 |
83 | # for PHP 5 compatibility
84 | $ composer require rollbar/rollbar:^1
85 | ```
86 |
87 | Refer to [CHANGELOG.md] for a complete history.
88 |
89 | [CHANGELOG.md]: ./CHANGELOG.md
90 | [Releases]: https://github.com/rollbar/rollbar-php/releases
91 |
92 | # License
93 | Rollbar-PHP is free software released under the MIT License. See [LICENSE]
94 | for details.
95 |
96 | [LICENSE]: ./LICENSE
97 |
--------------------------------------------------------------------------------
/THANKS.md:
--------------------------------------------------------------------------------
1 | Many thanks to the following contributors, by github username and in alphabetical order. For the most recent list, see https://github.com/rollbar/rollbar-php/graphs/contributors
2 |
3 | - [AlexeyMorozov](https://github.com/AlexeyMorozov)
4 | - [alexthegeek](https://github.com/alexthegeek)
5 | - [benjamin-smith](https://github.com/benjamin-smith)
6 | - [chanind](https://github.com/chanind)
7 | - [digilist](https://github.com/digilist)
8 | - [dimkalinux](https://github.com/dimkalinux)
9 | - [diogopms](https://github.com/diogopms)
10 | - [elfif](https://github.com/elfif)
11 | - [ghostal](https://github.com/ghostal)
12 | - [kidk](https://github.com/kidk)
13 | - [lindyhopchris](https://github.com/lindyhopchris)
14 | - [MaffooBristol](https://github.com/MaffooBristol)
15 | - [plusbryan](https://github.com/plusbryan)
16 | - [rekky](https://github.com/rekky)
17 | - [rfink](https://github.com/rfink)
18 | - [steveh](https://github.com/steveh)
19 | - [stof](https://github.com/stof)
20 | - [unix1](https://github.com/unix1)
21 | - [veloper](https://github.com/veloper)
22 | - [vilius-g](https://github.com/vilius-g)
23 | - [violuke](https://github.com/violuke)
24 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | 1. Include Code Context
2 | 1. Implement Agent Sender
3 | 1. Get and sanitize function arguments from backtrace:
4 | * You can get argument names like so: http://stackoverflow.com/a/2692514/456188
5 | * You can use `array_combine` to get a kwargs version of the arguments
6 | * You can then sanitize based on argument name
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rollbar/rollbar",
3 | "description": "Monitors errors and exceptions and reports them to Rollbar",
4 | "type": "library",
5 | "keywords": ["logging", "debugging", "monitoring", "errors", "exceptions"],
6 | "license": "MIT",
7 | "homepage": "https://github.com/rollbar/rollbar-php",
8 |
9 | "authors": [
10 | {
11 | "name": "Rollbar, Inc.",
12 | "email": "support@rollbar.com",
13 | "role": "Developer"
14 | }
15 | ],
16 |
17 | "support": {
18 | "email": "support@rollbar.com"
19 | },
20 |
21 | "autoload": {
22 | "psr-4": {
23 | "Rollbar\\": "src/"
24 | }
25 | },
26 |
27 | "autoload-dev": {
28 | "psr-4": {
29 | "Rollbar\\": "tests/"
30 | }
31 | },
32 |
33 | "require": {
34 | "php": ">=8.1.0 <9.0",
35 | "ext-curl": "*",
36 | "psr/log": "^1 || ^2 || ^3",
37 | "monolog/monolog": "^2 || ^3"
38 | },
39 |
40 | "require-dev": {
41 | "phpunit/phpunit": "^9.6 || ^10.1",
42 | "mockery/mockery": "^1.5.1",
43 | "squizlabs/php_codesniffer": "^3.7",
44 | "phpmd/phpmd" : "^2.13",
45 | "vimeo/psalm": "^5.9"
46 | },
47 |
48 | "suggest": {
49 | "fluent/logger": "Needed to use the 'fluent' handler for fluentd support"
50 | },
51 |
52 | "scripts": {
53 | "docker-build": "docker build -t rollbar/rollbar-php:3 .",
54 | "docker-run": "docker run -it -v \"${PWD}\":/opt/rollbar/rollbar-php rollbar/rollbar-php:3",
55 | "test": [
56 | "phpcs --standard=PSR2 src tests",
57 | "psalm --long-progress",
58 | "phpunit --coverage-clover build/logs/clover.xml"
59 | ],
60 | "fix": "phpcbf --standard=PSR2 src tests",
61 | "get-js-snippet": "ROLLBAR_JS_TAG=$(curl -s https://api.github.com/repos/rollbar/rollbar.js/releases/latest | sed -n 's/\"tag_name\":.*\"\\(.*\\)\",/\\1/p' | sed 's/ *//'); curl -X GET https://raw.githubusercontent.com/rollbar/rollbar.js/$ROLLBAR_JS_TAG/dist/rollbar.snippet.js > data/rollbar.snippet.js",
62 | "performance": "phpunit --coverage-clover build/logs/clover.xml --testsuite 'Rollbar Performance Test Suite'"
63 | },
64 |
65 | "config": {
66 | "process-timeout": 600
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | ./tests/
11 | ./tests/Performance/
12 | ./tests/TestHelpers/
13 | ./tests/FakeDataBuilder.php
14 | ./tests/bootstrap.php
15 | ./tests/BaseRollbarTest.php
16 |
17 |
18 |
19 | ./tests/Performance/
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | src
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/psalm.baseline:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | $result
6 |
7 |
8 |
9 |
10 | $toLog
11 |
12 |
13 |
14 |
15 | FluentLogger
16 | FluentLogger
17 |
18 |
19 | $this->fluentLogger
20 | FluentLogger
21 | private $fluentLogger = null;
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/DataBuilderInterface.php:
--------------------------------------------------------------------------------
1 | "E_ERROR",
16 | E_WARNING => "E_WARNING",
17 | E_PARSE => "E_PARSE",
18 | E_NOTICE => "E_NOTICE",
19 | E_CORE_ERROR => "E_CORE_ERROR",
20 | E_CORE_WARNING => "E_CORE_WARNING",
21 | E_COMPILE_ERROR => "E_COMPILE_ERROR",
22 | E_COMPILE_WARNING => "E_COMPILE_WARNING",
23 | E_USER_ERROR => "E_USER_ERROR",
24 | E_USER_WARNING => "E_USER_WARNING",
25 | E_USER_NOTICE => "E_USER_NOTICE",
26 | E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR",
27 | E_DEPRECATED => "E_DEPRECATED",
28 | E_USER_DEPRECATED => "E_USER_DEPRECATED"
29 | );
30 | }
31 | return self::$constName[$const] ?? null;
32 | }
33 |
34 | /**
35 | * Creates the instance from the error data.
36 | *
37 | * @param int $errorLevel The level of the error raised.
38 | * @param string $errorMessage The error message.
39 | * @param string|null $errorFile The filename that the error was raised in.
40 | * @param int|null $errorLine The line number where the error was raised.
41 | * @param array|null $backTrace The stack trace for the error.
42 | * @param Utilities $utilities The configured utilities class.
43 | */
44 | public function __construct(
45 | public int $errorLevel,
46 | public string $errorMessage,
47 | public ?string $errorFile,
48 | public ?int $errorLine,
49 | public ?array $backTrace,
50 | $utilities
51 | ) {
52 | parent::__construct($this->errorMessage, $this->errorLevel);
53 | $this->utilities = $utilities;
54 | }
55 |
56 | public function getBacktrace()
57 | {
58 | return $this->backTrace;
59 | }
60 |
61 | public function getClassName()
62 | {
63 | $constName = self::getConstName($this->errorLevel) ?: "#$this->errorLevel";
64 | return "$constName";
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/FilterInterface.php:
--------------------------------------------------------------------------------
1 | logger;
23 | }
24 |
25 | public function registered()
26 | {
27 | return $this->registered;
28 | }
29 |
30 | public function handle(...$args)
31 | {
32 | if (!$this->registered()) {
33 | throw new \Exception(get_class($this) . ' has not been set up.');
34 | }
35 | }
36 |
37 | public function register()
38 | {
39 | $this->registered = true;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Handlers/ErrorHandler.php:
--------------------------------------------------------------------------------
1 | previousHandler = set_error_handler(array($this, 'handle'));
14 |
15 | parent::register();
16 | }
17 |
18 | public function handle(...$args)
19 | {
20 | parent::handle(...$args);
21 |
22 | if (count($args) < 2) {
23 | throw new \Exception('No $errno or $errstr to be passed to the error handler.');
24 | }
25 |
26 | $errno = $args[0];
27 | $errstr = $args[1];
28 | $errfile = $args[2] ?: null;
29 | $errline = $args[3] ?: null;
30 |
31 | if ($this->previousHandler) {
32 | $stop_processing = ($this->previousHandler)($errno, $errstr, $errfile, $errline);
33 | if ($stop_processing) {
34 | return $stop_processing;
35 | }
36 | }
37 |
38 | if ($this->logger()->shouldIgnoreError($errno)) {
39 | return false;
40 | }
41 |
42 | $exception = $this->logger()->
43 | getDataBuilder()->
44 | generateErrorWrapper($errno, $errstr, $errfile, $errline);
45 | $this->logger()->report(Level::ERROR, $exception, isUncaught: true);
46 |
47 | return false;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Handlers/ExceptionHandler.php:
--------------------------------------------------------------------------------
1 | previousHandler = set_exception_handler(array($this, 'handle'));
14 |
15 | parent::register();
16 | }
17 |
18 | public function handle(...$args)
19 | {
20 | parent::handle(...$args);
21 |
22 | if (count($args) < 1) {
23 | throw new \Exception('No exception to be passed to the exception handler.');
24 | }
25 |
26 | $exception = $args[0];
27 | $this->logger()->report(Level::ERROR, $exception, isUncaught: true);
28 |
29 | // if there was no prior handler, then we toss that exception
30 | if ($this->previousHandler === null) {
31 | throw $exception;
32 | }
33 |
34 | // otherwise we overrode a previous handler, so restore it and call it
35 | restore_exception_handler();
36 | ($this->previousHandler)($exception);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Handlers/FatalHandler.php:
--------------------------------------------------------------------------------
1 | isFatal($lastError)) {
33 | $errno = $lastError['type'];
34 | $errstr = $lastError['message'];
35 | $errfile = $lastError['file'];
36 | $errline = $lastError['line'];
37 |
38 | $exception = $this->logger()->
39 | getDataBuilder()->
40 | generateErrorWrapper($errno, $errstr, $errfile, $errline);
41 | $this->logger()->report(Level::CRITICAL, $exception, isUncaught: true);
42 | }
43 | }
44 |
45 | /**
46 | * Check if the error triggered is indeed a fatal error.
47 | *
48 | * @var array $lastError Information fetched from error_get_last().
49 | *
50 | * @return bool
51 | */
52 | protected function isFatal($lastError)
53 | {
54 | return
55 | null !== $lastError &&
56 | in_array($lastError['type'], self::$fatalErrors, true) &&
57 | // don't log uncaught exceptions as they were handled by exceptionHandler()
58 | !(isset($lastError['message']) &&
59 | str_starts_with($lastError['message'], 'Uncaught'));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/LevelFactory.php:
--------------------------------------------------------------------------------
1 | Level].
11 | *
12 | * @var array|null
13 | */
14 | private static ?array $levels = null;
15 |
16 | /**
17 | * Returns the array of levels as [string => Level].
18 | *
19 | * @return array
20 | */
21 | private static function getLevels(): array
22 | {
23 | if (null === self::$levels) {
24 | self::$levels = array(
25 | Level::EMERGENCY => new Level("critical", 100000),
26 | Level::ALERT => new Level("critical", 100000),
27 | Level::CRITICAL => new Level("critical", 100000),
28 | Level::ERROR => new Level("error", 10000),
29 | Level::WARNING => new Level("warning", 1000),
30 | Level::NOTICE => new Level("info", 100),
31 | Level::INFO => new Level("info", 100),
32 | Level::DEBUG => new Level("debug", 10),
33 | );
34 | }
35 |
36 | return self::$levels;
37 | }
38 |
39 | /**
40 | * Returns the {@see Level} instance for a given log level. If the log level
41 | * is invalid null will be returned.
42 | *
43 | * @param string $name level name
44 | *
45 | * @return Level|null
46 | */
47 | public static function fromName(string $name): ?Level
48 | {
49 | $name = strtolower($name);
50 | return self::getLevels()[$name] ?? null;
51 | }
52 |
53 | /**
54 | * Check if the provided level is a valid level.
55 | *
56 | * @param string $level
57 | *
58 | * @return bool
59 | */
60 | public static function isValidLevel(string $level): bool
61 | {
62 | return self::fromName($level) !== null;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Payload/Body.php:
--------------------------------------------------------------------------------
1 | value;
38 | }
39 |
40 | /**
41 | * Sets the main content of the payload body.
42 | *
43 | * @param ContentInterface $value The value to assign to the content of the payload body.
44 | *
45 | * @return self
46 | */
47 | public function setValue(ContentInterface $value): self
48 | {
49 | $this->value = $value;
50 | return $this;
51 | }
52 |
53 | /**
54 | * Sets the array of extra data.
55 | *
56 | * @param array $extra The array of extra data.
57 | *
58 | * @return self
59 | */
60 | public function setExtra(array $extra): self
61 | {
62 | $this->extra = $extra;
63 | return $this;
64 | }
65 |
66 | /**
67 | * Returns the array of extra data.
68 | *
69 | * @return array
70 | */
71 | public function getExtra(): array
72 | {
73 | return $this->extra;
74 | }
75 |
76 | /**
77 | * Returns the array of telemetry events or null if there were none.
78 | *
79 | * @return TelemetryEvent[]|null
80 | *
81 | * @since 4.1.0
82 | */
83 | public function getTelemetry(): ?array
84 | {
85 | if (empty($this->telemetry)) {
86 | return null;
87 | }
88 | return $this->telemetry;
89 | }
90 |
91 | /**
92 | * Sets the list of telemetry events for this payload body.
93 | *
94 | * @param array|null $telemetry The list of telemetry events or null if there were none.
95 | *
96 | * @return void
97 | *
98 | * @since 4.1.0
99 | */
100 | public function setTelemetry(?array $telemetry): void
101 | {
102 | $this->telemetry = $telemetry;
103 | }
104 |
105 | /**
106 | * Returns the JSON serializable representation of the payload body.
107 | *
108 | * @return array
109 | *
110 | * @since 4.1.0 Includes the 'telemetry' key, if it is not empty.
111 | */
112 | public function serialize()
113 | {
114 | $result = array();
115 | $result[$this->value->getKey()] = $this->value;
116 |
117 | if (!empty($this->extra)) {
118 | $result['extra'] = $this->extra;
119 | }
120 |
121 | if (!empty($this->telemetry)) {
122 | $result['telemetry'] = $this->telemetry;
123 | }
124 |
125 | return $this->utilities()->serializeForRollbarInternal($result, array('extra'));
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/Payload/ContentInterface.php:
--------------------------------------------------------------------------------
1 | pre;
19 | }
20 |
21 | public function setPre(array $pre): self
22 | {
23 | $this->pre = $pre;
24 | return $this;
25 | }
26 |
27 | public function getPost(): ?array
28 | {
29 | return $this->post;
30 | }
31 |
32 | public function setPost(array $post): self
33 | {
34 | $this->post = $post;
35 | return $this;
36 | }
37 |
38 | public function serialize()
39 | {
40 | $result = array(
41 | "pre" => $this->pre,
42 | "post" => $this->post,
43 | );
44 |
45 | return $this->utilities()->serializeForRollbarInternal($result);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Payload/EncodedPayload.php:
--------------------------------------------------------------------------------
1 | data;
42 | }
43 |
44 | /**
45 | * Returns the cached size of the encoded data.
46 | *
47 | * @return int
48 | */
49 | public function size(): int
50 | {
51 | return $this->size;
52 | }
53 |
54 | /**
55 | * Reduces the cached length of the encoded data by the $amount specified.
56 | *
57 | * Note: this does not reduce size of the data, only the cached size. To reduce the size of the payload data the
58 | * payload must be truncated. See {@see Truncation} for more details.
59 | *
60 | * @param int $amount The amount to decrease the cached data length.
61 | *
62 | * @return void
63 | */
64 | public function decreaseSize(int $amount): void
65 | {
66 | $this->size -= $amount;
67 | }
68 |
69 | /**
70 | * Updates the payload data and JSON serializes it. The serialized data string is cached and can be access via the
71 | * {@see encoded()} method.
72 | *
73 | * @param array|null $data If an array is given it will overwrite any existing payload data prior to serialization.
74 | * If null (the default value) is given the existing payload data will be serialized.
75 | *
76 | * @return void
77 | * @throws Exception If JSON serialization fails.
78 | */
79 | public function encode(?array $data = null): void
80 | {
81 | if ($data !== null) {
82 | $this->data = $data;
83 | }
84 |
85 | $this->encoded = json_encode(
86 | $this->data,
87 | defined('JSON_PARTIAL_OUTPUT_ON_ERROR') ? JSON_PARTIAL_OUTPUT_ON_ERROR : 0
88 | );
89 |
90 | if ($this->encoded === false) {
91 | throw new Exception("Payload data could not be encoded to JSON format.");
92 | }
93 |
94 | $this->size = strlen($this->encoded);
95 | }
96 |
97 | /**
98 | * Returns the encoded JSON.
99 | *
100 | * @return string
101 | */
102 | public function __toString(): string
103 | {
104 | return (string)$this->encoded();
105 | }
106 |
107 | /**
108 | * Returns the encoded JSON or null if the data to encode was null.
109 | *
110 | * @return string|null
111 | */
112 | public function encoded(): ?string
113 | {
114 | return $this->encoded;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/Payload/ExceptionInfo.php:
--------------------------------------------------------------------------------
1 | class;
22 | }
23 |
24 | public function setClass(string $class): self
25 | {
26 | $this->class = $class;
27 | return $this;
28 | }
29 |
30 | public function getMessage(): string
31 | {
32 | return $this->message;
33 | }
34 |
35 | public function setMessage(string $message): self
36 | {
37 | $this->message = $message;
38 | return $this;
39 | }
40 |
41 | public function getDescription(): ?string
42 | {
43 | return $this->description;
44 | }
45 |
46 | public function setDescription(?string $description): self
47 | {
48 | $this->description = $description;
49 | return $this;
50 | }
51 |
52 | public function serialize()
53 | {
54 | $result = array(
55 | "class" => $this->class,
56 | "message" => $this->message,
57 | "description" => $this->description,
58 | );
59 |
60 | return $this->utilities()->serializeForRollbarInternal($result);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Payload/Frame.php:
--------------------------------------------------------------------------------
1 | filename;
31 | }
32 |
33 | public function setFilename(?string $filename): self
34 | {
35 | $this->filename = $filename;
36 | return $this;
37 | }
38 |
39 | public function getLineno(): ?int
40 | {
41 | return $this->lineno;
42 | }
43 |
44 | public function setLineno(?int $lineno): self
45 | {
46 | $this->lineno = $lineno;
47 | return $this;
48 | }
49 |
50 | public function getColno(): ?int
51 | {
52 | return $this->colno;
53 | }
54 |
55 | public function setColno(?int $colno): self
56 | {
57 | $this->colno = $colno;
58 | return $this;
59 | }
60 |
61 | public function getMethod(): ?string
62 | {
63 | return $this->method;
64 | }
65 |
66 | public function setMethod(?string $method): self
67 | {
68 | $this->method = $method;
69 | return $this;
70 | }
71 |
72 | public function getCode(): ?string
73 | {
74 | return $this->code;
75 | }
76 |
77 | public function setCode(?string $code): self
78 | {
79 | $this->code = $code;
80 | return $this;
81 | }
82 |
83 | public function getContext(): ?Context
84 | {
85 | return $this->context;
86 | }
87 |
88 | public function setContext(Context $context): self
89 | {
90 | $this->context = $context;
91 | return $this;
92 | }
93 |
94 | public function getArgs(): ?array
95 | {
96 | return $this->args;
97 | }
98 |
99 | public function setArgs(array $args): self
100 | {
101 | $this->args = $args;
102 | return $this;
103 | }
104 |
105 | public function serialize()
106 | {
107 | $result = array(
108 | "filename" => $this->filename,
109 | "lineno" => $this->lineno,
110 | "colno" => $this->colno,
111 | "method" => $this->method,
112 | "code" => $this->code,
113 | "context" => $this->context,
114 | "args" => $this->args
115 | );
116 |
117 | return $this->utilities()->serializeForRollbarInternal($result);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Payload/Level.php:
--------------------------------------------------------------------------------
1 | level;
43 | }
44 |
45 | /**
46 | * Returns the Rollbar service numeric error level.
47 | *
48 | * @return int
49 | */
50 | public function toInt(): int
51 | {
52 | return $this->val;
53 | }
54 |
55 | /**
56 | * Returns the serialized Rollbar service level.
57 | *
58 | * @return string
59 | */
60 | public function serialize()
61 | {
62 | return $this->level;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Payload/Message.php:
--------------------------------------------------------------------------------
1 | body;
25 | }
26 |
27 | public function setBody(string $body): self
28 | {
29 | $this->body = $body;
30 | return $this;
31 | }
32 |
33 | public function getBacktrace(): ?array
34 | {
35 | return $this->backtrace;
36 | }
37 |
38 | public function setBacktrace(?array $backtrace): self
39 | {
40 | $this->backtrace = $backtrace;
41 | return $this;
42 | }
43 |
44 | public function serialize()
45 | {
46 | $toSerialize = array(
47 | "body" => $this->getBody(),
48 | "backtrace" => $this->getBacktrace()
49 | );
50 | return $this->utilities()->serializeForRollbar($toSerialize);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Payload/Notifier.php:
--------------------------------------------------------------------------------
1 | name;
27 | }
28 |
29 | public function setName(string $name): self
30 | {
31 | $this->name = $name;
32 | return $this;
33 | }
34 |
35 | public function getVersion(): string
36 | {
37 | return $this->version;
38 | }
39 |
40 | public function setVersion(string $version): self
41 | {
42 | $this->version = $version;
43 | return $this;
44 | }
45 |
46 | public function serialize()
47 | {
48 | $result = array(
49 | "name" => $this->name,
50 | "version" => $this->version,
51 | );
52 |
53 | return $this->utilities()->serializeForRollbarInternal($result);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Payload/Payload.php:
--------------------------------------------------------------------------------
1 | data;
24 | }
25 |
26 | public function setData(Data $data): self
27 | {
28 | $this->data = $data;
29 | return $this;
30 | }
31 |
32 | public function getAccessToken(): string
33 | {
34 | return $this->accessToken;
35 | }
36 |
37 | public function setAccessToken(string $accessToken): self
38 | {
39 | $this->accessToken = $accessToken;
40 | return $this;
41 | }
42 |
43 | public function serialize($maxDepth = -1): array
44 | {
45 | $objectHashes = array();
46 | $result = array(
47 | "data" => $this->data,
48 | "access_token" => $this->accessToken,
49 | );
50 |
51 | return $this->utilities()->serializeForRollbar($result, null, $objectHashes, $maxDepth);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Payload/Person.php:
--------------------------------------------------------------------------------
1 | $extra
22 | */
23 | private array $extra;
24 |
25 | public function __construct(
26 | private string $id,
27 | private ?string $username = null,
28 | private ?string $email = null,
29 | array $extra = [],
30 | ) {
31 | unset($extra['id'], $extra['email'], $extra['username']);
32 | $this->extra = $extra;
33 | }
34 |
35 | public function getId(): string
36 | {
37 | return $this->id;
38 | }
39 |
40 | public function setId(string $id): self
41 | {
42 | $this->id = $id;
43 | return $this;
44 | }
45 |
46 | public function getUsername(): ?string
47 | {
48 | return $this->username;
49 | }
50 |
51 | public function setUsername(?string $username): self
52 | {
53 | $this->username = $username;
54 | return $this;
55 | }
56 |
57 | public function getEmail(): ?string
58 | {
59 | return $this->email;
60 | }
61 |
62 | public function setEmail(?string $email): self
63 | {
64 | $this->email = $email;
65 | return $this;
66 | }
67 |
68 | public function __get($name)
69 | {
70 | return $this->extra[$name] ?? null;
71 | }
72 |
73 | public function __set($name, $val)
74 | {
75 | $this->extra[$name] = $val;
76 | }
77 |
78 | public function serialize()
79 | {
80 | $result = array(
81 | "id" => $this->id,
82 | "username" => $this->username,
83 | "email" => $this->email,
84 | );
85 | foreach ($this->extra as $key => $val) {
86 | $result[$key] = $val;
87 | }
88 |
89 | return $this->utilities()->serializeForRollbarInternal($result, array_keys($this->extra));
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Payload/Request.php:
--------------------------------------------------------------------------------
1 | url;
30 | }
31 |
32 | public function setUrl(?string $url): self
33 | {
34 | $this->url = $url;
35 | return $this;
36 | }
37 |
38 | public function getMethod(): ?string
39 | {
40 | return $this->method;
41 | }
42 |
43 | public function setMethod(?string $method): self
44 | {
45 | $this->method = $method;
46 | return $this;
47 | }
48 |
49 | public function getHeaders(): ?array
50 | {
51 | return $this->headers;
52 | }
53 |
54 | public function setHeaders(?array $headers = null): self
55 | {
56 | $this->headers = $headers;
57 | return $this;
58 | }
59 |
60 | public function getParams(): ?array
61 | {
62 | return $this->params;
63 | }
64 |
65 | public function setParams(?array $params = null): self
66 | {
67 | $this->params = $params;
68 | return $this;
69 | }
70 |
71 | public function getGet(): ?array
72 | {
73 | return $this->get;
74 | }
75 |
76 | public function setGet(?array $get = null): self
77 | {
78 | $this->get = $get;
79 | return $this;
80 | }
81 |
82 | public function getQueryString(): ?string
83 | {
84 | return $this->queryString;
85 | }
86 |
87 | public function setQueryString(?string $queryString): self
88 | {
89 | $this->queryString = $queryString;
90 | return $this;
91 | }
92 |
93 | public function getPost(): ?array
94 | {
95 | return $this->post;
96 | }
97 |
98 | public function setPost(?array $post = null): self
99 | {
100 | $this->post = $post;
101 | return $this;
102 | }
103 |
104 | public function getBody(): ?string
105 | {
106 | return $this->body;
107 | }
108 |
109 | public function setBody(?string $body): self
110 | {
111 | $this->body = $body;
112 | return $this;
113 | }
114 |
115 | public function getUserIp(): ?string
116 | {
117 | return $this->userIp;
118 | }
119 |
120 | public function setUserIp(?string $userIp): self
121 | {
122 | $this->userIp = $userIp;
123 | return $this;
124 | }
125 |
126 | public function getExtras(): array
127 | {
128 | return $this->extra;
129 | }
130 |
131 | public function setExtras(array $extras): self
132 | {
133 | $this->extra = $extras;
134 | return $this;
135 | }
136 |
137 | public function setSession(array $session): self
138 | {
139 | $this->extra['session'] = $session;
140 | return $this;
141 | }
142 |
143 | public function serialize()
144 | {
145 | $result = array(
146 | "url" => $this->url,
147 | "method" => $this->method,
148 | "headers" => $this->headers,
149 | "params" => $this->params,
150 | "GET" => $this->get,
151 | "query_string" => $this->queryString,
152 | "POST" => $this->post,
153 | "body" => $this->body,
154 | "user_ip" => $this->userIp,
155 | );
156 | foreach ($this->extra as $key => $val) {
157 | $result[$key] = $val;
158 | }
159 |
160 | return $this->utilities()->serializeForRollbarInternal($result, array_keys($this->extra));
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/Payload/Server.php:
--------------------------------------------------------------------------------
1 | host;
25 | }
26 |
27 | public function setHost(?string $host): self
28 | {
29 | $this->host = $host;
30 | return $this;
31 | }
32 |
33 | public function getRoot(): ?string
34 | {
35 | return $this->root;
36 | }
37 |
38 | public function setRoot(?string $root): self
39 | {
40 | $this->root = $root;
41 | return $this;
42 | }
43 |
44 | public function getBranch(): ?string
45 | {
46 | return $this->branch;
47 | }
48 |
49 | public function setBranch(?string $branch): self
50 | {
51 | $this->branch = $branch;
52 | return $this;
53 | }
54 |
55 | public function getCodeVersion(): ?string
56 | {
57 | return $this->codeVersion;
58 | }
59 |
60 | public function setCodeVersion(?string $codeVersion): self
61 | {
62 | $this->codeVersion = $codeVersion;
63 | return $this;
64 | }
65 |
66 | public function setExtras(array $extras): self
67 | {
68 | $this->extra = $extras;
69 | return $this;
70 | }
71 |
72 | public function getExtras(): array
73 | {
74 | return $this->extra;
75 | }
76 |
77 | public function setArgv(array $argv): self
78 | {
79 | $this->extra['argv'] = $argv;
80 | return $this;
81 | }
82 |
83 | public function serialize()
84 | {
85 | $result = array(
86 | "host" => $this->host,
87 | "root" => $this->root,
88 | "branch" => $this->branch,
89 | "code_version" => $this->codeVersion,
90 | );
91 | foreach ($this->extra as $key => $val) {
92 | $result[$key] = $val;
93 | }
94 |
95 | return $this->utilities()->serializeForRollbarInternal($result, array_keys($this->extra));
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/Payload/TelemetryBody.php:
--------------------------------------------------------------------------------
1 | extra = $extra;
71 | }
72 |
73 | /**
74 | * Creates a {@see TelemetryBody} instance from an array of data.
75 | *
76 | * The data array may be loosely structured, as only the keys that match the defined keys will be used to create the
77 | * instance. Any undefined keys in data will be stored in the {@see $extra} property.
78 | *
79 | * @param array $data The data to create the {@see TelemetryBody} instance from.
80 | * @return self
81 | *
82 | * @since 4.1.1
83 | */
84 | public static function fromArray(array $data): self
85 | {
86 | // This filters out any keys that are not accepted by the constructor to prevent duplicate parameter errors from
87 | // named and positional arguments.
88 | $params = array_intersect_key($data, array_flip(self::DEFINED_KEYS));
89 | // Generates an array of all the keys not used in the constructor.
90 | $extra = array_diff_key($data, $params);
91 | $instance = new self(...$params);
92 | $instance->extra = $extra;
93 | return $instance;
94 | }
95 |
96 | /**
97 | * Returns the array representation of the telemetry body.
98 | *
99 | * @return array
100 | */
101 | public function serialize(): array
102 | {
103 | // This filters out any null or empty values.
104 | $result = array_filter([
105 | 'message' => $this->message,
106 | 'method' => $this->method,
107 | 'url' => $this->url,
108 | 'status_code' => $this->status_code,
109 | 'subtype' => $this->subtype,
110 | 'stack' => $this->stack,
111 | 'from' => $this->from,
112 | 'to' => $this->to,
113 | 'start_timestamp_ms' => $this->start_timestamp_ms,
114 | 'end_timestamp_ms' => $this->end_timestamp_ms,
115 | ]);
116 |
117 | if (empty($this->extra)) {
118 | return $result;
119 | }
120 |
121 | // This keeps the extra data from overwriting the defined keys when the extra data is merged into the result.
122 | $extra = array_diff_key($this->extra, array_fill_keys(self::DEFINED_KEYS, null));
123 |
124 | return $this->utilities()->serializeForRollbarInternal(array_merge($result, $extra));
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Payload/TelemetryEvent.php:
--------------------------------------------------------------------------------
1 | timestamp)) {
48 | $this->timestamp = floor(microtime(true) * 1000);
49 | }
50 | $this->body = is_array($body) ? TelemetryBody::fromArray($body): $body;
51 | }
52 |
53 | public function serialize(): array
54 | {
55 | $result = array_filter([
56 | 'uuid' => $this->uuid,
57 | 'source' => $this->source,
58 | 'level' => $this->level->value,
59 | 'type' => $this->type->value,
60 | 'body' => $this->body->serialize(),
61 | 'timestamp_ms' => $this->timestamp,
62 | ]);
63 |
64 | return $this->utilities()->serializeForRollbarInternal($result);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Payload/Trace.php:
--------------------------------------------------------------------------------
1 | frames;
25 | }
26 |
27 | public function setFrames(array $frames): self
28 | {
29 | $this->frames = $frames;
30 | return $this;
31 | }
32 |
33 | public function getException(): ExceptionInfo
34 | {
35 | return $this->exception;
36 | }
37 |
38 | public function setException(ExceptionInfo $exception): self
39 | {
40 | $this->exception = $exception;
41 | return $this;
42 | }
43 |
44 | public function serialize()
45 | {
46 | $result = array(
47 | "frames" => $this->frames,
48 | "exception" => $this->exception,
49 | );
50 | return $this->utilities()->serializeForRollbar($result);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Payload/TraceChain.php:
--------------------------------------------------------------------------------
1 | traces;
21 | }
22 |
23 | public function setTraces(array $traces): self
24 | {
25 | $this->traces = $traces;
26 | return $this;
27 | }
28 |
29 | public function serialize()
30 | {
31 | $mapValue = function ($value) {
32 | if ($value instanceof \Serializable) {
33 | trigger_error("Using the Serializable interface has been deprecated.", E_USER_DEPRECATED);
34 | return $value->serialize();
35 | }
36 | if ($value instanceof SerializerInterface) {
37 | return $value->serialize();
38 | }
39 | return $value;
40 | };
41 | return array_map($mapValue, $this->traces);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Response.php:
--------------------------------------------------------------------------------
1 | status;
17 | }
18 |
19 | public function getInfo(): mixed
20 | {
21 | return $this->info;
22 | }
23 |
24 | public function getUuid(): string
25 | {
26 | return $this->uuid;
27 | }
28 |
29 | public function wasSuccessful(): bool
30 | {
31 | return $this->status >= 200 && $this->status < 300;
32 | }
33 |
34 | public function getOccurrenceUrl(): ?string
35 | {
36 | if (is_null($this->uuid)) {
37 | return null;
38 | }
39 | if (!$this->wasSuccessful()) {
40 | return null;
41 | }
42 | return "https://rollbar.com/occurrence/uuid/?uuid=" . urlencode($this->uuid);
43 | }
44 |
45 | public function __toString(): string
46 | {
47 | $url = $this->getOccurrenceUrl();
48 | return "Status: $this->status\n" .
49 | "Body: " . json_encode($this->info) . "\n" .
50 | "URL: $url";
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/ResponseHandlerInterface.php:
--------------------------------------------------------------------------------
1 | addJs($headers, $nonce, $customJs);
33 | }
34 |
35 | /**
36 | * Build Javascript required to include RollbarJS on
37 | * an HTML page
38 | *
39 | * @param array $headers Response headers usually retrieved through
40 | * headers_list() used to verify if nonce should be added to script
41 | * tags based on Content-Security-Policy
42 | * @param string $nonce Content-Security-Policy nonce string if exists
43 | * @param string $customJs Additional JavaScript to add at the end of
44 | * RollbarJs snippet
45 | *
46 | * @return string
47 | */
48 | public function addJs($headers = null, $nonce = null, $customJs = "")
49 | {
50 | return $this->scriptTag(
51 | $this->configJsTag() . $this->jsSnippet() . ";" . $customJs,
52 | $headers,
53 | $nonce
54 | );
55 | }
56 |
57 | /**
58 | * Build RollbarJS config script
59 | *
60 | * @return string
61 | */
62 | public function configJsTag()
63 | {
64 | return "var _rollbarConfig = " . json_encode((object)$this->config) . ";";
65 | }
66 |
67 | /**
68 | * Build rollbar.snippet.js string
69 | *
70 | * @return string
71 | */
72 | public function jsSnippet()
73 | {
74 | return file_get_contents(
75 | $this->snippetPath()
76 | );
77 | }
78 |
79 | /**
80 | * @return string Path to the rollbar.snippet.js
81 | */
82 | public function snippetPath()
83 | {
84 | return realpath(__DIR__ . "/../data/rollbar.snippet.js");
85 | }
86 |
87 | /**
88 | * Should JS snippet be added to the HTTP response
89 | *
90 | * @param int $status
91 | * @param array $headers
92 | *
93 | * @return boolean
94 | */
95 | public function shouldAddJs($status, $headers)
96 | {
97 | return
98 | $status == 200 &&
99 | $this->isHtml($headers) &&
100 | !$this->hasAttachment($headers);
101 |
102 | /**
103 | * @todo not sure if below two conditions will be applicable
104 | */
105 | /* !env[JS_IS_INJECTED_KEY] */
106 | /* && !streaming?(env) */
107 | }
108 |
109 | /**
110 | * Is the HTTP response a valid HTML response
111 | *
112 | * @param array $headers
113 | *
114 | * @return boolean
115 | */
116 | public function isHtml($headers)
117 | {
118 | return in_array('Content-Type: text/html', $headers);
119 | }
120 |
121 | /**
122 | * Does the HTTP response include an attachment
123 | *
124 | * @param array $headers
125 | *
126 | * @return boolean
127 | */
128 | public function hasAttachment($headers)
129 | {
130 | return in_array('Content-Disposition: attachment', $headers);
131 | }
132 |
133 | /**
134 | * Is `nonce` attribute on the script tag needed?
135 | *
136 | * @param array $headers
137 | *
138 | * @return boolean
139 | */
140 | public function shouldAppendNonce($headers)
141 | {
142 | foreach ($headers as $header) {
143 | if (str_contains($header, 'Content-Security-Policy') &&
144 | str_contains($header, "'unsafe-inline'")) {
145 | return true;
146 | }
147 | }
148 |
149 | return false;
150 | }
151 |
152 | /**
153 | * Build safe HTML script tag
154 | *
155 | * @param string $content
156 | * @param array $headers
157 | * @param
158 | *
159 | * @return string
160 | */
161 | public function scriptTag($content, $headers = null, $nonce = null)
162 | {
163 | if ($headers !== null && $this->shouldAppendNonce($headers)) {
164 | if (!$nonce) {
165 | throw new \Exception(
166 | 'Content-Security-Policy is script-src '.
167 | 'inline-unsafe but nonce value not provided.'
168 | );
169 | }
170 |
171 | return "\n";
172 | }
173 | return "\n";
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/src/ScrubberInterface.php:
--------------------------------------------------------------------------------
1 | agentLogLocation = \Rollbar\Defaults::get()->agentLogLocation();
21 | if (array_key_exists('agentLogLocation', $opts)) {
22 | $this->utilities()->validateString($opts['agentLogLocation'], 'opts["agentLogLocation"]', null, false);
23 | $this->agentLogLocation = $opts['agentLogLocation'];
24 | }
25 | }
26 |
27 | /**
28 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
29 | */
30 | public function send(EncodedPayload $payload, string $accessToken): Response
31 | {
32 | if (empty($this->agentLog)) {
33 | $this->loadAgentFile();
34 | }
35 | fwrite($this->agentLog, $payload->encoded() . "\n");
36 |
37 | $data = $payload->data();
38 | $uuid = $data['data']['uuid'];
39 | return new Response(0, "Written to agent file", $uuid);
40 | }
41 |
42 | /**
43 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
44 | */
45 | public function sendBatch(array $batch, string $accessToken): void
46 | {
47 | if (empty($this->agentLog)) {
48 | $this->loadAgentFile();
49 | }
50 | foreach ($batch as $payload) {
51 | fwrite($this->agentLog, $payload->encoded() . "\n");
52 | }
53 | }
54 |
55 | /**
56 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
57 | */
58 | public function wait(string $accessToken, int $max): void
59 | {
60 | return;
61 | }
62 |
63 | /**
64 | * Returns true if the access token is required by the sender to send the payload. The agent can be configured to
65 | * provide its own access token. But may not have its own, so we are requiring it for now. See
66 | * {@link https://github.com/rollbar/rollbar-php/issues/405} for more details.
67 | *
68 | * @since 4.0.0
69 | *
70 | * @return bool
71 | */
72 | public function requireAccessToken(): bool
73 | {
74 | return true;
75 | }
76 |
77 | private function loadAgentFile()
78 | {
79 | $filename = $this->agentLogLocation . '/rollbar-relay.' . getmypid() . '.' . microtime(true) . '.rollbar';
80 | $this->agentLog = fopen($filename, 'a');
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Senders/FluentSender.php:
--------------------------------------------------------------------------------
1 | fluentHost = \Rollbar\Defaults::get()->fluentHost();
44 | $this->fluentPort = \Rollbar\Defaults::get()->fluentPort();
45 | $this->fluentTag = \Rollbar\Defaults::get()->fluentTag();
46 |
47 | if (isset($opts['fluentHost'])) {
48 | $this->utilities()->validateString($opts['fluentHost'], 'opts["fluentHost"]', null, false);
49 | $this->fluentHost = $opts['fluentHost'];
50 | }
51 |
52 | if (isset($opts['fluentPort'])) {
53 | $this->utilities()->validateInteger($opts['fluentPort'], 'opts["fluentPort"]', null, null, false);
54 | $this->fluentPort = $opts['fluentPort'];
55 | }
56 |
57 | if (isset($opts['fluentTag'])) {
58 | $this->utilities()->validateString($opts['fluentTag'], 'opts["fluentTag"]', null, false);
59 | $this->fluentTag = $opts['fluentTag'];
60 | }
61 | }
62 |
63 |
64 | /**
65 | * @param \Rollbar\Payload\EncodedPayload $payload
66 | * @param string $accessToken
67 | * @return Response
68 | *
69 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) Unused parameter is
70 | * intended here to comply with SenderInterface
71 | */
72 | public function send(EncodedPayload $payload, string $accessToken): Response
73 | {
74 | if (empty($this->fluentLogger)) {
75 | $this->loadFluentLogger();
76 | }
77 |
78 | $scrubbedPayload = $payload->data();
79 |
80 | $success = $this->fluentLogger->post($this->fluentTag, $scrubbedPayload);
81 | $status = $success ? 200 : 400;
82 | $info = $success ? 'OK' : 'Bad Request';
83 | $uuid = $scrubbedPayload['data']['uuid'];
84 |
85 | return new Response($status, $info, $uuid);
86 | }
87 |
88 | public function sendBatch(array $batch, string $accessToken, &$responses = array ()): void
89 | {
90 | $responses = array();
91 | foreach ($batch as $payload) {
92 | $responses[] = $this->send($payload, $accessToken);
93 | }
94 | }
95 |
96 | /**
97 | * @SuppressWarnings(PHPMD.UnusedFormalParameter)
98 | */
99 | public function wait(string $accessToken, int $max): void
100 | {
101 | return;
102 | }
103 |
104 | /**
105 | * Returns true if the access token is required by the sender to send the payload. The Fluentd service can provide
106 | * its own access token.
107 | *
108 | * @return bool
109 | * @since 4.0.0
110 | */
111 | public function requireAccessToken(): bool
112 | {
113 | return false;
114 | }
115 |
116 | /**
117 | * Loads the fluent logger
118 | */
119 | protected function loadFluentLogger()
120 | {
121 | $this->fluentLogger = new FluentLogger($this->fluentHost, $this->fluentPort);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/Senders/SenderInterface.php:
--------------------------------------------------------------------------------
1 | includeItemsInTelemetry` config
49 | * option to `false`.
50 | * 2. Ignored Rollbar items are not included in the telemetry data by changing the default value of the
51 | * `telemetry => includeIgnoredItemsInTelemetry` config option to `true`. And the reported item is ignored
52 | * because of its log level or PHP error reporting level.
53 | *
54 | * @param string $level The PSR-3 log level.
55 | * @param string|Stringable $message The message to log.
56 | * @param array $context The context.
57 | * @param bool $ignored Whether the item was ignored as a result of the configuration. If false, then
58 | * the item will not be sent to Rollbar. However, you may still want to include
59 | * it in the telemetry data.
60 | *
61 | * @return bool True if the item should be included in the telemetry queue, false if it should be excluded.
62 | */
63 | public function includeRollbarItem(
64 | string $level,
65 | string|Stringable $message,
66 | array $context = [],
67 | bool $ignored = false,
68 | ): bool;
69 |
70 | /**
71 | * Returns `true` if the {@see include()} method should be called not only before the event is added to the queue,
72 | * but also before the event is sent to Rollbar. This means the {@see include()} method will be called twice for
73 | * each event.
74 | *
75 | * If this method returns `false`, then the {@see include()} method will only be called before the event is added to
76 | * the queue.
77 | *
78 | * @return bool
79 | */
80 | public function filterOnRead(): bool;
81 | }
82 |
--------------------------------------------------------------------------------
/src/TransformerInterface.php:
--------------------------------------------------------------------------------
1 | data();
32 |
33 | if (isset($data['data']['body']['trace_chain'])) {
34 | foreach ($data['data']['body']['trace_chain'] as $offset => $value) {
35 | $data['data']['body']['trace_chain'][$offset]['frames'] = self::selectFrames($value['frames']);
36 | }
37 |
38 | $payload->encode($data);
39 | } elseif (isset($data['data']['body']['trace']['frames'])) {
40 | $data['data']['body']['trace']['frames'] = self::selectFrames($data['data']['body']['trace']['frames']);
41 | $payload->encode($data);
42 | }
43 |
44 | return $payload;
45 | }
46 |
47 | /**
48 | * Removes frames from the middle of the stack trace frames. Will keep the number of frames specified by $range at
49 | * the start and end of the array.
50 | *
51 | * This method is also used by {@see MinBodyStrategy}.
52 | *
53 | * @param array $frames The list of stack trace frames.
54 | * @param int $range The number of frames to keep on each end of the frames array.
55 | *
56 | * @return array
57 | */
58 | public static function selectFrames(array $frames, int $range = self::FRAMES_OPTIMIZATION_RANGE): array
59 | {
60 | if (count($frames) <= $range * 2) {
61 | return $frames;
62 | }
63 |
64 | return array_merge(
65 | array_splice($frames, 0, $range),
66 | array_splice($frames, -$range, $range)
67 | );
68 | }
69 |
70 | /**
71 | * Returns true if the payload has a trace chain or trace frames.
72 | *
73 | * @param EncodedPayload $payload
74 | *
75 | * @return bool
76 | */
77 | public function applies(EncodedPayload $payload): bool
78 | {
79 | $payload = $payload->data();
80 |
81 | if (isset($payload['data']['body']['trace_chain']) ||
82 | isset($payload['data']['body']['trace']['frames'])) {
83 | return true;
84 | }
85 |
86 | return false;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/Truncation/MinBodyStrategy.php:
--------------------------------------------------------------------------------
1 | data();
37 | $modified = false;
38 | $traceData = array();
39 |
40 | if (isset($data['data']['body']['trace'])) {
41 | $traceData = &$data['data']['body']['trace'];
42 | } elseif (isset($data['data']['body']['trace_chain'])) {
43 | $traceData = &$data['data']['body']['trace_chain'];
44 | }
45 |
46 | if (isset($traceData['exception'])) {
47 | // Delete exception description
48 | unset($traceData['exception']['description']);
49 |
50 | // Truncate exception message
51 | $traceData['exception']['message'] = substr(
52 | $traceData['exception']['message'],
53 | 0,
54 | static::EXCEPTION_MESSAGE_LIMIT
55 | );
56 |
57 | $modified = true;
58 | }
59 |
60 | // Limit trace frames
61 | if (!empty($traceData['frames'])) {
62 | $traceData['frames'] = FramesStrategy::selectFrames(
63 | $traceData['frames'],
64 | static::EXCEPTION_FRAMES_RANGE
65 | );
66 |
67 | $modified = true;
68 | }
69 |
70 | if ($modified) {
71 | $payload->encode($data);
72 | }
73 |
74 | return $payload;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Truncation/RawStrategy.php:
--------------------------------------------------------------------------------
1 | data();
37 | $modified = false;
38 |
39 | foreach (static::getThresholds() as $threshold) {
40 | if (!$this->truncation->needsTruncating($payload)) {
41 | break;
42 | }
43 |
44 | if ($this->traverse($data, $threshold, $payload)) {
45 | $modified = true;
46 | }
47 | }
48 |
49 | if ($modified) {
50 | $payload->encode($data);
51 | }
52 |
53 | return $payload;
54 | }
55 |
56 | /**
57 | * Traverse recursively reduces the length of each string to the max length of $threshold. The strings in the $data
58 | * array are truncated in place and not returned.
59 | *
60 | * @param array $data An array that may contain strings needing to be truncated.
61 | * @param int $threshold The maximum length string may be before it is truncated.
62 | * @param EncodedPayload $payload The payload that may need to be truncated.
63 | *
64 | * @return bool Returns true if the data was modified.
65 | */
66 | protected function traverse(array &$data, int $threshold, EncodedPayload $payload): bool
67 | {
68 | $modified = false;
69 |
70 | foreach ($data as &$value) {
71 | if (is_array($value)) {
72 | if ($this->traverse($value, $threshold, $payload)) {
73 | $modified = true;
74 | }
75 | continue;
76 | }
77 | if (is_string($value) && (($strlen = strlen($value)) > $threshold)) {
78 | $value = substr($value, 0, $threshold);
79 | $modified = true;
80 | $payload->decreaseSize($strlen - $threshold);
81 | }
82 | }
83 |
84 | return $modified;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Truncation/TelemetryStrategy.php:
--------------------------------------------------------------------------------
1 | data();
56 |
57 | // If telemetry is not enabled, then remove the telemetry data from the payload entirely.
58 | if (null === Rollbar::getTelemeter()) {
59 | unset($data['data']['body']['telemetry']);
60 | $payload->encode($data);
61 | return $payload;
62 | }
63 |
64 | if (!isset($data['data']['body']['telemetry'])) {
65 | return $payload;
66 | }
67 |
68 | $data['data']['body']['telemetry'] = self::selectTelemetry($data['data']['body']['telemetry']);
69 | $payload->encode($data);
70 |
71 | return $payload;
72 | }
73 |
74 | /**
75 | * Returns true if the given payload contains telemetry data. This is irrespective of whether the telemetry is
76 | * enabled or not.
77 | *
78 | * @param EncodedPayload $payload The payload to truncate.
79 | *
80 | * @return bool
81 | *
82 | * @since 4.1.0
83 | */
84 | public function applies(EncodedPayload $payload): bool
85 | {
86 | // If the payload does not telemetry data, then this strategy does not apply.
87 | return isset($payload->data()['data']['body']['telemetry']);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Truncation/Truncation.php:
--------------------------------------------------------------------------------
1 | getCustomTruncation()) {
38 | $this->registerStrategy($custom);
39 | }
40 | }
41 |
42 | /**
43 | * Adds a new truncation strategy to the list of strategies used to truncate large payloads before they are sent
44 | * over the wire. A strategy registered with this method will be used before any existing ones are used. It does
45 | * not replace or remove existing strategies.
46 | *
47 | * @param string $type The fully qualified class name of a truncation strategy. The strategy must implement the
48 | * {@see StrategyInterface} interface.
49 | *
50 | * @return void
51 | * @throws Exception If the strategy class does not implement {@see StrategyInterface}.
52 | */
53 | public function registerStrategy(string $type): void
54 | {
55 | if (!class_exists($type)) {
56 | throw new Exception('Truncation strategy "' . $type . '" doesn\'t exist.');
57 | }
58 | if (!in_array(StrategyInterface::class, class_implements($type))) {
59 | throw new Exception(
60 | 'Truncation strategy "' . $type . '" doesn\'t implement ' . StrategyInterface::class
61 | );
62 | }
63 | array_unshift(static::$truncationStrategies, $type);
64 | }
65 |
66 | /**
67 | * Applies truncation strategies in order to keep the payload size under
68 | * configured limit.
69 | *
70 | * @param EncodedPayload $payload The payload that may need to be truncated.
71 | *
72 | * @return EncodedPayload
73 | */
74 | public function truncate(EncodedPayload $payload): EncodedPayload
75 | {
76 | foreach (static::$truncationStrategies as $strategy) {
77 | $strategy = new $strategy($this);
78 |
79 | if (!$strategy->applies($payload)) {
80 | continue;
81 | }
82 |
83 | if (!$this->needsTruncating($payload)) {
84 | break;
85 | }
86 |
87 | $this->config->verboseLogger()->debug('Applying truncation strategy ' . get_class($strategy));
88 |
89 | $payload = $strategy->execute($payload);
90 | }
91 |
92 | return $payload;
93 | }
94 |
95 | /**
96 | * Check if the payload is too big to be sent
97 | *
98 | * @param EncodedPayload $payload The payload that may need to be truncated.
99 | *
100 | * @return boolean
101 | */
102 | public function needsTruncating(EncodedPayload $payload): bool
103 | {
104 | return $payload->size() > self::MAX_PAYLOAD_SIZE;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/UtilitiesTrait.php:
--------------------------------------------------------------------------------
1 | path)) {
19 | mkdir($this->path);
20 | }
21 | }
22 |
23 | public function testAgent(): void
24 | {
25 | Rollbar\Rollbar::init(array(
26 | 'access_token' => $this->getTestAccessToken(),
27 | 'environment' => 'testing',
28 | 'agent_log_location' => $this->path,
29 | 'handler' => 'agent'
30 | ), false, false, false);
31 | $logger = Rollbar\Rollbar::logger();
32 | $logger->info("this is a test");
33 | $file = fopen($this->path . '/rollbar-relay.' . getmypid() . '.' . microtime() . '.rollbar', 'r');
34 | $line = fgets($file);
35 | $this->assertStringContainsString('this is a test', $line);
36 | }
37 |
38 | public function tearDown(): void
39 | {
40 | parent::tearDown();
41 | $this->rrmdir($this->path);
42 | }
43 |
44 | private function rrmdir($dir): void
45 | {
46 | if (!is_dir($dir)) {
47 | return;
48 | }
49 |
50 | $objects = scandir($dir);
51 | foreach ($objects as $object) {
52 | if ($object != "." && $object != "..") {
53 | if (filetype($dir . "/" . $object) == "dir") {
54 | $this->rrmdir($dir . "/" . $object);
55 | } else {
56 | unlink($dir . "/" . $object);
57 | }
58 | }
59 | }
60 | reset($objects);
61 | rmdir($dir);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/BaseRollbarTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($value, $body->getValue());
14 |
15 | $mock2 = m::mock(ContentInterface::class);
16 | $this->assertEquals($mock2, $body->setValue($mock2)->getValue());
17 | }
18 |
19 | public function testExtra(): void
20 | {
21 | $value = m::mock(ContentInterface::class)
22 | ->shouldReceive("serialize")
23 | ->andReturn("{CONTENT}")
24 | ->shouldReceive("getKey")
25 | ->andReturn("content_interface")
26 | ->mock();
27 | $expected = array(
28 | "hello" => "world"
29 | );
30 | $body = new Body($value, $expected);
31 | $this->assertEquals($body->getExtra(), $expected);
32 | }
33 |
34 | public function testSerialize(): void
35 | {
36 | $value = m::mock(ContentInterface::class)
37 | ->shouldReceive("serialize")
38 | ->andReturn("{CONTENT}")
39 | ->shouldReceive("getKey")
40 | ->andReturn("content_interface")
41 | ->mock();
42 | $body = new Body($value, array('foo' => 'bar'));
43 | $encoded = json_encode($body->serialize());
44 | $this->assertEquals(
45 | "{\"content_interface\":\"{CONTENT}\",\"extra\":{\"foo\":\"bar\"}}",
46 | $encoded
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/ContextTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($pre, $context->getPre());
13 |
14 | $pre2 = array("lineone", "linetwo");
15 | $this->assertEquals($pre2, $context->setPre($pre2)->getPre());
16 | }
17 |
18 | public function testContextPost(): void
19 | {
20 | $post = array("four", "five");
21 | $context = new Context(array(), $post);
22 | $this->assertEquals($post, $context->getPost());
23 |
24 | $post2 = array("six", "seven", "eight");
25 | $this->assertEquals($post2, $context->setPost($post2)->getPost());
26 | }
27 |
28 | public function testEncode(): void
29 | {
30 | $context = new Context(array(), array());
31 | $encoded = json_encode($context->serialize());
32 | $this->assertEquals('{"pre":[],"post":[]}', $encoded);
33 |
34 | $context = new Context(array("one"), array("three"));
35 | $encoded = json_encode($context->serialize());
36 | $this->assertEquals('{"pre":["one"],"post":["three"]}', $encoded);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/CurlSenderTest.php:
--------------------------------------------------------------------------------
1 | $this->getTestAccessToken(),
17 | "environment" => "testing-php",
18 | "endpoint" => "fake-endpoint"
19 | ));
20 | $response = $logger->report(Level::WARNING, "Testing PHP Notifier", array());
21 |
22 | $this->assertContains(
23 | $response->getInfo(),
24 | array(
25 | "Couldn't resolve host 'fake-endpointitem'", // hack for PHP 5.3
26 | "Could not resolve host: fake-endpointitem",
27 | "Could not resolve: fake-endpointitem (Domain name not found)",
28 | "Empty reply from server"
29 | )
30 | );
31 | }
32 |
33 | /**
34 | * This test will fail if the {@see CurlSender::$ipResolve} property is renamed or removed.
35 | */
36 | public function testIPResolve(): void
37 | {
38 | $sender = new CurlSender([
39 | 'ip_resolve' => CURL_IPRESOLVE_V4,
40 | ]);
41 | self::assertSame(CURL_IPRESOLVE_V4, self::getPrivateProperty($sender, 'ipResolve'));
42 |
43 | $sender = new CurlSender([]);
44 | self::assertSame(CURL_IPRESOLVE_V4, self::getPrivateProperty($sender, 'ipResolve'));
45 |
46 | $sender = new CurlSender([
47 | 'ip_resolve' => CURL_IPRESOLVE_V6,
48 | ]);
49 | self::assertSame(CURL_IPRESOLVE_V6, self::getPrivateProperty($sender, 'ipResolve'));
50 |
51 | $sender = new CurlSender([
52 | 'ip_resolve' => CURL_IPRESOLVE_WHATEVER,
53 | ]);
54 | self::assertSame(CURL_IPRESOLVE_WHATEVER, self::getPrivateProperty($sender, 'ipResolve'));
55 | }
56 |
57 | /**
58 | * Returns the value of a private property of an object.
59 | *
60 | * @param object $object The object from which to get the private property.
61 | * @param string $property The name of the private property to get.
62 | * @return mixed
63 | * @throws ReflectionException If the object or property does not exist.
64 | */
65 | private static function getPrivateProperty(object $object, string $property): mixed
66 | {
67 | $reflection = new ReflectionClass($object);
68 | $property = $reflection->getProperty($property);
69 | $property->setAccessible(true);
70 | return $property->getValue($object);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tests/ErrorWrapperTest.php:
--------------------------------------------------------------------------------
1 | 'FAKE BACKTRACE'],
15 | new Utilities
16 | );
17 | $this->assertEquals(['fake' => 'FAKE BACKTRACE'], $errWrapper->getBacktrace());
18 | }
19 |
20 | public function testGetClassName(): void
21 | {
22 | $errWrapper = new ErrorWrapper(
23 | E_ERROR,
24 | "Message Content",
25 | null,
26 | null,
27 | null,
28 | new Utilities
29 | );
30 | $this->assertEquals("E_ERROR", $errWrapper->getClassName());
31 |
32 | $errWrapper = new ErrorWrapper(
33 | 3,
34 | "Fake Error Number",
35 | null,
36 | null,
37 | null,
38 | new Utilities
39 | );
40 | $this->assertEquals("#3", $errWrapper->getClassName());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/ExceptionInfoTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($class, $exc->getClass());
12 |
13 | $this->assertEquals("TestClass", $exc->setClass("TestClass")->getClass());
14 | }
15 |
16 | public function testMessage(): void
17 | {
18 | $message = "A message";
19 | $exc = new ExceptionInfo("C", $message);
20 | $this->assertEquals($message, $exc->getMessage());
21 |
22 | $this->assertEquals("Another", $exc->setMessage("Another")->getMessage());
23 | }
24 |
25 | public function testDescription(): void
26 | {
27 | $description = "long form";
28 | $exc = new ExceptionInfo("C", "s", $description);
29 | $this->assertEquals($description, $exc->getDescription());
30 |
31 | $this->assertEquals("longer form", $exc->setDescription("longer form")->getDescription());
32 | $this->assertNull($exc->setDescription(null)->getDescription());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/FakeDataBuilder.php:
--------------------------------------------------------------------------------
1 | markTestSkipped(
14 | 'Suggested package fluent/logger not installed, skip FluentTest'
15 | );
16 | }
17 |
18 | $socket = socket_create_listen(null);
19 | socket_getsockname($socket, $address, $port);
20 |
21 | Rollbar::init(array(
22 | 'access_token' => 'ad865e76e7fb496fab096ac07b1dbabb',
23 | 'environment' => 'testing'
24 | ), false, false, false);
25 | $logger = Rollbar::scope(array(
26 | 'batched' => false,
27 | 'fluent_host' => $address,
28 | 'fluent_port' => $port,
29 | 'handler' => 'fluent'
30 | ));
31 | $this->assertEquals(200, $logger->report(Level::INFO, 'this is a test', array())->getStatus());
32 |
33 | socket_close($socket);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/FrameTest.php:
--------------------------------------------------------------------------------
1 | exception = m::mock(ExceptionInfo::class);
16 | $this->frame = new Frame("tests/FrameTest.php");
17 | }
18 |
19 | public function testFilename(): void
20 | {
21 | $frame = new Frame("filename.php");
22 | $this->assertEquals("filename.php", $frame->getFilename());
23 | $frame->setFilename("other.php");
24 | $this->assertEquals("other.php", $frame->getFilename());
25 | }
26 |
27 | public function testLineno(): void
28 | {
29 | $this->frame->setLineno(5);
30 | $this->assertEquals(5, $this->frame->getLineno());
31 | }
32 |
33 | public function testColno(): void
34 | {
35 | $this->frame->setColno(5);
36 | $this->assertEquals(5, $this->frame->getColno());
37 | }
38 |
39 | public function testMethod(): void
40 | {
41 | $this->frame->setMethod("method");
42 | $this->assertEquals("method", $this->frame->getMethod());
43 | }
44 |
45 | public function testCode(): void
46 | {
47 | $this->frame->setCode("code->whatever()");
48 | $this->assertEquals("code->whatever()", $this->frame->getCode());
49 | }
50 |
51 | public function testContext(): void
52 | {
53 | $context = m::mock(Context::class);
54 | $this->frame->setContext($context);
55 | $this->assertEquals($context, $this->frame->getContext());
56 | }
57 |
58 | public function testArgs(): void
59 | {
60 | $this->frame->setArgs(array());
61 | $this->assertEquals(array(), $this->frame->getArgs());
62 |
63 | $this->frame->setArgs(array(1, "hi"));
64 | $this->assertEquals(array(1, "hi"), $this->frame->getArgs());
65 | }
66 |
67 | public function testEncode(): void
68 | {
69 | $context = m::mock("Rollbar\Payload\Context, Rollbar\SerializerInterface")
70 | ->shouldReceive("serialize")
71 | ->andReturn("{CONTEXT}")
72 | ->mock();
73 | $this->exception
74 | ->shouldReceive("serialize")
75 | ->andReturn("{EXC}")
76 | ->mock();
77 | $this->frame->setFilename("rollbar.php")
78 | ->setLineno(1024)
79 | ->setColno(42)
80 | ->setMethod("testEncode()")
81 | ->setCode('$frame->setFilename("rollbar.php")')
82 | ->setContext($context)
83 | ->setArgs(array("hello", "world"));
84 |
85 | $actual = json_encode($this->frame->serialize());
86 | $expected = '{' .
87 | '"filename":"rollbar.php",' .
88 | '"lineno":1024,"colno":42,' .
89 | '"method":"testEncode()",' .
90 | '"code":"$frame->setFilename(\"rollbar.php\")",' .
91 | '"context":"{CONTEXT}",' .
92 | '"args":["hello","world"]' .
93 | '}';
94 |
95 | $this->assertEquals($expected, $actual);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/tests/Handlers/ErrorHandlerTest.php:
--------------------------------------------------------------------------------
1 | useErrorHandler to deal with stopping the
10 | * PHPUnit's error handler instead of set_error_handler()
11 | */
12 | class ErrorHandlerTest extends BaseRollbarTest
13 | {
14 | public function __construct($name = null, $data = array(), $dataName = null)
15 | {
16 | self::$simpleConfig['access_token'] = $this->getTestAccessToken();
17 | self::$simpleConfig['included_errno'] = E_ALL;
18 | self::$simpleConfig['environment'] = 'test';
19 |
20 | parent::__construct($name, $data, $dataName);
21 | }
22 |
23 | private static array $simpleConfig = array();
24 |
25 | public function testPreviousErrorHandler(): void
26 | {
27 | $testCase = $this;
28 |
29 | set_error_handler(function () use ($testCase) {
30 |
31 | $testCase->assertTrue(true, "Previous error handler invoked.");
32 |
33 | set_error_handler(null);
34 | });
35 |
36 | Rollbar::init(self::$simpleConfig);
37 |
38 | @trigger_error(E_USER_ERROR);
39 | }
40 |
41 | public function testRegister(): void
42 | {
43 | $handler = $this->getMockBuilder(ErrorHandler::class)
44 | ->setConstructorArgs(array(new RollbarLogger(self::$simpleConfig)))
45 | ->onlyMethods(array('handle'))
46 | ->getMock();
47 |
48 | $handler->expects($this->once())
49 | ->method('handle');
50 |
51 | $handler->register();
52 |
53 | trigger_error(E_USER_ERROR);
54 | }
55 |
56 | public function testHandle(): void
57 | {
58 | $logger = $this->getMockBuilder(RollbarLogger::class)
59 | ->setConstructorArgs(array(self::$simpleConfig))
60 | ->onlyMethods(array('report'))
61 | ->getMock();
62 |
63 | $logger->expects($this->once())
64 | ->method('report');
65 |
66 | /**
67 | * Disable PHPUnit's error handler as it would get triggered as the
68 | * previously set error handler. No need for that here.
69 | */
70 | $phpunitHandler = set_error_handler(function () {
71 | });
72 |
73 | $handler = new ErrorHandler($logger);
74 | $handler->register();
75 |
76 | $handler->handle(E_USER_ERROR, "", "", "");
77 |
78 | /**
79 | * Clean up the error handler set up for this test.
80 | */
81 | set_error_handler($phpunitHandler);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/tests/Handlers/ExceptionHandlerTest.php:
--------------------------------------------------------------------------------
1 | getTestAccessToken();
13 | self::$simpleConfig['included_errno'] = E_ALL;
14 | self::$simpleConfig['environment'] = 'test';
15 |
16 | parent::__construct($name, $data, $dataName);
17 | }
18 |
19 | private static array $simpleConfig = array();
20 |
21 | /**
22 | * It's impossible to throw an uncaught exception with PHPUnit and thus
23 | * trigger the exception handler automatically. To overcome this limitation,
24 | * this test invokes the handle() methd manually with an assertion in the
25 | * previously set exception handler.
26 | */
27 | public function testPreviousExceptionHandler(): void
28 | {
29 | $testCase = $this;
30 |
31 | set_exception_handler(function () use ($testCase) {
32 |
33 | $testCase->assertTrue(true, "Previous exception handler invoked.");
34 | });
35 |
36 | $handler = new ExceptionHandler(new RollbarLogger(self::$simpleConfig));
37 | $handler->register();
38 |
39 | $handler->handle(new \Exception());
40 | }
41 |
42 | /**
43 | * It's impossible to throw an uncaught exception with PHPUnit and thus
44 | * trigger the exception handler automatically. To overcome this limitation,
45 | * this test fetches the exception handler set by the setup method with
46 | * set_exception_handler() and invokes it manually with a mock expectation.
47 | */
48 | public function testSetup(): void
49 | {
50 | $handler = $this->getMockBuilder(ExceptionHandler::class)
51 | ->setConstructorArgs(array(new RollbarLogger(self::$simpleConfig)))
52 | ->onlyMethods(array('handle'))
53 | ->getMock();
54 |
55 | $handler->expects($this->once())
56 | ->method('handle');
57 |
58 | $handler->register();
59 |
60 | $setExceptionHandler = set_exception_handler(function () {
61 | });
62 |
63 | $handler = $setExceptionHandler[0];
64 | $method = $setExceptionHandler[1];
65 |
66 | $handler->$method();
67 | }
68 |
69 | public function testHandle(): void
70 | {
71 | set_exception_handler(function () {
72 | });
73 |
74 | $logger = $this->getMockBuilder(RollbarLogger::class)
75 | ->setConstructorArgs(array(self::$simpleConfig))
76 | ->onlyMethods(array('report'))
77 | ->getMock();
78 |
79 | $logger->expects($this->once())
80 | ->method('report');
81 |
82 | $handler = new ExceptionHandler($logger);
83 | $handler->register();
84 |
85 | $handler->handle(new \Exception());
86 |
87 | set_exception_handler(function () {
88 | });
89 | }
90 |
91 | /**
92 | * This test is specifically for the deprecated dynamic properties in PHP 8.2. We were setting a property named
93 | * "isUncaught" on the exception object, which is now deprecated. This test ensures that we are no longer setting
94 | * that property.
95 | *
96 | * @return void
97 | */
98 | public function testDeprecatedDynamicProperties(): void
99 | {
100 | // Set error reporting level and error handler to capture deprecation
101 | // warnings.
102 | $prev = error_reporting(E_ALL);
103 | $errors = array();
104 | set_error_handler(function ($errno, $errstr, $errfile, $errline) use (&$errors) {
105 | $errors[] = array(
106 | 'errno' => $errno,
107 | 'errstr' => $errstr,
108 | 'errfile' => $errfile,
109 | 'errline' => $errline,
110 | );
111 | });
112 | $handler = new ExceptionHandler(new RollbarLogger(self::$simpleConfig));
113 | $handler->register();
114 |
115 | $handler->handle(new \Exception());
116 | restore_error_handler();
117 | error_reporting($prev);
118 |
119 | // self::assertSame used instead of self::assertSame so the contents of
120 | // $errors are printed in the test output.
121 | self::assertSame([], $errors);
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/tests/LevelFactoryTest.php:
--------------------------------------------------------------------------------
1 | 'value 1');
10 | $msg = new Message("Test", $expected);
11 | $this->assertEquals($expected, $msg->getBacktrace());
12 | }
13 |
14 | public function testBody(): void
15 | {
16 | $msg = new Message("Test");
17 | $this->assertEquals("Test", $msg->getBody());
18 |
19 | $this->assertEquals("Test2", $msg->setBody("Test2")->getBody());
20 | }
21 |
22 | public function testMessageKey(): void
23 | {
24 | $msg = new Message("Test");
25 | $this->assertEquals("message", $msg->getKey());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/NotifierTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($name, $notifier->getName());
13 |
14 | $name2 = "RollbarPHP";
15 | $this->assertEquals($name2, $notifier->setName($name2)->getName());
16 | }
17 |
18 | public function testVersion(): void
19 | {
20 | $version = Notifier::VERSION;
21 | $notifier = new Notifier("PHP-Rollbar", $version);
22 | $this->assertEquals($version, $notifier->getVersion());
23 |
24 | $version2 = "0.9";
25 | $this->assertEquals($version2, $notifier->setVersion($version2)->getVersion());
26 | }
27 |
28 | public function testDefaultNotifierIsRepresentableAsJson(): void
29 | {
30 | $notifier = Notifier::defaultNotifier()->serialize();
31 | $encoding = json_encode($notifier, flags: JSON_THROW_ON_ERROR|JSON_FORCE_OBJECT);
32 | $decoding = json_decode($encoding, flags: JSON_THROW_ON_ERROR);
33 |
34 | if (method_exists($this, 'assertObjectHasProperty')) {
35 | # phpUnit 10
36 | $this->assertObjectHasProperty('name', $decoding);
37 | $this->assertObjectHasProperty('version', $decoding);
38 | } else {
39 | # phpUnit 9
40 | $this->assertObjectHasAttribute('name', $decoding);
41 | $this->assertObjectHasAttribute('version', $decoding);
42 | }
43 | }
44 |
45 | public function testDefaultNotifierVersionIsSemVerCompliant(): void
46 | {
47 | // https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
48 | $semVerRegex = '/
49 | (0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)
50 | (?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
51 | (?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?
52 | (?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?
53 | /x';
54 | $this->assertMatchesRegularExpression(
55 | $semVerRegex,
56 | Notifier::defaultNotifier()->getVersion()
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/tests/Payload/EncodedPayloadTest.php:
--------------------------------------------------------------------------------
1 | "bar"
11 | );
12 |
13 | $encoded = new EncodedPayload($data);
14 | $encoded->encode();
15 |
16 | $this->assertEquals($expected, $encoded);
17 |
18 | $expected = '{"new":"bar"}';
19 | $encoded->encode(array("new" => "bar"));
20 |
21 | $this->assertEquals($expected, $encoded);
22 | }
23 |
24 | /**
25 | * @requires PHP 5.5
26 | */
27 | public function testEncodeMalformedData(): void
28 | {
29 | $encoded = new EncodedPayload(array(
30 | 'data' => array(
31 | 'body' => array(
32 | 'exception' => array(
33 | 'trace' => array(
34 | 'frames' => fopen('/dev/null', 'r')
35 | ),
36 | ),
37 | 'ecodable1' => true
38 | ),
39 | 'ecodable2' => true
40 | )
41 | ));
42 | $encoded->encode();
43 |
44 | $result = json_decode($encoded->encoded(), true);
45 |
46 | if (defined('HHVM_VERSION')) {
47 | $this->assertEmpty($result['data']['body']['exception']['trace']['frames']);
48 | } else {
49 | $this->assertNull($result['data']['body']['exception']['trace']['frames']);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Payload/LevelTest.php:
--------------------------------------------------------------------------------
1 | level = (new LevelFactory())->fromName(Level::ERROR);
12 | }
13 |
14 | public function testInvalidLevelThrowsAnException(): void
15 | {
16 | self::expectException(\Error::class);
17 | $level = Level::TEST();
18 | }
19 |
20 | public function testLevel(): void
21 | {
22 | $level = Level::CRITICAL;
23 | self::assertNotNull($level);
24 | self::assertSame(Level::CRITICAL, $level);
25 | }
26 |
27 | public function testStringable(): void
28 | {
29 | self::assertSame('error', (string)$this->level);
30 | }
31 |
32 | public function testToInt(): void
33 | {
34 | self::assertSame(10000, $this->level->toInt());
35 | }
36 |
37 | public function testSerialize(): void
38 | {
39 | self::assertSame('error', $this->level->serialize());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Payload/TelemetryBodyTest.php:
--------------------------------------------------------------------------------
1 | message);
28 | self::assertSame('method', $body->method);
29 | self::assertSame('url', $body->url);
30 | self::assertSame('status', $body->status_code);
31 | self::assertSame('sub', $body->subtype);
32 | self::assertSame('stack', $body->stack);
33 | self::assertSame('from', $body->from);
34 | self::assertSame('to', $body->to);
35 | self::assertSame(42, $body->start_timestamp_ms);
36 | self::assertSame(43, $body->end_timestamp_ms);
37 | self::assertSame([
38 | 'extraOne' => 'foo',
39 | 'extraTwo' => 'bar',
40 | ], $body->extra);
41 |
42 | // Assert array order does not matter.
43 | $body = new TelemetryBody(...[
44 | 'message' => 'message',
45 | 'extraOne' => 'foo',
46 | 'stack' => 'stack',
47 | ]);
48 |
49 | self::assertSame('message', $body->message);
50 | self::assertSame('foo', $body->extra['extraOne']);
51 | self::assertSame('stack', $body->stack);
52 | }
53 |
54 | public function testSerialize(): void
55 | {
56 | $body = new TelemetryBody(
57 | message: 'message',
58 | method: 'method',
59 | url: 'url',
60 | status_code: 'status',
61 | subtype: 'sub',
62 | stack: 'stack',
63 | from: 'from',
64 | to: 'to',
65 | start_timestamp_ms: 42,
66 | end_timestamp_ms: 43,
67 | extraOne: 'foo',
68 | extraTwo: 'bar',
69 | );
70 |
71 | self::assertSame([
72 | 'message' => 'message',
73 | 'method' => 'method',
74 | 'url' => 'url',
75 | 'status_code' => 'status',
76 | 'subtype' => 'sub',
77 | 'stack' => 'stack',
78 | 'from' => 'from',
79 | 'to' => 'to',
80 | 'start_timestamp_ms' => 42,
81 | 'end_timestamp_ms' => 43,
82 | 'extraOne' => 'foo',
83 | 'extraTwo' => 'bar',
84 | ], $body->serialize());
85 | }
86 |
87 | public function testEmptyProperties(): void
88 | {
89 | $body = new TelemetryBody();
90 | self::assertEmpty($body->serialize());
91 | }
92 |
93 | public function testExtraDoesNotOverrideProperty(): void
94 | {
95 | $body = new TelemetryBody(message: 'foo');
96 | $body->extra['message'] = 'bar';
97 |
98 | self::assertSame(['message' => 'foo'], $body->serialize());
99 | }
100 |
101 | /**
102 | * @since 4.1.1
103 | */
104 | public function testFromArray(): void
105 | {
106 | $body = TelemetryBody::fromArray([
107 | 'message' => 'message',
108 | 'method' => 'method',
109 | 'url' => 'url',
110 | 'status_code' => 'status',
111 | 'subtype' => 'sub',
112 | 'stack' => 'stack',
113 | 'from' => 'from',
114 | 'to' => 'to',
115 | 'start_timestamp_ms' => 42,
116 | 'end_timestamp_ms' => 43,
117 | 'extraOne' => 'foo',
118 | 'extraTwo' => 'bar',
119 | ]);
120 |
121 | self::assertSame('message', $body->message);
122 | self::assertSame('method', $body->method);
123 | self::assertSame('url', $body->url);
124 | self::assertSame('status', $body->status_code);
125 | self::assertSame('sub', $body->subtype);
126 | self::assertSame('stack', $body->stack);
127 | self::assertSame('from', $body->from);
128 | self::assertSame('to', $body->to);
129 | self::assertSame(42, $body->start_timestamp_ms);
130 | self::assertSame(43, $body->end_timestamp_ms);
131 | self::assertSame([
132 | 'extraOne' => 'foo',
133 | 'extraTwo' => 'bar',
134 | ], $body->extra);
135 | }
136 |
137 | /**
138 | * @since 4.1.1
139 | */
140 | public function testFromArrayNested(): void
141 | {
142 | // Assert that nested/non-standard arrays don't throw an error.
143 | $body = TelemetryBody::fromArray([["data" => "some data"]]);
144 | self::assertSame([["data" => "some data"]], $body->extra);
145 |
146 | // Assert that positional arguments are not passed to named arguments on the constructor.
147 | $body = TelemetryBody::fromArray(["0" => ["id" => "some id"], 'message' => 'test message']);
148 | self::assertSame('test message', $body->message);
149 | self::assertSame(["0" => ["id" => "some id"]], $body->extra);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/tests/Payload/TelemetryEventTest.php:
--------------------------------------------------------------------------------
1 | 'foo']);
17 | $after = floor(microtime(true) * 1000);
18 |
19 | self::assertNotNull($event->timestamp);
20 | self::assertGreaterThanOrEqual($before, $event->timestamp);
21 | self::assertLessThanOrEqual($after, $event->timestamp);
22 | }
23 |
24 | /**
25 | * @since 4.1.1
26 | */
27 | public function testNestedArrayBody(): void
28 | {
29 | $event = new TelemetryEvent(EventType::Network, EventLevel::Info, [
30 | 'method' => 'GET',
31 | 'url' => 'https://example.com',
32 | 'status_code' => '200',
33 | [
34 | 'unstructured' => 'data',
35 | 0 => 'foo',
36 | ],
37 | ]);
38 |
39 | self::assertSame('GET', $event->body->method);
40 | self::assertSame('https://example.com', $event->body->url);
41 | self::assertSame('200', $event->body->status_code);
42 | self::assertSame([
43 | [
44 | 'unstructured' => 'data',
45 | 0 => 'foo',
46 | ],
47 | ], $event->body->extra);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/PayloadTest.php:
--------------------------------------------------------------------------------
1 | shouldReceive('getAccessToken')
17 | ->andReturn('012345678901234567890123456789ab')
18 | ->mock();
19 |
20 | $payload = new Payload($data, $config->getAccessToken());
21 |
22 | $this->assertEquals($data, $payload->getData());
23 |
24 | $data2 = m::mock(Data::class);
25 | $this->assertEquals($data2, $payload->setData($data2)->getData());
26 | }
27 |
28 | public function testPayloadAccessToken(): void
29 | {
30 | $accessToken = "012345678901234567890123456789ab";
31 | $data = m::mock(Data::class);
32 | $config = m::mock(Config::class)
33 | ->shouldReceive('getAccessToken')
34 | ->andReturn($accessToken)
35 | ->mock();
36 |
37 | $payload = new Payload($data, $accessToken);
38 | $this->assertEquals($accessToken, $payload->getAccessToken());
39 |
40 | $accessToken = "012345678901234567890123456789ab";
41 | $config = m::mock(Config::class)
42 | ->shouldReceive('getAccessToken')
43 | ->andReturn($accessToken)
44 | ->mock();
45 | $payload = new Payload($data, $accessToken);
46 | $this->assertEquals($accessToken, $payload->getAccessToken());
47 |
48 | $at2 = "ab012345678901234567890123456789";
49 | $this->assertEquals($at2, $payload->setAccessToken($at2)->getAccessToken());
50 | }
51 |
52 | public function testEncode(): void
53 | {
54 | $accessToken = $this->getTestAccessToken();
55 | $data = m::mock('Rollbar\Payload\Data, Rollbar\SerializerInterface')
56 | ->shouldReceive('serialize')
57 | ->andReturn(new \ArrayObject())
58 | ->mock();
59 | m::mock(DataBuilder::class)
60 | ->shouldReceive('getScrubFields')
61 | ->andReturn(array())
62 | ->shouldReceive('scrub')
63 | ->andReturn(new \ArrayObject())
64 | ->mock();
65 |
66 | $payload = new Payload($data, $accessToken);
67 | $encoded = json_encode($payload->serialize());
68 | $json = '{"data":{},"access_token":"'.$accessToken.'"}';
69 | $this->assertEquals($json, $encoded);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/Performance/MassivePayload.php:
--------------------------------------------------------------------------------
1 | generateStringData();
16 |
17 | $data = $framesTest->executeProvider();
18 | $data = $data['truncate middle using trace key'][0];
19 | foreach ($data['data']['body']['trace']['frames'] as $i => $frame) {
20 | $data['data']['body']['trace']['frames'][$i] = $stringData;
21 | }
22 |
23 | return $data;
24 | }
25 |
26 | public function generateStringData(): array
27 | {
28 | $stringsTest = new StringsStrategyTest();
29 |
30 | $data = $stringsTest->executeProvider();
31 |
32 | $thresholds = StringsStrategy::getThresholds();
33 | $biggestThreshold = $thresholds[0];
34 |
35 | $data = $data['truncate strings to ' . $biggestThreshold][0];
36 |
37 | return $data['data']['body']['message']['body']['value'];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Performance/TestHelpers/EncodedPayload.php:
--------------------------------------------------------------------------------
1 | strategiesUsed = array();
15 |
16 | $this->payloadSize = $payload->size();
17 |
18 | $memUsageBefore = memory_get_peak_usage(true);
19 | $timeBefore = microtime(true) * 1000;
20 |
21 | \Rollbar\Performance\TestHelpers\EncodedPayload::resetEncodingCount();
22 |
23 | $result = parent::truncate($payload);
24 |
25 | $timeAfter = microtime(true) * 1000;
26 | $memUsageAfter = memory_get_peak_usage(true);
27 |
28 | $this->memoryUsage = $memUsageAfter - $memUsageBefore;
29 | $this->timeUsage = $timeAfter - $timeBefore;
30 | $this->strategiesUsed = array_unique($this->strategiesUsed);
31 |
32 | $this->lastRunOutput = $this->composeLastRunOutput();
33 |
34 | return $result;
35 | }
36 |
37 | public function getLastRun(): string
38 | {
39 | return $this->lastRunOutput;
40 | }
41 |
42 | public function composeLastRunOutput(): string
43 | {
44 | $output = "\n";
45 |
46 | $output .= "Payload size: " .
47 | $this->payloadSize . " bytes = " .
48 | round($this->payloadSize / 1024 / 1024, 2) . " MB \n";
49 |
50 | $output .= "Strategies used: \n" .
51 | (count($this->strategiesUsed) ? implode(", \n", $this->strategiesUsed) : "none") . "\n";
52 |
53 | $output .= "Encoding triggered: " .
54 | \Rollbar\Performance\TestHelpers\EncodedPayload::getEncodingCount() . "\n";
55 |
56 | $output .= "Memory usage: " .
57 | $this->memoryUsage . " bytes = " .
58 | round($this->memoryUsage / 1024 / 1024, 2) . " MB\n";
59 |
60 | $output .= "Execution time: " . $this->timeUsage . " ms\n";
61 |
62 | return $output;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/PersonTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($id, $person->getId());
13 |
14 | $id2 = "RollbarPHP";
15 | $this->assertEquals($id2, $person->setId($id2)->getId());
16 | }
17 |
18 | public function testUsername(): void
19 | {
20 | $username = "user@rollbar.com";
21 | $person = new Person("15", $username);
22 | $this->assertEquals($username, $person->getUsername());
23 |
24 | $username2 = "user-492";
25 | $this->assertEquals($username2, $person->setUsername($username2)->getUsername());
26 | }
27 |
28 | public function testEmail(): void
29 | {
30 | $email = "1.0.0";
31 | $person = new Person("Rollbar_Master", null, $email);
32 | $this->assertEquals($email, $person->getEmail());
33 |
34 | $email2 = "1.0.1";
35 | $this->assertEquals($email2, $person->setEmail($email2)->getEmail());
36 | }
37 |
38 | public function testExtra(): void
39 | {
40 | $person = new Person("42");
41 | $person->test = "testing";
42 | $this->assertEquals("testing", $person->test);
43 | }
44 |
45 | public function testExtraId(): void
46 | {
47 | $person = new Person('42', extra: ['id' => 'testing']);
48 | $this->assertEquals('42', $person->getId());
49 | $this->assertEquals(array('id' => '42'), $person->serialize());
50 | }
51 |
52 | public function testEncode(): void
53 | {
54 | $person = new Person("1024");
55 | $person->setUsername("username")
56 | ->setEmail("user@gmail.com");
57 | $person->Settings = array(
58 | "send_email" => true
59 | );
60 | $encoded = json_encode($person->serialize());
61 | $expected ='{"id":"1024","username":"username","email":"user@gmail.com","Settings":{"send_email":true}}';
62 | $this->assertEquals($expected, $encoded);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/ReadmeTest.php:
--------------------------------------------------------------------------------
1 | $this->getTestAccessToken(),
22 | 'environment' => 'production'
23 | )
24 | );
25 |
26 | try {
27 | throw new \Exception('test exception');
28 | } catch (\Exception $e) {
29 | Rollbar::log(Level::ERROR, $e);
30 | }
31 |
32 | // Message at level 'info'
33 | Rollbar::log(Level::INFO, 'testing info level');
34 |
35 | // With extra data (3rd arg) and custom payload options (4th arg)
36 | Rollbar::log(
37 | Level::INFO,
38 | 'testing extra data',
39 | array("some_key" => "some value") // key-value additional data
40 | );
41 |
42 | // If you want to check if logging with Rollbar was successful
43 | $response = Rollbar::report(LevelFactory::fromName(Level::INFO), 'testing wasSuccessful()');
44 | if (!$response->wasSuccessful()) {
45 | throw new \Exception('logging with Rollbar failed');
46 | }
47 |
48 | // raises an E_NOTICE which will *not* be reported by the error handler
49 | // $foo = $bar;
50 |
51 | // will be reported by the exception handler
52 | $this->expectException(\Exception::class);
53 | throw new \Exception('testing exception handler');
54 | }
55 |
56 | public function testSetup1(): void
57 | {
58 | $config = array(
59 | // required
60 | 'access_token' => $this->getTestAccessToken(),
61 | // optional - environment name. any string will do.
62 | 'environment' => 'production',
63 | // optional - path to directory your code is in. used for linking stack traces.
64 | 'root' => '/Users/brian/www/myapp'
65 | );
66 | Rollbar::init($config);
67 |
68 | $this->assertTrue(true);
69 | }
70 |
71 | public function testSetup2(): void
72 | {
73 | $config = array(
74 | // required
75 | 'access_token' => $this->getTestAccessToken(),
76 | // optional - environment name. any string will do.
77 | 'environment' => 'production',
78 | // optional - path to directory your code is in. used for linking stack traces.
79 | 'root' => '/Users/brian/www/myapp'
80 | );
81 |
82 | $set_exception_handler = false;
83 | $set_error_handler = false;
84 | Rollbar::init($config, $set_exception_handler, $set_error_handler);
85 |
86 | $this->assertTrue(true);
87 | }
88 |
89 | public function testBasicUsage(): void
90 | {
91 | Rollbar::init(
92 | array(
93 | 'access_token' => $this->getTestAccessToken(),
94 | 'environment' => 'test'
95 | )
96 | );
97 |
98 | try {
99 | do_something();
100 | } catch (\Exception $e) {
101 | $result1 = Rollbar::report(Level::ERROR, $e);
102 | // or
103 | $result2 = Rollbar::report(Level::ERROR, $e, array("my" => "extra", "data" => 42));
104 | }
105 |
106 | $this->assertEquals(200, $result1->getStatus());
107 | $this->assertEquals(200, $result2->getStatus());
108 | }
109 |
110 | public function testBasicUsage2(): void
111 | {
112 | Rollbar::init(
113 | array(
114 | 'access_token' => $this->getTestAccessToken(),
115 | 'environment' => 'test'
116 | )
117 | );
118 |
119 | $result1 = Rollbar::report(Level::WARNING, 'could not connect to mysql server');
120 | $result2 = Rollbar::report(
121 | Level::INFO,
122 | 'Here is a message with some additional data',
123 | array('x' => 10, 'code' => 'blue')
124 | );
125 |
126 | $this->assertEquals(200, $result1->getStatus());
127 | $this->assertEquals(200, $result2->getStatus());
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/tests/ResponseTest.php:
--------------------------------------------------------------------------------
1 | 5));
10 | $this->assertEquals(200, $r->getStatus());
11 | }
12 |
13 | public function testInfo(): void
14 | {
15 | $r = new Response(200, "FAKE INFO");
16 | $this->assertEquals("FAKE INFO", $r->getInfo());
17 | }
18 |
19 | public function testUuid(): void
20 | {
21 | $r = new Response(200, "FAKE INFO", "abc123");
22 | $this->assertEquals("abc123", $r->getUuid());
23 | }
24 |
25 | public function testWasSuccessful(): void
26 | {
27 | $response = new Response(200, null);
28 | $this->assertTrue($response->wasSuccessful());
29 | $response = new Response(199, null);
30 | $this->assertFalse($response->wasSuccessful());
31 | $response = new Response(300, null);
32 | $this->assertFalse($response->wasSuccessful());
33 | }
34 |
35 | /**
36 | * @testWith ["abc123", "https://rollbar.com/occurrence/uuid/?uuid=abc123"]
37 | * ["a bar", "https://rollbar.com/occurrence/uuid/?uuid=a+bar"]
38 | */
39 | public function testUrl(string $uuid, string $expectedOccurrenceUrl): void
40 | {
41 | $response = new Response(200, "fake", $uuid);
42 | $this->assertEquals($expectedOccurrenceUrl, $response->getOccurrenceUrl());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/ServerTest.php:
--------------------------------------------------------------------------------
1 | setHost($val);
12 | $this->assertEquals($val, $server->getHost());
13 |
14 | $val2 = "TEST2";
15 | $this->assertEquals($val2, $server->setHost($val2)->getHost());
16 | }
17 |
18 | public function testRoot(): void
19 | {
20 | $val = "TEST";
21 | $server = new Server();
22 | $server->setRoot($val);
23 | $this->assertEquals($val, $server->getRoot());
24 |
25 | $val2 = "TEST2";
26 | $this->assertEquals($val2, $server->setRoot($val2)->getRoot());
27 | }
28 |
29 | public function testBranch(): void
30 | {
31 | $val = "TEST";
32 | $server = new Server();
33 | $server->setBranch($val);
34 | $this->assertEquals($val, $server->getBranch());
35 |
36 | $val2 = "TEST2";
37 | $this->assertEquals($val2, $server->setBranch($val2)->getBranch());
38 | }
39 |
40 | public function testCodeVersion(): void
41 | {
42 | $val = "TEST";
43 | $server = new Server();
44 | $server->setCodeVersion($val);
45 | $this->assertEquals($val, $server->getCodeVersion());
46 |
47 | $val2 = "TEST2";
48 | $this->assertEquals($val2, $server->setCodeVersion($val2)->getCodeVersion());
49 | }
50 |
51 | public function testExtra(): void
52 | {
53 | $server = new Server();
54 | $server->setExtras(array("test" => "testing"));
55 | $extras = $server->getExtras();
56 | $this->assertEquals("testing", $extras["test"]);
57 | }
58 |
59 | public function testEncode(): void
60 | {
61 | $server = new Server();
62 | $server->setHost("server2-ec-us")
63 | ->setRoot("/home/app/testingRollbar")
64 | ->setBranch("master")
65 | ->setCodeVersion("#dca015");
66 | $extras = array("test" => array(1, 2, "3", array()));
67 | $server->setExtras($extras);
68 | $expected = '{' .
69 | '"host":"server2-ec-us",' .
70 | '"root":"\\/home\\/app\\/testingRollbar",' .
71 | '"branch":"master",' .
72 | '"code_version":"#dca015",' .
73 | '"test":' .
74 | '[' .
75 | '1,' .
76 | '2,' .
77 | '"3",' .
78 | '[]' .
79 | ']' .
80 | '}';
81 | $this->assertEquals($expected, json_encode($server->serialize()));
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/tests/TestHelpers/ArrayLogger.php:
--------------------------------------------------------------------------------
1 | logs[] = self::stringify($level, $message);
36 | }
37 |
38 | /**
39 | * Returns the number of instances of a log in the logs.
40 | *
41 | * @param $level
42 | * @param Stringable|string $message
43 | *
44 | * @return int
45 | */
46 | public function count($level, Stringable|string $message): int
47 | {
48 | $count = 0;
49 | $str = self::stringify($level, $message);
50 | foreach ($this->logs as $log) {
51 | if ($str === $log) {
52 | $count++;
53 | }
54 | }
55 | return $count;
56 | }
57 |
58 | /**
59 | * Returns the index of the first instance of a matching log in the logs. Returns -1 if no matching log is found.
60 | *
61 | * @param string $level The log level of the message to search for.
62 | * @param Stringable|string $message The message body to search for.
63 | *
64 | * @return int
65 | */
66 | public function indexOf(string $level, Stringable|string $message): int
67 | {
68 | $str = self::stringify($level, $message);
69 | foreach ($this->logs as $index => $log) {
70 | if ($str === $log) {
71 | return $index;
72 | }
73 | }
74 | return -1;
75 | }
76 |
77 | /**
78 | * Returns the index of the first instance of a matching level and message pattern in the logs. Returns -1 if no
79 | * matching log is found.
80 | *
81 | * @param string $level The log level of the message to search for. You can pass '.+' to match any level.
82 | * @param string $pattern The regex pattern to search for.
83 | *
84 | * @return int
85 | */
86 | public function indexOfRegex(string $level, string $pattern): int
87 | {
88 | $pattern = '/\[' . $level . '\].*' . $pattern . '/';
89 | foreach ($this->logs as $index => $log) {
90 | if (preg_match($pattern, $log)) {
91 | return $index;
92 | }
93 | }
94 | return -1;
95 | }
96 |
97 | /**
98 | * Checks the log at the given index to see if it matches the given level and message pattern. Returns true if it
99 | * matches, false otherwise.
100 | *
101 | * @param int $index The index of the log to check.
102 | * @param string $level The log level of the message to search for. You can pass '.+' to match any level.
103 | * @param string $pattern The regex pattern to search for.
104 | *
105 | * @return bool
106 | */
107 | public function indexMatchesRegex(int $index, string $level, string $pattern): bool
108 | {
109 | if ($index < 0 || $index >= count($this->logs)) {
110 | return false;
111 | }
112 | $log = $this->logs[$index];
113 | $pattern = '/\[' . $level . '\].*' . $pattern . '/';
114 | if (preg_match($pattern, $log)) {
115 | return true;
116 | }
117 | return false;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/tests/TestHelpers/CustomSerializable.php:
--------------------------------------------------------------------------------
1 | data = $data;
12 | }
13 |
14 | public function serialize(): array
15 | {
16 | throw new \Exception("Not implemented");
17 | }
18 |
19 | public function unserialize(string $data): void
20 | {
21 | throw new \Exception("Not implemented");
22 | }
23 |
24 | public function __serialize(): array
25 | {
26 | return $this->data;
27 | }
28 |
29 | public function __unserialize(array $data): void
30 | {
31 | throw new \Exception("Not implemented");
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/TestHelpers/CustomTruncation.php:
--------------------------------------------------------------------------------
1 | encode(array(
13 | "data" => array(
14 | "body" => array(
15 | "message" => array(
16 | "body" => array(
17 | "value" => 'Custom truncation test string'
18 | )
19 | )
20 | )
21 | )
22 | ));
23 |
24 | return $payload;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/TestHelpers/CycleCheck/ChildCycleCheck.php:
--------------------------------------------------------------------------------
1 | parent = $parent;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tests/TestHelpers/CycleCheck/ChildCycleCheckSerializable.php:
--------------------------------------------------------------------------------
1 | parent = $parent;
12 | }
13 |
14 | public function serialize(): array
15 | {
16 | return array(
17 | "parent" => \Rollbar\Utilities::serializeForRollbarInternal($this->parent)
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/TestHelpers/CycleCheck/ParentCycleCheck.php:
--------------------------------------------------------------------------------
1 | child = new ChildCycleCheck($this);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tests/TestHelpers/CycleCheck/ParentCycleCheckSerializable.php:
--------------------------------------------------------------------------------
1 | child = new ChildCycleCheck($this);
12 | }
13 |
14 | public function serialize(): array
15 | {
16 | return array(
17 | "child" => \Rollbar\Utilities::serializeForRollbarInternal($this->child)
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/TestHelpers/DeprecatedSerializable.php:
--------------------------------------------------------------------------------
1 | data = $data;
10 | }
11 |
12 | public function serialize()
13 | {
14 | return $this->data;
15 | }
16 |
17 | public function unserialize(string $data)
18 | {
19 | throw new \Exception("Not implemented");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/TestHelpers/Exceptions/FiftyFiftyExceptionSampleRate.php:
--------------------------------------------------------------------------------
1 | makePartial();
17 | $mock->shouldReceive("serialize")->andReturn(array());
18 | $mock->setLevel(\Rollbar\LevelFactory::fromName($level));
19 | $payload->setData($mock);
20 | return $payload;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/TestHelpers/MockPhpStream.php:
--------------------------------------------------------------------------------
1 | length = strlen(self::$data);
34 | }
35 | $length = min($count, self::$length - self::$index);
36 | $data = substr(self::$data, self::$index);
37 | self::$index = self::$index + $length;
38 | return $data;
39 | }
40 |
41 | public function stream_eof(): bool
42 | {
43 | return (self::$index >= self::$length ? true : false);
44 | }
45 |
46 | public function stream_write($data): ?int
47 | {
48 | self::$data = $data;
49 | self::$length = strlen(self::$data);
50 | self::$index = 0;
51 | return self::$length;
52 | }
53 |
54 | // @codingStandardsIgnoreEnd
55 | }
56 |
--------------------------------------------------------------------------------
/tests/TestHelpers/StdOutLogger.php:
--------------------------------------------------------------------------------
1 | config = $config;
25 | }
26 |
27 | /**
28 | * @inheritDoc
29 | */
30 | public function include(TelemetryEvent $event, int $queueSize): bool
31 | {
32 | return $this->includeFunction?->call($this, $event, $queueSize) ?? false;
33 | }
34 |
35 | /**
36 | * @inheritDoc
37 | */
38 | public function includeRollbarItem(
39 | string $level,
40 | Stringable|string $message,
41 | array $context = [],
42 | bool $ignored = false,
43 | ): bool {
44 | return $this->includeRollbarItemFunction?->call($this, $level, $message, $context, $ignored) ?? false;
45 | }
46 |
47 | /**
48 | * @inheritDoc
49 | */
50 | public function filterOnRead(): bool
51 | {
52 | return $this->filterOnRead;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/tests/TraceChainTest.php:
--------------------------------------------------------------------------------
1 | trace1 = m::mock(Trace::class);
17 | $this->trace2 = m::mock(Trace::class);
18 | }
19 |
20 | public function testTraces(): void
21 | {
22 | $chain = array($this->trace1);
23 | $traceChain = new TraceChain($chain);
24 | $this->assertEquals($chain, $traceChain->getTraces());
25 |
26 | $traceChain = new TraceChain($chain);
27 | $chain = array($this->trace1, $this->trace2);
28 | $traceChain->setTraces($chain);
29 | $this->assertEquals($chain, $traceChain->getTraces());
30 | }
31 |
32 | public function testKey(): void
33 | {
34 | $chain = new TraceChain(array($this->trace1));
35 | $this->assertEquals("trace_chain", $chain->getKey());
36 | }
37 |
38 | public function testEncode(): void
39 | {
40 | $trace1 = m::mock(Trace::class)
41 | ->shouldReceive("serialize")
42 | ->andReturn("TRACE1")
43 | ->mock();
44 | $trace2 = m::mock(Trace::class)
45 | ->shouldReceive("serialize")
46 | ->andReturn("TRACE2")
47 | ->mock();
48 | $chain = new TraceChain(array($trace1, $trace2));
49 | $this->assertEquals('["TRACE1","TRACE2"]', json_encode($chain->serialize()));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/TraceTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(array(), $trace->getFrames());
18 | $this->assertEquals($exc, $trace->getException());
19 |
20 | $trace = new Trace($frames, $exc);
21 | $this->assertEquals($frames, $trace->getFrames());
22 | $this->assertEquals($exc, $trace->getException());
23 | }
24 |
25 | public function testFrames(): void
26 | {
27 | $frames = array(
28 | new Frame("one.php"),
29 | new Frame("two.php")
30 | );
31 | $exc = m::mock(ExceptionInfo::class);
32 | $trace = new Trace(array(), $exc);
33 | $this->assertEquals($frames, $trace->setFrames($frames)->getFrames());
34 | }
35 |
36 | public function testException(): void
37 | {
38 | $exc = m::mock(ExceptionInfo::class);
39 | $trace = new Trace(array(), $exc);
40 | $this->assertEquals($exc, $trace->getException());
41 |
42 | $exc2 = m::mock(ExceptionInfo::class);
43 | $this->assertEquals($exc2, $trace->setException($exc2)->getException());
44 | }
45 |
46 | public function testEncode(): void
47 | {
48 | $value = m::mock("Rollbar\Payload\ExceptionInfo, Rollbar\SerializerInterface")
49 | ->shouldReceive("serialize")
50 | ->andReturn("{EXCEPTION}")
51 | ->mock();
52 | $trace = new Trace(array(), $value);
53 | $encoded = json_encode($trace->serialize());
54 | $this->assertEquals("{\"frames\":[],\"exception\":\"{EXCEPTION}\"}", $encoded);
55 | }
56 |
57 | public function testTraceKey(): void
58 | {
59 | $trace = new Trace(array(), m::mock(ExceptionInfo::class));
60 | $this->assertEquals("trace", $trace->getKey());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/Truncation/FramesStrategyTest.php:
--------------------------------------------------------------------------------
1 | $this->getTestAccessToken()));
17 | $truncation = new Truncation($config);
18 |
19 | $strategy = new FramesStrategy($truncation);
20 |
21 | $data = new EncodedPayload($data);
22 | $data->encode();
23 |
24 | $result = $strategy->execute($data);
25 |
26 | $this->assertEquals($expected, $result->data());
27 | }
28 |
29 |
30 | /**
31 | * Also used by {@see TruncationTest::truncateProvider()}.
32 | *
33 | * @return array
34 | */
35 | public static function executeProvider(): array
36 | {
37 | $data = array(
38 | 'nothing to truncate using trace key' => array(
39 | array('data' => array('body' =>
40 | array('trace' => array('frames' => range(1, 6)))
41 | )),
42 | array('data' => array('body' =>
43 | array('trace' => array('frames' => range(1, 6)))
44 | ))
45 | ),
46 | 'nothing to truncate using trace_chain key' => array(
47 | array('data' => array('body' =>
48 | array('trace_chain' => array(array('frames' => range(1, 6))))
49 | )),
50 | array('data' => array('body' =>
51 | array('trace_chain' => array(array('frames' => range(1, 6))))
52 | ))
53 | ),
54 | 'truncate middle using trace key' => array(
55 | array('data' => array('body' =>
56 | array(
57 | 'trace' => array(
58 | 'frames' => range(
59 | 1,
60 | FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
61 | )
62 | )
63 | )
64 | )),
65 | array('data' => array('body' =>
66 | array(
67 | 'trace' => array(
68 | 'frames' => array_merge(
69 | range(1, FramesStrategy::FRAMES_OPTIMIZATION_RANGE),
70 | range(
71 | FramesStrategy::FRAMES_OPTIMIZATION_RANGE + 2,
72 | FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
73 | )
74 | )
75 | )
76 | )
77 | ))
78 |
79 | ),
80 | 'truncate middle using trace_chain key' => array(
81 | array('data' => array('body' =>
82 | array(
83 | 'trace_chain' => array(
84 | array(
85 | 'frames' => range(
86 | 1,
87 | FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
88 | )
89 | )
90 | )
91 | )
92 | )),
93 | array('data' => array('body' =>
94 | array(
95 | 'trace_chain' => array(
96 | array(
97 | 'frames' => array_merge(
98 | range(1, FramesStrategy::FRAMES_OPTIMIZATION_RANGE),
99 | range(
100 | FramesStrategy::FRAMES_OPTIMIZATION_RANGE + 2,
101 | FramesStrategy::FRAMES_OPTIMIZATION_RANGE * 2 + 1
102 | )
103 | )
104 | )
105 | )
106 | )
107 | ))
108 |
109 | )
110 | );
111 |
112 | return $data;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/tests/Truncation/MinBodyStrategyTest.php:
--------------------------------------------------------------------------------
1 | $this->getTestAccessToken()));
18 | $truncation = new Truncation($config);
19 |
20 | $strategy = new MinBodyStrategy($truncation);
21 |
22 | $data = new EncodedPayload($data);
23 | $data->encode();
24 |
25 | $result = $strategy->execute($data);
26 |
27 | $this->assertEquals($expected, $result->data());
28 | }
29 |
30 | public static function executeProvider(): array
31 | {
32 | $data = array();
33 |
34 | $traceData = array(
35 | 'exception' => array(
36 | 'description' => 'Test description',
37 | 'message' => str_repeat(
38 | 'A',
39 | MinBodyStrategy::EXCEPTION_MESSAGE_LIMIT+1
40 | )
41 | ),
42 | 'frames' => array('Frame 1', 'Frame 2', 'Frame 3')
43 | );
44 |
45 | $expected = $traceData;
46 | unset($expected['exception']['description']);
47 | $expected['exception']['message'] = str_repeat(
48 | 'A',
49 | MinBodyStrategy::EXCEPTION_MESSAGE_LIMIT
50 | );
51 | $expected['frames'] = array('Frame 1', 'Frame 3');
52 |
53 |
54 | $data['trace data set'] = array(
55 | self::payloadStructureProvider(array('trace' => $traceData)),
56 | self::payloadStructureProvider(array('trace' => $expected))
57 | );
58 |
59 | $data['trace_chain data set'] = array(
60 | self::payloadStructureProvider(array('trace_chain' => $traceData)),
61 | self::payloadStructureProvider(array('trace_chain' => $expected))
62 | );
63 | return $data;
64 | }
65 |
66 | protected static function payloadStructureProvider($body): array
67 | {
68 | return array(
69 | "data" => array(
70 | "body" => $body
71 | )
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tests/Truncation/RawStrategyTest.php:
--------------------------------------------------------------------------------
1 | 'test data');
14 |
15 | $config = new Config(array('access_token' => $this->getTestAccessToken()));
16 | $truncation = new Truncation($config);
17 |
18 | $strategy = new RawStrategy($truncation);
19 |
20 | $data = new EncodedPayload($payload);
21 | $data->encode();
22 |
23 | $result = $strategy->execute($data);
24 |
25 | $this->assertEquals(
26 | strlen(json_encode($payload)),
27 | $result->size()
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/Truncation/StringsStrategyTest.php:
--------------------------------------------------------------------------------
1 | $this->getTestAccessToken()));
14 | $truncation = new Truncation($config);
15 |
16 | $strategy = new StringsStrategy($truncation);
17 |
18 | $data = new EncodedPayload($data);
19 | $data->encode();
20 |
21 | return $strategy->execute($data)->data();
22 | }
23 |
24 | /**
25 | * @dataProvider executeTruncateNothingProvider
26 | */
27 | public function testExecuteTruncateNothing($data, $expected): void
28 | {
29 | $result = $this->execute($data);
30 | $this->assertEquals($expected, $result);
31 | }
32 |
33 | /**
34 | * Also used by {@see TruncationTest::truncateProvider()}.
35 | *
36 | * @return array
37 | */
38 | public static function executeTruncateNothingProvider(): array
39 | {
40 | $data = array();
41 |
42 | $data["truncate nothing"] = array(
43 | self::payloadStructureProvider(str_repeat("A", 10)),
44 | self::payloadStructureProvider(str_repeat("A", 10))
45 | );
46 |
47 | return $data;
48 | }
49 |
50 | /**
51 | * @dataProvider executeArrayProvider
52 | */
53 | public function testExecuteArray($data, $expectedThreshold): void
54 | {
55 | $result = $this->execute($data);
56 |
57 | foreach ($result['data']['body']['message']['body']['value'] as $key => $toTrim) {
58 | $this->assertTrue(
59 | strlen($toTrim) <= $expectedThreshold,
60 | "The string '$toTrim' was expected to be trimmed to " . $expectedThreshold . " characters. " .
61 | "Actual length: " . strlen($toTrim)
62 | );
63 | }
64 | }
65 |
66 | /**
67 | * Also used by {@see TruncationTest::truncateProvider()}.
68 | *
69 | * @return array
70 | */
71 | public static function executeArrayProvider(): array
72 | {
73 | $data = array();
74 |
75 | $thresholds = StringsStrategy::getThresholds();
76 | foreach ($thresholds as $threshold) {
77 | $data['truncate strings to ' . $threshold] = self::thresholdTestProvider($threshold);
78 | }
79 |
80 | return $data;
81 | }
82 |
83 | public static function thresholdTestProvider($threshold): array
84 | {
85 | $stringLengthToTrim = $threshold*2;
86 |
87 | $payload = self::payloadStructureProvider(array());
88 | $payload['data']['body']['message']['body']['value2'] = array();
89 |
90 | while (strlen(json_encode($payload)) <= Truncation::MAX_PAYLOAD_SIZE) {
91 | $payload['data']['body']['message']['body']['value'] []=
92 | str_repeat('A', $stringLengthToTrim);
93 | $payload['data']['body']['message']['body']['value2'] []=
94 | str_repeat('A', $stringLengthToTrim);
95 | }
96 |
97 | return array($payload, $threshold);
98 | }
99 |
100 | public function executeProvider(): array
101 | {
102 | $data = array();
103 |
104 | $data["truncate nothing"] = array(
105 | self::payloadStructureProvider(str_repeat("A", 10)),
106 | self::payloadStructureProvider(str_repeat("A", 10))
107 | );
108 |
109 | $thresholds = StringsStrategy::getThresholds();
110 | foreach ($thresholds as $threshold) {
111 | $data['truncate strings to ' . $threshold] = self::thresholdTestProvider($threshold);
112 | }
113 |
114 | return $data;
115 | }
116 |
117 | public static function payloadStructureProvider($message): array
118 | {
119 | return array(
120 | "data" => array(
121 | "body" => array(
122 | "message" => array(
123 | "body" => array(
124 | "value" => $message
125 | )
126 | )
127 | )
128 | )
129 | );
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/tests/Truncation/TelemetryStrategyTest.php:
--------------------------------------------------------------------------------
1 | $this->getTestAccessToken(),
17 | 'environment' => 'test',
18 | ]);
19 | }
20 |
21 | /**
22 | * @dataProvider executeProvider
23 | */
24 | public function testExecute(array $data, array $expected): void
25 | {
26 | $config = new Config(['access_token' => $this->getTestAccessToken()]);
27 | $truncation = new Truncation($config);
28 |
29 | $strategy = new TelemetryStrategy($truncation);
30 |
31 | $data = new EncodedPayload($data);
32 | $data->encode();
33 |
34 | $result = $strategy->execute($data);
35 |
36 | $this->assertEquals($expected, $result->data());
37 | }
38 |
39 | /**
40 | * @return array
41 | */
42 | public static function executeProvider(): array
43 | {
44 | return [
45 | 'nothing to truncate: no telemetry data' => [
46 | [
47 | 'data' => [
48 | 'body' => [],
49 | ],
50 | ],
51 | [
52 | 'data' => [
53 | 'body' => [],
54 | ],
55 | ],
56 | ],
57 | 'nothing to truncate: telemetry in range' => [
58 | [
59 | 'data' => [
60 | 'body' => [
61 | 'telemetry' => range(1, 6),
62 | ],
63 | ],
64 | ],
65 | [
66 | 'data' => [
67 | 'body' => [
68 | 'telemetry' => range(1, 6),
69 | ],
70 | ],
71 | ],
72 | ],
73 | 'truncate middle: telemetry too long' => [
74 | [
75 | 'data' => [
76 | 'body' => [
77 | 'telemetry' => range(1, TelemetryStrategy::TELEMETRY_OPTIMIZATION_RANGE * 2 + 1),
78 | ],
79 | ],
80 | ],
81 | [
82 | 'data' => [
83 | 'body' => [
84 | 'telemetry' => array_merge(
85 | range(1, TelemetryStrategy::TELEMETRY_OPTIMIZATION_RANGE),
86 | range(
87 | TelemetryStrategy::TELEMETRY_OPTIMIZATION_RANGE + 2,
88 | TelemetryStrategy::TELEMETRY_OPTIMIZATION_RANGE * 2 + 1
89 | ),
90 | ),
91 | ],
92 | ],
93 | ],
94 | ],
95 | ];
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/tests/Truncation/TruncationTest.php:
--------------------------------------------------------------------------------
1 | $this->getTestAccessToken()));
17 | $this->truncate = new Truncation($config);
18 | }
19 |
20 | public function testCustomTruncation(): void
21 | {
22 | $config = new Config(array(
23 | 'access_token' => $this->getTestAccessToken(),
24 | 'custom_truncation' => CustomTruncation::class
25 | ));
26 | $this->truncate = new Truncation($config);
27 |
28 | $data = new EncodedPayload(array(
29 | "data" => array(
30 | "body" => array(
31 | "message" => array(
32 | "body" => array(
33 | "value" => str_repeat('A', 1000 * 1000)
34 | )
35 | )
36 | )
37 | )
38 | ));
39 | $data->encode();
40 |
41 | $result = $this->truncate->truncate($data);
42 |
43 | $this->assertStringContainsString('Custom truncation test string', $result);
44 | }
45 |
46 | /**
47 | * @dataProvider truncateProvider
48 | */
49 | public function testTruncateNoPerformance($data): void
50 | {
51 | $data = new EncodedPayload($data);
52 | $data->encode();
53 |
54 | $result = $this->truncate->truncate($data);
55 |
56 | $size = strlen(json_encode($result));
57 |
58 | $this->assertTrue(
59 | $size <= Truncation::MAX_PAYLOAD_SIZE,
60 | "Truncation failed. Payload size exceeds MAX_PAYLOAD_SIZE."
61 | );
62 | }
63 |
64 | public static function truncateProvider(): array
65 | {
66 | $stringsTest = new StringsStrategyTest("StringsStrategyTest");
67 | $framesTest = new FramesStrategyTest("FramesStrategyTest");
68 |
69 | $framesTestData = $framesTest::executeProvider();
70 |
71 | // Fill up frames with data to go over the allowed payload size limit
72 | $frames = &$framesTestData['truncate middle using trace key'][0]['data']['body']['trace']['frames'];
73 | $stringValue = str_repeat('A', 1024 * 10);
74 | foreach ($frames as $key => $data) {
75 | $frames[$key] = $stringValue;
76 | }
77 |
78 | $frames_body = &$framesTestData['truncate middle using trace_chain key'][0]['data']['body'];
79 | $frames = $frames_body['trace_chain'][0]['frames'];
80 | foreach ($frames as $key => $data) {
81 | $frames[$key] = $stringValue;
82 | }
83 |
84 | $data = array_merge(
85 | $stringsTest::executeTruncateNothingProvider(),
86 | $stringsTest::executeArrayProvider(),
87 | $framesTestData
88 | );
89 |
90 | return $data;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |