├── .travis.yml.dist
├── .travis.yml
├── tests
├── bootstrap.php
└── UrlboxTest.php
├── src
├── Facades
│ └── Urlbox.php
├── UrlboxProvider.php
└── Urlbox.php
├── .github
└── workflows
│ └── ci.yaml
├── phpunit.xml.dist
├── phpunit.xml
├── .gitignore
├── LICENSE
├── composer.json
├── README.md
└── composer.lock
/.travis.yml.dist:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - "5.5"
4 | - "5.4"
5 |
6 | env:
7 | # - DB=mysql
8 |
9 | before_script:
10 | # - composer require --dev aura/sql:1.3
11 | - composer install --dev --no-interaction
12 | # - if [[ "$DB" == "mysql" ]]; then mysql -e "create database IF NOT EXISTS tomkyle_test;" -utravis; fi
13 |
14 | script:
15 | - mkdir -p build/logs
16 | - phpunit
17 |
18 |
19 | after_script:
20 | - php vendor/bin/coveralls -v
21 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - "5.5"
4 | - "5.4"
5 | - "7.0"
6 |
7 | env:
8 | # - DB=mysql
9 |
10 | before_script:
11 | # - composer require --dev aura/sql:1.3
12 | - composer install --dev --no-interaction
13 | # - if [[ "$DB" == "mysql" ]]; then mysql -e "create database IF NOT EXISTS tomkyle_test;" -utravis; fi
14 |
15 | script:
16 | - mkdir -p build/logs
17 | - phpunit
18 |
19 |
20 | after_script:
21 | - php vendor/bin/coveralls -v
22 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | $autoloader = __DIR__ . '/../vendor/autoload.php';
12 | if (!is_readable( $autoloader )) {
13 | die("\nMissing Composer's vendor/autoload.php; run 'composer update' first.\n\n");
14 | }
15 | require $autoloader;
16 |
--------------------------------------------------------------------------------
/src/Facades/Urlbox.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class Urlbox extends Facade
14 | {
15 | /**
16 | * Get the registered name of the component.
17 | *
18 | * @return string
19 | */
20 | protected static function getFacadeAccessor()
21 | {
22 | return 'urlbox';
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: Run Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 |
8 | jobs:
9 | laravel-tests:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout Code
14 | uses: actions/checkout@v3
15 |
16 | - name: Setup PHP
17 | uses: shivammathur/setup-php@v2
18 | with:
19 | php-version: 8.1
20 | coverage: xdebug
21 |
22 | - name: Install Dependencies
23 | run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist
24 |
25 | - name: Execute tests
26 | env:
27 | XDEBUG_MODE: coverage
28 | run: vendor/bin/phpunit
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 | tests/src
11 |
12 |
13 |
14 |
15 |
16 | src
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | src
6 |
7 |
8 | src/Facades/Urlbox.php
9 | src/UrlboxProvider.php
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | tests
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/UrlboxProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton( Urlbox::class, function ( $app ) {
14 | $key = config( 'services.urlbox.key' );
15 | $secret = config( 'services.urlbox.secret' );
16 | $webhook_secret = config( 'services.urlbox.webhook_secret' );
17 |
18 | if ( ! $key || ! $secret ) {
19 | throw new InvalidArgumentException( 'Please ensure you have set values for `services.urlbox.key` and `services.urlbox.secret`' );
20 | }
21 |
22 | return new Urlbox( $key, $secret, $webhook_secret, $app->make( Client::class ) );
23 | } );
24 |
25 | $this->app->alias( Urlbox::class, 'urlbox' );
26 | }
27 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 |
10 | # Packages #
11 | ############
12 | # it's better to unpack these files and commit the raw source
13 | # git has its own built in compression methods
14 | *.7z
15 | *.dmg
16 | *.gz
17 | *.iso
18 | *.jar
19 | *.rar
20 | *.tar
21 | *.zip
22 |
23 | # Logs and databases #
24 | ######################
25 | *.log
26 | *.sql
27 | *.sqlite
28 |
29 | # OS generated files #
30 | ######################
31 | .DS_Store
32 | .DS_Store?
33 | ._*
34 | .Spotlight-V100
35 | .Trashes
36 | ehthumbs.db
37 | Thumbs.db
38 | .LSOverride
39 | Icon
40 | .AppleDouble
41 |
42 | # Project files #
43 | ######################
44 | config.json
45 | var/*
46 | logs/*
47 | composer.phar
48 | /vendor/
49 | .idea
50 |
51 | # Workflow files #
52 | ######################
53 | _schrott
54 | vendor
55 | node_modules
56 | var/*
57 | logs/*
58 | config.json
59 | .htaccess
60 | tests/build
61 | build
62 | #phpunit.xml
63 | .phpunit.result.cache
64 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Carsten Witt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "urlbox/screenshots",
3 | "description": "Use urlbox to easily generate website thumbnail screenshots from a URL",
4 | "homepage": "https://github.com/urlbox/urlbox-php",
5 | "keywords": [
6 | "convert",
7 | "render",
8 | "capture",
9 | "webpage",
10 | "image",
11 | "screenshot",
12 | "provider",
13 | "facade",
14 | "screenshots",
15 | "urlbox",
16 | "browser",
17 | "headless",
18 | "responsive",
19 | "retina",
20 | "phantomjs",
21 | "laravel",
22 | "thumbnail",
23 | "website",
24 | "preview",
25 | "thumbnails"
26 | ],
27 | "license": "MIT",
28 | "authors": [
29 | {
30 | "name": "Chris Roebuck",
31 | "email": "chris@urlbox.io",
32 | "role": "Developer"
33 | }
34 | ],
35 | "require": {
36 | "php": "^7.4|^8.0",
37 | "guzzlehttp/guzzle": "^7.5.0",
38 | "ext-json": "*"
39 | },
40 | "require-dev": {
41 | "phpunit/phpunit": "^9.5.26",
42 | "phpunit/php-code-coverage": "^9.2.18",
43 | "mockery/mockery": "^1.5"
44 | },
45 | "autoload": {
46 | "psr-4": {
47 | "Urlbox\\Screenshots\\": "src/"
48 | }
49 | },
50 | "autoload-dev": {
51 | "psr-4": {
52 | "Urlbox\\Screenshots\\Tests\\": "tests/"
53 | }
54 | },
55 | "extra": {
56 | "laravel": {
57 | "providers": [
58 | "Urlbox\\Screenshots\\UrlboxProvider"
59 | ],
60 | "aliases": {
61 | "Urlbox": "Urlbox\\Screenshots\\Facade\\Urlbox"
62 | }
63 | }
64 | },
65 | "config": {
66 | "allow-plugins": {
67 | "symfony/flex": true
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/Urlbox.php:
--------------------------------------------------------------------------------
1 | ensureIsValidCredentials( $apiKey, $apiSecret );
30 |
31 | $this->apiKey = $apiKey;
32 | $this->apiSecret = $apiSecret;
33 | $this->webhookSecret = $webhookSecret;
34 |
35 | $this->client = $client ?? new Client();
36 | }
37 |
38 | /**
39 | * @param string $apiKey
40 | * @param string $apiSecret
41 | *
42 | * @return void
43 | *
44 | * @throws InvalidArgumentException
45 | */
46 | private function ensureIsValidCredentials( string $apiKey, string $apiSecret )
47 | {
48 | if ( empty( $apiKey ) ) {
49 | throw new InvalidArgumentException( 'Requires an api key - https://www.urlbox.io/dashboard/projects' );
50 | }
51 |
52 | if ( empty( $apiSecret ) ) {
53 | throw new InvalidArgumentException( 'Requires an api secret - https://www.urlbox.io/dashboard/projects' );
54 | }
55 | }
56 |
57 | /**
58 | * @param string $apiKey
59 | * @param string $apiSecret
60 | * @param string|null $webhookSecret
61 | * @param Client|null $client
62 | *
63 | * @return Urlbox
64 | * @throws InvalidArgumentException
65 | */
66 | public static function fromCredentials( string $apiKey, string $apiSecret, ?string $webhookSecret = null, ?Client $client = null ): Urlbox
67 | {
68 | return new self( $apiKey, $apiSecret, $webhookSecret, $client );
69 | }
70 |
71 | /**
72 | * @param array $options
73 | * @param string|null $saveToDiskPath
74 | *
75 | * @return array{renderUrl: string, size: int, localPath: string}
76 | * @throws GuzzleException
77 | */
78 | public function render( array $options, ?string $saveToDiskPath = null ): array
79 | {
80 | $response = $this->makeUrlboxPostRequest( '/render/sync', $options );
81 |
82 | if ( $saveToDiskPath !== null ) {
83 | $imageResponse = $this->client->get( $response['renderUrl'] );
84 | file_put_contents( $saveToDiskPath, $imageResponse->getBody()->getContents() );
85 | $response['localPath'] = $saveToDiskPath;
86 | }
87 |
88 | return $response;
89 | }
90 |
91 | /**
92 | * @param string $endpoint
93 | * @param array $options
94 | *
95 | * @return array{renderUrl: string, size: int}
96 | * @throws GuzzleException
97 | */
98 | private function makeUrlboxPostRequest( string $endpoint, array $options ): array
99 | {
100 | $response = $this->client->post(
101 | $this->baseUrl . $endpoint,
102 | [
103 | RequestOptions::HEADERS => [
104 | 'Authorization' => 'Bearer ' . $this->apiSecret,
105 | ],
106 | RequestOptions::JSON => $options
107 | ]
108 | );
109 |
110 | return json_decode( $response->getBody()->getContents(), true );
111 | }
112 |
113 | /**
114 | * @param array $options
115 | *
116 | * @return array{status: string, renderId: string, statusUrl: string}
117 | * @throws GuzzleException
118 | */
119 | public function renderAsync( array $options ): array
120 | {
121 | return $this->makeUrlboxPostRequest( '/render', $options );
122 | }
123 |
124 | /**
125 | * @param array $options
126 | *
127 | * @return string
128 | */
129 | public function generateSignedUrl( array $options ): string
130 | {
131 | return $this->generateUrl( $options, true );
132 | }
133 |
134 | /**
135 | * @param array $options
136 | * @param bool $signed
137 | *
138 | * @return string
139 | */
140 | private function generateUrl( array $options, bool $signed = true ): string
141 | {
142 | $format = $options['format'] ?? 'png';
143 | unset( $options['format'] );
144 |
145 | $queryStringParts = [];
146 | foreach ( $options as $key => $values ) {
147 | $values = is_array( $values ) ? $values : [ $values ];
148 | foreach ( $values as $value ) {
149 | if ( isset( $value ) ) {
150 | $encodedValue = $this->sanitizeValue( $value );
151 | $queryStringParts[] = "$key=$encodedValue";
152 | }
153 | }
154 | }
155 |
156 | $queryString = implode( "&", $queryStringParts );
157 |
158 | $generatedUrl = $this->baseUrl . '/' . $this->apiKey . '/';
159 |
160 | if ( $signed ) {
161 | $generatedUrl .= hash_hmac( "sha1", $queryString, $this->apiSecret ) . '/';
162 | }
163 |
164 | return $generatedUrl . $format . '?' . $queryString;
165 | }
166 |
167 | /**
168 | * @param array $options
169 | *
170 | * @return string
171 | */
172 | public function generateUnsignedUrl( array $options ): string
173 | {
174 | return $this->generateUrl( $options, false );
175 | }
176 |
177 | /**
178 | * @param mixed $value
179 | *
180 | * @return string|null
181 | */
182 | private function sanitizeValue( $value ): ?string
183 | {
184 | if ( is_string( $value ) ) {
185 | return $this->encodeURIComponent( $value );
186 | }
187 |
188 | return var_export( $value, true );
189 | }
190 |
191 | /**
192 | * @param string $value
193 | *
194 | * @return string
195 | */
196 | private function encodeURIComponent( string $value ): string
197 | {
198 | $revert = array( '%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')' );
199 |
200 | return strtr( rawurlencode( $value ), $revert );
201 | }
202 |
203 | /**
204 | * @param string $header
205 | * @param string $content
206 | *
207 | * @return bool
208 | * @throws Exception
209 | */
210 | public function verifyWebhookSignature( string $header, string $content ): bool
211 | {
212 | if ( empty( $this->webhookSecret ) ) {
213 | throw new Exception( 'Unable to verify signature as Webhook Secret is not set. You can find your webhook secret inside your project\'s settings - https://www.urlbox.io/dashboard/projects' );
214 | }
215 |
216 | if ( empty( $header ) ) {
217 | throw new InvalidArgumentException( 'Unable to verify signature as header is empty. Please ensure you pass the `x-urlbox-signature` from the header of the webhook response' );
218 | }
219 |
220 | $generatedHash = hash_hmac( 'sha256', $this->getTimestamp( $header ) . '.' . $content, $this->webhookSecret );
221 | $requestHash = $this->getSignature( $header );
222 |
223 | return $generatedHash === $requestHash;
224 | }
225 |
226 | /**
227 | * @param string $header
228 | *
229 | * @return string
230 | */
231 | private function getTimestamp( string $header ): string
232 | {
233 | return array_reverse( explode( 't=', explode( ',', $header )[0], 2 ) )[0];
234 | }
235 |
236 | /**
237 | * @param string $header
238 | *
239 | * @return string
240 | */
241 | private function getSignature( string $header ): string
242 | {
243 | return array_reverse( explode( 'sha256=', $header, 2 ) )[0];
244 | }
245 |
246 | }
247 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Urlbox/Screenshots
2 |
3 | ## Capture highly accurate webpage screenshots of any site using Urlbox.io in PHP
4 |
5 | This package uses the [Urlbox.io](https://urlbox.io) screenshot as a service to generate website screenshots.
6 |
7 | ## Installation
8 |
9 | This package can be installed through Composer.
10 |
11 | ```bash
12 | composer require urlbox/screenshots
13 | ```
14 |
15 |
16 | ### Laravel Setup
17 |
18 | When using Laravel, if you are using a version pre v5.5 you will need to include the Service Provider manually:
19 |
20 | ```php
21 | // app/config/app.php
22 |
23 | 'providers' => [
24 | // ...
25 | 'Urlbox\Screenshots\UrlboxProvider'
26 | ];
27 | ```
28 |
29 | Setup your API keys:
30 |
31 | ```php
32 | // config/services.php
33 |
34 | 'urlbox' => [
35 | 'key' => env('URLBOX_KEY'),
36 | 'secret' => env('URLBOX_SECRET'),
37 | 'webhook_secret' => env('URLBOX_WEBHOOK_SECRET'),
38 | ];
39 | ```
40 |
41 | and in your .env file:
42 |
43 | ```bash
44 | # URLBOX
45 | URLBOX_KEY=YOUR_URLBOX_KEY
46 | URLBOX_SECRET=YOUR_URLBOX_SECRET
47 | URLBOX_WEBHOOK_SECRET=YOUR_URLBOX_WEBHOOK_SECRET
48 | ```
49 |
50 | You can find your API Credentials and Webhook Secret [here](https://www.urlbox.io/dashboard/projects)
51 |
52 | ## Usage
53 |
54 | You will need a [Urlbox](https://urlbox.io) account to use this package. Please signup [here](https://urlbox.io/pricing) and get your API Key and Secret from the Urlbox dashboard once you have logged in.
55 |
56 | Here is a sample call to generate a Urlbox screenshot URL:
57 |
58 | ```php
59 | use Urlbox\Screenshots\Urlbox;
60 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET' );
61 |
62 | $options = [
63 | // only required option is a url:
64 | 'url' => 'example.com',
65 |
66 | // specify any other options to augment the screenshot...
67 | 'width' => 1280,
68 | 'height' => 1024,
69 | ];
70 |
71 | // Create the Urlbox URL
72 | $urlboxUrl = $urlbox->generateSignedUrl( $options );
73 | // $urlboxUrl is now 'https://api.urlbox.io/v1/API_KEY/TOKEN/png?url=example.com'
74 |
75 | // Generate a screenshot by loading the Urlbox URL in an img tag:
76 | echo '
'
77 | ```
78 |
79 | If you're using Laravel and have set up the service provider, you can use the Facade provided:
80 |
81 | ```php
82 | use Urlbox\Screenshots\Facades\Urlbox;
83 |
84 | $options = [ 'url' => 'example.com' ];
85 | $urlboxUrl = Urlbox::generateUrl( $options );
86 | // $urlboxUrl is now 'https://api.urlbox.io/v1/API_KEY/TOKEN/png?url=example.com'
87 | ```
88 |
89 | You can now use the result (`$urlboxUrl`) by placing it inside an `
` tag as the `src` parameter.
90 |
91 | When you load the image, a screenshot of example.com will be returned.
92 |
93 | ## Options
94 |
95 | You can find the full list of available options within the [docs](https://urlbox.io/docs/options). Below is a list of the most commonly used options:
96 |
97 | | Option | Default | Description |
98 | |-----------------|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
99 | | `url` | - | The URL of the website you want to screenshot **(Required)** |
100 | | `width` | 1280 | Viewport width of the browser in pixels |
101 | | `height` | 1024 | Viewport height of the browser in pixels |
102 | | `selector` | - | Take a screenshot of the element that matches this selector |
103 | | `thumb_width` | - | Width in pixels of the generated thumbnail, leave off for full-size screenshot |
104 | | `format` | png | Image format of the resulting screenshot image (`png` or `jpg`) |
105 | | `user_agent` | - | User-Agent string used to emulate a particular client. |
106 | | `cookie` | - | Set a cookie value before taking a screenshot. E.g. OptIn=true. Can be set multiple times to set more than one cookie. |
107 | | `delay` | - | Amount of time to wait in milliseconds before urlbox takes the screenshot |
108 | | `wait_for` | - | Waits for the element specified by this selector to be visible on the page before taking a screenshot |
109 | | `timeout` | 30000 | Amount of time to wait in milliseconds for the website at url to respond |
110 | | `full_page` | false | Specify whether to capture the full-length of the website |
111 | | `flash` | false | Enable the flash plugin for flash using websites |
112 | | `force` | false | Take a fresh screenshot instead of getting a cached version |
113 | | `ttl` | 2592000 (30 days) | Short for 'time to live'. Number of seconds to keep a screenshot in the cache. Note the default is also the maximum value for this option. |
114 | | `quality` | 80 | JPEG only - image quality of resulting screenshot (0-100) |
115 | | `disable_js` | false | Turn off javascript on target url to prevent popups |
116 | | `retina` | false | Take a 'retina' or high definition screenshot equivalent to setting a device pixel ratio of 2.0 or @2x. Please note that retina screenshots will be double the normal dimensions and will normally take slightly longer to process due to the much bigger image size. |
117 | | `click` | - | Element selector that is clicked before taking a screenshot e.g. #clickme would click the element with id="clickme" |
118 | | `hover` | - | Element selector that is hovered before taking a screenshot e.g. #hoverme would hover over the element with id="hoverme" |
119 | | `bg_color` | - | Hex code or css color string. Some websites don't set a body background colour, and will show up as transparent backgrounds with PNG or black when using JPG. Use this setting to set a background colour. If the website explicitly sets a transparent background on the html or body elements, this setting will be overridden. |
120 | | `crop_width` | - | Crop the width of the screenshot to this size in pixels |
121 | | `hide_selector` | - | Hides all elements that match the element selector by setting their style to `display:none !important;`. Useful for hiding popups. |
122 | | `highlight` | - | Word to highlight on the page before capturing a screenshot |
123 | | `highlightfg` | white | Text color of the highlighted word |
124 | | `highlightbg` | red | Background color of the highlighted word |
125 | | `use_s3` | false | Save the screenshot directly to the S3 bucket configured on your account |
126 | | `webhook_url` | - | Urlbox will send a POST request back to the supplied URL with data about the screenshot in JSON format once it has completed rendering. |
127 |
128 | ***
129 |
130 | ## Other implementations
131 |
132 | * [Node.js](https://github.com/urlbox-io/urlbox-screenshots-node)
133 |
134 | ## Contributing
135 |
136 | We are open to pull requests.
137 |
138 | ## Security
139 |
140 | If you discover any security related issues, please email services@urlbox.io instead of using the issue tracker.
141 |
142 | ## About Urlbox
143 |
144 | Urlbox is a premium Screenshot as a Service API. It lets you render highly accurate screenshots of webpages and display them anywhere you like. [Urlbox.io](https://urlbox.io).
145 |
146 | ## License
147 |
148 | The MIT License (MIT).
149 |
--------------------------------------------------------------------------------
/tests/UrlboxTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(
16 | Urlbox::class,
17 | Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) )
18 | );
19 | }
20 |
21 | public function testConstructorThrowsInvalidArgumentExceptionWhenApiKeyIsEmpty()
22 | {
23 | $this->expectException( InvalidArgumentException::class );
24 | new Urlbox( '', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
25 | }
26 |
27 | public function testConstructorThrowsInvalidArgumentExceptionWhenApiSecretIsEmpty()
28 | {
29 | $this->expectException( InvalidArgumentException::class );
30 | new Urlbox( 'API_KEY', '', Mockery::mock( Client::class ) );
31 | }
32 |
33 | public function testFromCredentialsReturnsUrlboxInstanceWhenValidCredentialsSupplied()
34 | {
35 | $this->assertInstanceOf(
36 | Urlbox::class,
37 | Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) )
38 | );
39 | }
40 |
41 | public function testFromCredentialsThrowsInvalidArgumentExceptionWhenApiKeyIsEmpty()
42 | {
43 | $this->expectException( InvalidArgumentException::class );
44 | Urlbox::fromCredentials( '', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
45 | }
46 |
47 | public function testFromCredentialsThrowsInvalidArgumentExceptionWhenApiSecretIsEmpty()
48 | {
49 | $this->expectException( InvalidArgumentException::class );
50 | Urlbox::fromCredentials( 'API_KEY', '', Mockery::mock( Client::class ) );
51 | }
52 |
53 | public function testRenderReturnsJsonArrayOfApiResponse()
54 | {
55 | $guzzleMock = Mockery::mock( Client::class )
56 | ->shouldReceive( 'post' )
57 | ->with( Mockery::capture( $url ), Mockery::capture( $requestOptions ) )
58 | ->andReturn( $this->getMockedGuzzleResponse( '{"renderUrl":"http://storage.foobar.com/urlbox/renders/123456.png","size":525949}' ) )
59 | ->getMock();
60 |
61 | $urlbox = new Urlbox( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', $guzzleMock );
62 |
63 | $response = $urlbox->render( [
64 | 'url' => 'https://example.com',
65 | 'format' => 'png'
66 | ] );
67 |
68 | $this->assertEquals(
69 | [ 'renderUrl' => 'http://storage.foobar.com/urlbox/renders/123456.png', 'size' => 525949 ],
70 | $response
71 | );
72 | $this->assertEquals( 'https://api.urlbox.io/v1/render/sync', $url );
73 | $this->assertEquals( [
74 | 'headers' => [
75 | 'Authorization' => 'Bearer API_SECRET',
76 | ],
77 | 'json' => [
78 | 'url' => 'https://example.com',
79 | 'format' => 'png'
80 | ]
81 | ], $requestOptions );
82 | }
83 |
84 | private function getMockedGuzzleResponse( string $content ): MockInterface
85 | {
86 | return Mockery::mock( GuzzleHttp\Psr7\Response::class )
87 | ->shouldReceive( 'getBody' )
88 | ->andReturn(
89 | Mockery::mock( GuzzleHttp\Psr7\Stream::class )
90 | ->shouldReceive( 'getContents' )
91 | ->andReturn( $content )
92 | ->getMock()
93 | )
94 | ->getMock();
95 | }
96 |
97 | public function testRenderSavesToDiskAndReturnsJsonArrayOfApiResponseWithLocalPath()
98 | {
99 | $guzzleMock = Mockery::mock( Client::class )
100 | ->shouldReceive( 'post' )
101 | ->with( Mockery::capture( $postUrl ), Mockery::capture( $postRequestOptions ) )
102 | ->andReturn( $this->getMockedGuzzleResponse( '{"renderUrl":"http://storage.foobar.com/urlbox/renders/123456.png","size":525949}' ) )
103 | ->shouldReceive( 'get' )
104 | ->with( Mockery::capture( $getUrl ) )
105 | ->andReturn( $this->getMockedGuzzleResponse( 'foo bar' ) )
106 | ->getMock();
107 |
108 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', $guzzleMock );
109 |
110 | $filename = tempnam( '/tmp', 'URLBOX' );
111 |
112 | $result = $urlbox->render( [
113 | 'url' => 'https://example.com',
114 | 'format' => 'png'
115 | ], $filename );
116 |
117 | $this->assertEquals(
118 | [
119 | 'renderUrl' => 'http://storage.foobar.com/urlbox/renders/123456.png',
120 | 'size' => 525949,
121 | 'localPath' => $filename
122 | ],
123 | $result
124 | );
125 |
126 | $this->assertEquals( 'https://api.urlbox.io/v1/render/sync', $postUrl );
127 | $this->assertEquals( [
128 | 'headers' => [
129 | 'Authorization' => 'Bearer API_SECRET',
130 | ],
131 | 'json' => [
132 | 'url' => 'https://example.com',
133 | 'format' => 'png'
134 | ]
135 | ], $postRequestOptions );
136 | $this->assertEquals( 'http://storage.foobar.com/urlbox/renders/123456.png', $getUrl );
137 |
138 | $this->assertEquals(
139 | 'foo bar',
140 | file_get_contents( $filename )
141 | );
142 | }
143 |
144 | public function testRenderSyncReturnsJsonArrayOfApiResponse()
145 | {
146 | $guzzleMock = Mockery::mock( Client::class )
147 | ->shouldReceive( 'post' )
148 | ->with( Mockery::capture( $url ), Mockery::capture( $requestOptions ) )
149 | ->andReturn( $this->getMockedGuzzleResponse( '{"status":"created","renderId":"00000000-0000-0000-0000-000000000000","statusUrl":"https://api.urlbox.io/render/00000000-0000-0000-0000-000000000000"}' ) )
150 | ->getMock();
151 |
152 | $urlbox = new Urlbox( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', $guzzleMock );
153 |
154 | $response = $urlbox->renderAsync( [
155 | 'url' => 'https://example.com',
156 | 'format' => 'png'
157 | ] );
158 |
159 | $this->assertEquals(
160 | [
161 | 'status' => 'created',
162 | 'renderId' => '00000000-0000-0000-0000-000000000000',
163 | 'statusUrl' => 'https://api.urlbox.io/render/00000000-0000-0000-0000-000000000000'
164 | ],
165 | $response
166 | );
167 | $this->assertEquals( 'https://api.urlbox.io/v1/render', $url );
168 | $this->assertEquals( [
169 | 'headers' => [
170 | 'Authorization' => 'Bearer API_SECRET',
171 | ],
172 | 'json' => [
173 | 'url' => 'https://example.com',
174 | 'format' => 'png'
175 | ]
176 | ], $requestOptions );
177 | }
178 |
179 | public function testGenerateSignedUrlDefaultFormatToPng()
180 | {
181 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
182 | $this->assertEquals(
183 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/png?url=https%3A%2F%2Fexample.com',
184 | $urlbox->generateSignedUrl( [ 'url' => 'https://example.com' ] )
185 | );
186 | }
187 |
188 | public function testGenerateSignedUrlCanSetFormatToPng()
189 | {
190 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
191 |
192 | $url = $urlbox->generateSignedUrl( [
193 | 'url' => 'https://example.com',
194 | 'format' => 'png'
195 | ] );
196 |
197 | $this->assertEquals(
198 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/png?url=https%3A%2F%2Fexample.com',
199 | $url
200 | );
201 | }
202 |
203 | public function testGenerateSignedUrlCanSetFormatToJpg()
204 | {
205 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
206 |
207 | $url = $urlbox->generateSignedUrl( [
208 | 'url' => 'https://example.com',
209 | 'format' => 'jpg'
210 | ] );
211 |
212 | $this->assertEquals(
213 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/jpg?url=https%3A%2F%2Fexample.com',
214 | $url
215 | );
216 | }
217 |
218 | public function testGenerateSignedUrlCanSetFormatToJpeg()
219 | {
220 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
221 |
222 | $url = $urlbox->generateSignedUrl( [
223 | 'url' => 'https://example.com',
224 | 'format' => 'jpeg'
225 | ] );
226 |
227 | $this->assertEquals(
228 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/jpeg?url=https%3A%2F%2Fexample.com',
229 | $url
230 | );
231 | }
232 |
233 | public function testGenerateSignedUrlCanSetFormatToAvif()
234 | {
235 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
236 |
237 | $url = $urlbox->generateSignedUrl( [
238 | 'url' => 'https://example.com',
239 | 'format' => 'avif'
240 | ] );
241 |
242 | $this->assertEquals(
243 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/avif?url=https%3A%2F%2Fexample.com',
244 | $url
245 | );
246 | }
247 |
248 | public function testGenerateSignedUrlCanSetFormatToWebp()
249 | {
250 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
251 |
252 | $url = $urlbox->generateSignedUrl( [
253 | 'url' => 'https://example.com',
254 | 'format' => 'webp'
255 | ] );
256 |
257 | $this->assertEquals(
258 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/webp?url=https%3A%2F%2Fexample.com',
259 | $url
260 | );
261 | }
262 |
263 | public function testGenerateSignedUrlCanSetFormatToPdf()
264 | {
265 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
266 |
267 | $url = $urlbox->generateSignedUrl( [
268 | 'url' => 'https://example.com',
269 | 'format' => 'pdf'
270 | ] );
271 |
272 | $this->assertEquals(
273 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/pdf?url=https%3A%2F%2Fexample.com',
274 | $url
275 | );
276 | }
277 |
278 | public function testGenerateSignedUrlCanSetFormatToSvg()
279 | {
280 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
281 |
282 | $url = $urlbox->generateSignedUrl( [
283 | 'url' => 'https://example.com',
284 | 'format' => 'svg'
285 | ] );
286 |
287 | $this->assertEquals(
288 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/svg?url=https%3A%2F%2Fexample.com',
289 | $url
290 | );
291 | }
292 |
293 | public function testGenerateSignedUrlCanSetFormatToHtml()
294 | {
295 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
296 |
297 | $url = $urlbox->generateSignedUrl( [
298 | 'url' => 'https://example.com',
299 | 'format' => 'html'
300 | ] );
301 |
302 | $this->assertEquals(
303 | 'https://api.urlbox.io/v1/API_KEY/5eaae418596fb183174660503df908a3966f4ba5/html?url=https%3A%2F%2Fexample.com',
304 | $url
305 | );
306 | }
307 |
308 | public function testGenerateSignedUrlEncodesOptionsCorrectly()
309 | {
310 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
311 |
312 | $url = $urlbox->generateSignedUrl( [
313 | 'url' => 'https://example.com/~!@#$%^&*(){}[]=:/,;?+\'"\\',
314 | 'format' => 'png',
315 | 'block_ads' => true
316 | ] );
317 |
318 | $this->assertEquals(
319 | "https://api.urlbox.io/v1/API_KEY/897c2361c52a5eb41b9128a2b7e70ffd5fefd662/png?url=https%3A%2F%2Fexample.com%2F~!%40%23%24%25%5E%26*()%7B%7D%5B%5D%3D%3A%2F%2C%3B%3F%2B'%22%5C&block_ads=true",
320 | $url
321 | );
322 | }
323 |
324 | public function testGenerateSignedUrlProducesCorrectUrlForTheKitchenSink()
325 | {
326 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
327 | $options = [
328 | 'format' => 'png',
329 | 'url' => 'https://app_staging.example.com/misc/template_preview.php?dsfdsfsdf&acc=79&cb=ba86b4c1®ions=%5B%7B%22id%22%3A%22dsfds%22%2C%22data%22%3A%7B%22html%22%3A%22It%20works!%22%7D%2C%22type%22%3A%22html%22%7D%5D&state=published&tid=7&sig=a642316f7e0ac9d783c30ef30a89bed3204252000319a2789851bc3de65ea216',
330 | 'delay' => 5000,
331 | 'selector' => '#trynow',
332 | 'full_page' => true,
333 | 'width' => 1280,
334 | 'height' => '1024',
335 | 'cookie' => [ 'ckplns=1', 'foo=bar' ],
336 | 'user_agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.32 (KHTML, like Gecko) Version/10.0 Mobile/14A5261v Safari/602.1',
337 | 'retina' => 'true',
338 | 'thumb_width' => '400',
339 | 'crop_width' => 500,
340 | 'ttl' => '604800',
341 | 'force' => true,
342 | 'headless' => false,
343 | 'wait_for' => '.someel',
344 | 'click' => '#tab-specs-trigger',
345 | 'hover' => 'a[href="https://google.com"]',
346 | 'bg_color' => '#bbbddd',
347 | 'highlight' => 'trump|inauguration',
348 | 'highlightbg' => '#11cc77',
349 | 'highlightfg' => 'green',
350 | 'hide_selector' => '.modal-backdrop, #email-roadblock-topographic-modal',
351 | 'flash' => 'true',
352 | 'timeout' => 40000,
353 | 's3_path' => '/path/to/image with space',
354 | 'use_s3' => 'true',
355 | ];
356 |
357 | $url = $urlbox->generateSignedUrl( $options );
358 |
359 | $this->assertEquals(
360 | "https://api.urlbox.io/v1/API_KEY/5280bc0f0fa198eb6fcde9fd3f32280dec496ee3/png?url=https%3A%2F%2Fapp_staging.example.com%2Fmisc%2Ftemplate_preview.php%3Fdsfdsfsdf%26acc%3D79%26cb%3Dba86b4c1%26regions%3D%255B%257B%2522id%2522%253A%2522dsfds%2522%252C%2522data%2522%253A%257B%2522html%2522%253A%2522It%2520works!%2522%257D%252C%2522type%2522%253A%2522html%2522%257D%255D%26state%3Dpublished%26tid%3D7%26sig%3Da642316f7e0ac9d783c30ef30a89bed3204252000319a2789851bc3de65ea216&delay=5000&selector=%23trynow&full_page=true&width=1280&height=1024&cookie=ckplns%3D1&cookie=foo%3Dbar&user_agent=Mozilla%2F5.0%20(iPhone%3B%20CPU%20iPhone%20OS%2010_0%20like%20Mac%20OS%20X)%20AppleWebKit%2F602.1.32%20(KHTML%2C%20like%20Gecko)%20Version%2F10.0%20Mobile%2F14A5261v%20Safari%2F602.1&retina=true&thumb_width=400&crop_width=500&ttl=604800&force=true&headless=false&wait_for=.someel&click=%23tab-specs-trigger&hover=a%5Bhref%3D%22https%3A%2F%2Fgoogle.com%22%5D&bg_color=%23bbbddd&highlight=trump%7Cinauguration&highlightbg=%2311cc77&highlightfg=green&hide_selector=.modal-backdrop%2C%20%23email-roadblock-topographic-modal&flash=true&timeout=40000&s3_path=%2Fpath%2Fto%2Fimage%20with%20space&use_s3=true",
361 | $url
362 | );
363 | }
364 |
365 | public function testGenerateUnsignedUrlDefaultFormatToPng()
366 | {
367 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
368 | $this->assertEquals(
369 | 'https://api.urlbox.io/v1/API_KEY/png?url=https%3A%2F%2Fexample.com',
370 | $urlbox->generateUnsignedUrl( [ 'url' => 'https://example.com' ] )
371 | );
372 | }
373 |
374 | public function testGenerateUnsignedUrlCanSetFormatToPng()
375 | {
376 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
377 |
378 | $url = $urlbox->generateUnsignedUrl( [
379 | 'url' => 'https://example.com',
380 | 'format' => 'png'
381 | ] );
382 |
383 | $this->assertEquals(
384 | 'https://api.urlbox.io/v1/API_KEY/png?url=https%3A%2F%2Fexample.com',
385 | $url
386 | );
387 | }
388 |
389 | public function testGenerateUnsignedUrlCanSetFormatToJpg()
390 | {
391 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
392 |
393 | $url = $urlbox->generateUnsignedUrl( [
394 | 'url' => 'https://example.com',
395 | 'format' => 'jpg'
396 | ] );
397 |
398 | $this->assertEquals(
399 | 'https://api.urlbox.io/v1/API_KEY/jpg?url=https%3A%2F%2Fexample.com',
400 | $url
401 | );
402 | }
403 |
404 | public function testGenerateUnsignedUrlCanSetFormatToJpeg()
405 | {
406 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
407 |
408 | $url = $urlbox->generateUnsignedUrl( [
409 | 'url' => 'https://example.com',
410 | 'format' => 'jpeg'
411 | ] );
412 |
413 | $this->assertEquals(
414 | 'https://api.urlbox.io/v1/API_KEY/jpeg?url=https%3A%2F%2Fexample.com',
415 | $url
416 | );
417 | }
418 |
419 | public function testGenerateUnsignedUrlCanSetFormatToAvif()
420 | {
421 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
422 |
423 | $url = $urlbox->generateUnsignedUrl( [
424 | 'url' => 'https://example.com',
425 | 'format' => 'avif'
426 | ] );
427 |
428 | $this->assertEquals(
429 | 'https://api.urlbox.io/v1/API_KEY/avif?url=https%3A%2F%2Fexample.com',
430 | $url
431 | );
432 | }
433 |
434 | public function testGenerateUnsignedUrlCanSetFormatToWebp()
435 | {
436 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
437 |
438 | $url = $urlbox->generateUnsignedUrl( [
439 | 'url' => 'https://example.com',
440 | 'format' => 'webp'
441 | ] );
442 |
443 | $this->assertEquals(
444 | 'https://api.urlbox.io/v1/API_KEY/webp?url=https%3A%2F%2Fexample.com',
445 | $url
446 | );
447 | }
448 |
449 | public function testGenerateUnsignedUrlCanSetFormatToPdf()
450 | {
451 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
452 |
453 | $url = $urlbox->generateUnsignedUrl( [
454 | 'url' => 'https://example.com',
455 | 'format' => 'pdf'
456 | ] );
457 |
458 | $this->assertEquals(
459 | 'https://api.urlbox.io/v1/API_KEY/pdf?url=https%3A%2F%2Fexample.com',
460 | $url
461 | );
462 | }
463 |
464 | public function testGenerateUnsignedUrlCanSetFormatToSvg()
465 | {
466 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
467 |
468 | $url = $urlbox->generateUnsignedUrl( [
469 | 'url' => 'https://example.com',
470 | 'format' => 'svg'
471 | ] );
472 |
473 | $this->assertEquals(
474 | 'https://api.urlbox.io/v1/API_KEY/svg?url=https%3A%2F%2Fexample.com',
475 | $url
476 | );
477 | }
478 |
479 | public function testGenerateUnsignedUrlCanSetFormatToHtml()
480 | {
481 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
482 |
483 | $url = $urlbox->generateUnsignedUrl( [
484 | 'url' => 'https://example.com',
485 | 'format' => 'html'
486 | ] );
487 |
488 | $this->assertEquals(
489 | 'https://api.urlbox.io/v1/API_KEY/html?url=https%3A%2F%2Fexample.com',
490 | $url
491 | );
492 | }
493 |
494 | public function testGenerateUnsignedUrlEncodesOptionsCorrectly()
495 | {
496 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
497 |
498 | $url = $urlbox->generateUnsignedUrl( [
499 | 'url' => 'https://example.com/~!@#$%^&*(){}[]=:/,;?+\'"\\',
500 | 'format' => 'png',
501 | 'block_ads' => true
502 | ] );
503 |
504 | $this->assertEquals(
505 | "https://api.urlbox.io/v1/API_KEY/png?url=https%3A%2F%2Fexample.com%2F~!%40%23%24%25%5E%26*()%7B%7D%5B%5D%3D%3A%2F%2C%3B%3F%2B'%22%5C&block_ads=true",
506 | $url
507 | );
508 | }
509 |
510 | public function testGenerateUnsignedUrlProducesCorrectUrlForTheKitchenSink()
511 | {
512 | $urlbox = Urlbox::fromCredentials( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET', Mockery::mock( Client::class ) );
513 | $options = [
514 | 'format' => 'png',
515 | 'url' => 'https://app_staging.example.com/misc/template_preview.php?dsfdsfsdf&acc=79&cb=ba86b4c1®ions=%5B%7B%22id%22%3A%22dsfds%22%2C%22data%22%3A%7B%22html%22%3A%22It%20works!%22%7D%2C%22type%22%3A%22html%22%7D%5D&state=published&tid=7&sig=a642316f7e0ac9d783c30ef30a89bed3204252000319a2789851bc3de65ea216',
516 | 'delay' => 5000,
517 | 'selector' => '#trynow',
518 | 'full_page' => true,
519 | 'width' => 1280,
520 | 'height' => '1024',
521 | 'cookie' => [ 'ckplns=1', 'foo=bar' ],
522 | 'user_agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.32 (KHTML, like Gecko) Version/10.0 Mobile/14A5261v Safari/602.1',
523 | 'retina' => 'true',
524 | 'thumb_width' => '400',
525 | 'crop_width' => 500,
526 | 'ttl' => '604800',
527 | 'force' => true,
528 | 'headless' => false,
529 | 'wait_for' => '.someel',
530 | 'click' => '#tab-specs-trigger',
531 | 'hover' => 'a[href="https://google.com"]',
532 | 'bg_color' => '#bbbddd',
533 | 'highlight' => 'trump|inauguration',
534 | 'highlightbg' => '#11cc77',
535 | 'highlightfg' => 'green',
536 | 'hide_selector' => '.modal-backdrop, #email-roadblock-topographic-modal',
537 | 'flash' => 'true',
538 | 'timeout' => 40000,
539 | 's3_path' => '/path/to/image with space',
540 | 'use_s3' => 'true',
541 | ];
542 |
543 | $url = $urlbox->generateUnsignedUrl( $options );
544 |
545 | $this->assertEquals(
546 | "https://api.urlbox.io/v1/API_KEY/png?url=https%3A%2F%2Fapp_staging.example.com%2Fmisc%2Ftemplate_preview.php%3Fdsfdsfsdf%26acc%3D79%26cb%3Dba86b4c1%26regions%3D%255B%257B%2522id%2522%253A%2522dsfds%2522%252C%2522data%2522%253A%257B%2522html%2522%253A%2522It%2520works!%2522%257D%252C%2522type%2522%253A%2522html%2522%257D%255D%26state%3Dpublished%26tid%3D7%26sig%3Da642316f7e0ac9d783c30ef30a89bed3204252000319a2789851bc3de65ea216&delay=5000&selector=%23trynow&full_page=true&width=1280&height=1024&cookie=ckplns%3D1&cookie=foo%3Dbar&user_agent=Mozilla%2F5.0%20(iPhone%3B%20CPU%20iPhone%20OS%2010_0%20like%20Mac%20OS%20X)%20AppleWebKit%2F602.1.32%20(KHTML%2C%20like%20Gecko)%20Version%2F10.0%20Mobile%2F14A5261v%20Safari%2F602.1&retina=true&thumb_width=400&crop_width=500&ttl=604800&force=true&headless=false&wait_for=.someel&click=%23tab-specs-trigger&hover=a%5Bhref%3D%22https%3A%2F%2Fgoogle.com%22%5D&bg_color=%23bbbddd&highlight=trump%7Cinauguration&highlightbg=%2311cc77&highlightfg=green&hide_selector=.modal-backdrop%2C%20%23email-roadblock-topographic-modal&flash=true&timeout=40000&s3_path=%2Fpath%2Fto%2Fimage%20with%20space&use_s3=true",
547 | $url
548 | );
549 | }
550 |
551 | public function testVerifyWebhookSignatureThrowsExceptionWhenWebhookSecretNotSet()
552 | {
553 | try {
554 | $urlbox = new Urlbox( 'API_KEY', 'API_SECRET' );
555 | $urlbox->verifyWebhookSignature( 't=1,sha256=foobar', '' );
556 |
557 | $this->fail( 'Expected Exception not thrown' );
558 | } catch ( Exception $exception ) {
559 | $this->assertEquals(
560 | 'Unable to verify signature as Webhook Secret is not set. You can find your webhook secret inside your project\'s settings - https://www.urlbox.io/dashboard/projects',
561 | $exception->getMessage()
562 | );
563 | }
564 | }
565 |
566 | public function testVerifyWebhookSignatureThrowsExceptionWhenHeaderIsEmpty()
567 | {
568 | try {
569 | $urlbox = new Urlbox( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET' );
570 | $urlbox->verifyWebhookSignature( '', '' );
571 |
572 | $this->fail( 'Expected Exception not thrown' );
573 | } catch ( InvalidArgumentException $exception ) {
574 | $this->assertEquals(
575 | 'Unable to verify signature as header is empty. Please ensure you pass the `x-urlbox-signature` from the header of the webhook response',
576 | $exception->getMessage()
577 | );
578 | }
579 | }
580 |
581 | public function testVerifyWebhookSignatureReturnsTrueWhenSignatureMatches()
582 | {
583 | $content = '{"event": "render.succeeded","renderId": "19a59ab6-a5aa-4cde-86cb-d2b23302fd84","result": {"renderUrl": "https://renders.urlbox.io/urlbox1/renders/6215a3df94d7588f7d910513/2022/7/6/19a59ab6-a5aa-4cde-86cb-d2b23302fd84.png","size": 34097},"meta": {"startTime": "2022-07-06T17:49:18.593Z","endTime": "2022-07-06T17:49:21.103Z"}}';
584 | $header = 't=1657129761,sha256=ddbceae3998704c0b264d8e8c1d486df9f1c0b6cdb77e6e13ce7de4a72fbd81d';
585 |
586 | $urlbox = new Urlbox( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET' );
587 |
588 | $this->assertTrue( $urlbox->verifyWebhookSignature( $header, $content ) );
589 | }
590 |
591 | public function testVerifyWebhookSignatureReturnsFalseWhenSignatureDoNotMatch()
592 | {
593 | $content = '{"event": "render.succeeded","renderId": "19a59ab6-a5aa-4cde-86cb-d2b23302fd84","result": {"renderUrl": "https://renders.urlbox.io/urlbox1/renders/6215a3df94d7588f7d910513/2022/7/6/19a59ab6-a5aa-4cde-86cb-d2b23302fd84.png","size": 34097},"meta": {"startTime": "2022-07-06T17:49:18.593Z","endTime": "2022-07-06T17:49:21.103Z"}}';
594 | $header = 't=1657129761,sha256=foobare3998704c0b264d8e8c1d4foobar1c0b6cdb77e6e13ce7de4a72foobar';
595 |
596 | $urlbox = new Urlbox( 'API_KEY', 'API_SECRET', 'WEBHOOK_SECRET' );
597 |
598 | $this->assertFalse( $urlbox->verifyWebhookSignature( $header, $content ) );
599 | }
600 |
601 | public function testVerifyWebhookSignatureReturnsFalseWhenWebhookSecretIncorrect()
602 | {
603 | $content = '{"event": "render.succeeded","renderId": "19a59ab6-a5aa-4cde-86cb-d2b23302fd84","result": {"renderUrl": "https://renders.urlbox.io/urlbox1/renders/6215a3df94d7588f7d910513/2022/7/6/19a59ab6-a5aa-4cde-86cb-d2b23302fd84.png","size": 34097},"meta": {"startTime": "2022-07-06T17:49:18.593Z","endTime": "2022-07-06T17:49:21.103Z"}}';
604 | $header = 't=1657129761,sha256=ddbceae3998704c0b264d8e8c1d486df9f1c0b6cdb77e6e13ce7de4a72fbd81d';
605 |
606 | $urlbox = new Urlbox( 'API_KEY', 'API_SECRET', 'INCORRECT_WEBHOOK_SECRET' );
607 |
608 | $this->assertFalse( $urlbox->verifyWebhookSignature( $header, $content ) );
609 | }
610 |
611 |
612 | }
613 |
614 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "baa09048ac3d6f09e083fdb16ce3f546",
8 | "packages": [
9 | {
10 | "name": "guzzlehttp/guzzle",
11 | "version": "7.5.0",
12 | "source": {
13 | "type": "git",
14 | "url": "https://github.com/guzzle/guzzle.git",
15 | "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba"
16 | },
17 | "dist": {
18 | "type": "zip",
19 | "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b50a2a1251152e43f6a37f0fa053e730a67d25ba",
20 | "reference": "b50a2a1251152e43f6a37f0fa053e730a67d25ba",
21 | "shasum": ""
22 | },
23 | "require": {
24 | "ext-json": "*",
25 | "guzzlehttp/promises": "^1.5",
26 | "guzzlehttp/psr7": "^1.9 || ^2.4",
27 | "php": "^7.2.5 || ^8.0",
28 | "psr/http-client": "^1.0",
29 | "symfony/deprecation-contracts": "^2.2 || ^3.0"
30 | },
31 | "provide": {
32 | "psr/http-client-implementation": "1.0"
33 | },
34 | "require-dev": {
35 | "bamarni/composer-bin-plugin": "^1.8.1",
36 | "ext-curl": "*",
37 | "php-http/client-integration-tests": "^3.0",
38 | "phpunit/phpunit": "^8.5.29 || ^9.5.23",
39 | "psr/log": "^1.1 || ^2.0 || ^3.0"
40 | },
41 | "suggest": {
42 | "ext-curl": "Required for CURL handler support",
43 | "ext-intl": "Required for Internationalized Domain Name (IDN) support",
44 | "psr/log": "Required for using the Log middleware"
45 | },
46 | "type": "library",
47 | "extra": {
48 | "bamarni-bin": {
49 | "bin-links": true,
50 | "forward-command": false
51 | },
52 | "branch-alias": {
53 | "dev-master": "7.5-dev"
54 | }
55 | },
56 | "autoload": {
57 | "files": [
58 | "src/functions_include.php"
59 | ],
60 | "psr-4": {
61 | "GuzzleHttp\\": "src/"
62 | }
63 | },
64 | "notification-url": "https://packagist.org/downloads/",
65 | "license": [
66 | "MIT"
67 | ],
68 | "authors": [
69 | {
70 | "name": "Graham Campbell",
71 | "email": "hello@gjcampbell.co.uk",
72 | "homepage": "https://github.com/GrahamCampbell"
73 | },
74 | {
75 | "name": "Michael Dowling",
76 | "email": "mtdowling@gmail.com",
77 | "homepage": "https://github.com/mtdowling"
78 | },
79 | {
80 | "name": "Jeremy Lindblom",
81 | "email": "jeremeamia@gmail.com",
82 | "homepage": "https://github.com/jeremeamia"
83 | },
84 | {
85 | "name": "George Mponos",
86 | "email": "gmponos@gmail.com",
87 | "homepage": "https://github.com/gmponos"
88 | },
89 | {
90 | "name": "Tobias Nyholm",
91 | "email": "tobias.nyholm@gmail.com",
92 | "homepage": "https://github.com/Nyholm"
93 | },
94 | {
95 | "name": "Márk Sági-Kazár",
96 | "email": "mark.sagikazar@gmail.com",
97 | "homepage": "https://github.com/sagikazarmark"
98 | },
99 | {
100 | "name": "Tobias Schultze",
101 | "email": "webmaster@tubo-world.de",
102 | "homepage": "https://github.com/Tobion"
103 | }
104 | ],
105 | "description": "Guzzle is a PHP HTTP client library",
106 | "keywords": [
107 | "client",
108 | "curl",
109 | "framework",
110 | "http",
111 | "http client",
112 | "psr-18",
113 | "psr-7",
114 | "rest",
115 | "web service"
116 | ],
117 | "support": {
118 | "issues": "https://github.com/guzzle/guzzle/issues",
119 | "source": "https://github.com/guzzle/guzzle/tree/7.5.0"
120 | },
121 | "funding": [
122 | {
123 | "url": "https://github.com/GrahamCampbell",
124 | "type": "github"
125 | },
126 | {
127 | "url": "https://github.com/Nyholm",
128 | "type": "github"
129 | },
130 | {
131 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle",
132 | "type": "tidelift"
133 | }
134 | ],
135 | "time": "2022-08-28T15:39:27+00:00"
136 | },
137 | {
138 | "name": "guzzlehttp/promises",
139 | "version": "1.5.2",
140 | "source": {
141 | "type": "git",
142 | "url": "https://github.com/guzzle/promises.git",
143 | "reference": "b94b2807d85443f9719887892882d0329d1e2598"
144 | },
145 | "dist": {
146 | "type": "zip",
147 | "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
148 | "reference": "b94b2807d85443f9719887892882d0329d1e2598",
149 | "shasum": ""
150 | },
151 | "require": {
152 | "php": ">=5.5"
153 | },
154 | "require-dev": {
155 | "symfony/phpunit-bridge": "^4.4 || ^5.1"
156 | },
157 | "type": "library",
158 | "extra": {
159 | "branch-alias": {
160 | "dev-master": "1.5-dev"
161 | }
162 | },
163 | "autoload": {
164 | "files": [
165 | "src/functions_include.php"
166 | ],
167 | "psr-4": {
168 | "GuzzleHttp\\Promise\\": "src/"
169 | }
170 | },
171 | "notification-url": "https://packagist.org/downloads/",
172 | "license": [
173 | "MIT"
174 | ],
175 | "authors": [
176 | {
177 | "name": "Graham Campbell",
178 | "email": "hello@gjcampbell.co.uk",
179 | "homepage": "https://github.com/GrahamCampbell"
180 | },
181 | {
182 | "name": "Michael Dowling",
183 | "email": "mtdowling@gmail.com",
184 | "homepage": "https://github.com/mtdowling"
185 | },
186 | {
187 | "name": "Tobias Nyholm",
188 | "email": "tobias.nyholm@gmail.com",
189 | "homepage": "https://github.com/Nyholm"
190 | },
191 | {
192 | "name": "Tobias Schultze",
193 | "email": "webmaster@tubo-world.de",
194 | "homepage": "https://github.com/Tobion"
195 | }
196 | ],
197 | "description": "Guzzle promises library",
198 | "keywords": [
199 | "promise"
200 | ],
201 | "support": {
202 | "issues": "https://github.com/guzzle/promises/issues",
203 | "source": "https://github.com/guzzle/promises/tree/1.5.2"
204 | },
205 | "funding": [
206 | {
207 | "url": "https://github.com/GrahamCampbell",
208 | "type": "github"
209 | },
210 | {
211 | "url": "https://github.com/Nyholm",
212 | "type": "github"
213 | },
214 | {
215 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises",
216 | "type": "tidelift"
217 | }
218 | ],
219 | "time": "2022-08-28T14:55:35+00:00"
220 | },
221 | {
222 | "name": "guzzlehttp/psr7",
223 | "version": "2.6.1",
224 | "source": {
225 | "type": "git",
226 | "url": "https://github.com/guzzle/psr7.git",
227 | "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727"
228 | },
229 | "dist": {
230 | "type": "zip",
231 | "url": "https://api.github.com/repos/guzzle/psr7/zipball/be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
232 | "reference": "be45764272e8873c72dbe3d2edcfdfcc3bc9f727",
233 | "shasum": ""
234 | },
235 | "require": {
236 | "php": "^7.2.5 || ^8.0",
237 | "psr/http-factory": "^1.0",
238 | "psr/http-message": "^1.1 || ^2.0",
239 | "ralouphie/getallheaders": "^3.0"
240 | },
241 | "provide": {
242 | "psr/http-factory-implementation": "1.0",
243 | "psr/http-message-implementation": "1.0"
244 | },
245 | "require-dev": {
246 | "bamarni/composer-bin-plugin": "^1.8.1",
247 | "http-interop/http-factory-tests": "^0.9",
248 | "phpunit/phpunit": "^8.5.29 || ^9.5.23"
249 | },
250 | "suggest": {
251 | "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
252 | },
253 | "type": "library",
254 | "extra": {
255 | "bamarni-bin": {
256 | "bin-links": true,
257 | "forward-command": false
258 | }
259 | },
260 | "autoload": {
261 | "psr-4": {
262 | "GuzzleHttp\\Psr7\\": "src/"
263 | }
264 | },
265 | "notification-url": "https://packagist.org/downloads/",
266 | "license": [
267 | "MIT"
268 | ],
269 | "authors": [
270 | {
271 | "name": "Graham Campbell",
272 | "email": "hello@gjcampbell.co.uk",
273 | "homepage": "https://github.com/GrahamCampbell"
274 | },
275 | {
276 | "name": "Michael Dowling",
277 | "email": "mtdowling@gmail.com",
278 | "homepage": "https://github.com/mtdowling"
279 | },
280 | {
281 | "name": "George Mponos",
282 | "email": "gmponos@gmail.com",
283 | "homepage": "https://github.com/gmponos"
284 | },
285 | {
286 | "name": "Tobias Nyholm",
287 | "email": "tobias.nyholm@gmail.com",
288 | "homepage": "https://github.com/Nyholm"
289 | },
290 | {
291 | "name": "Márk Sági-Kazár",
292 | "email": "mark.sagikazar@gmail.com",
293 | "homepage": "https://github.com/sagikazarmark"
294 | },
295 | {
296 | "name": "Tobias Schultze",
297 | "email": "webmaster@tubo-world.de",
298 | "homepage": "https://github.com/Tobion"
299 | },
300 | {
301 | "name": "Márk Sági-Kazár",
302 | "email": "mark.sagikazar@gmail.com",
303 | "homepage": "https://sagikazarmark.hu"
304 | }
305 | ],
306 | "description": "PSR-7 message implementation that also provides common utility methods",
307 | "keywords": [
308 | "http",
309 | "message",
310 | "psr-7",
311 | "request",
312 | "response",
313 | "stream",
314 | "uri",
315 | "url"
316 | ],
317 | "support": {
318 | "issues": "https://github.com/guzzle/psr7/issues",
319 | "source": "https://github.com/guzzle/psr7/tree/2.6.1"
320 | },
321 | "funding": [
322 | {
323 | "url": "https://github.com/GrahamCampbell",
324 | "type": "github"
325 | },
326 | {
327 | "url": "https://github.com/Nyholm",
328 | "type": "github"
329 | },
330 | {
331 | "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7",
332 | "type": "tidelift"
333 | }
334 | ],
335 | "time": "2023-08-27T10:13:57+00:00"
336 | },
337 | {
338 | "name": "psr/http-client",
339 | "version": "1.0.1",
340 | "source": {
341 | "type": "git",
342 | "url": "https://github.com/php-fig/http-client.git",
343 | "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
344 | },
345 | "dist": {
346 | "type": "zip",
347 | "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
348 | "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
349 | "shasum": ""
350 | },
351 | "require": {
352 | "php": "^7.0 || ^8.0",
353 | "psr/http-message": "^1.0"
354 | },
355 | "type": "library",
356 | "extra": {
357 | "branch-alias": {
358 | "dev-master": "1.0.x-dev"
359 | }
360 | },
361 | "autoload": {
362 | "psr-4": {
363 | "Psr\\Http\\Client\\": "src/"
364 | }
365 | },
366 | "notification-url": "https://packagist.org/downloads/",
367 | "license": [
368 | "MIT"
369 | ],
370 | "authors": [
371 | {
372 | "name": "PHP-FIG",
373 | "homepage": "http://www.php-fig.org/"
374 | }
375 | ],
376 | "description": "Common interface for HTTP clients",
377 | "homepage": "https://github.com/php-fig/http-client",
378 | "keywords": [
379 | "http",
380 | "http-client",
381 | "psr",
382 | "psr-18"
383 | ],
384 | "support": {
385 | "source": "https://github.com/php-fig/http-client/tree/master"
386 | },
387 | "time": "2020-06-29T06:28:15+00:00"
388 | },
389 | {
390 | "name": "psr/http-factory",
391 | "version": "1.0.2",
392 | "source": {
393 | "type": "git",
394 | "url": "https://github.com/php-fig/http-factory.git",
395 | "reference": "e616d01114759c4c489f93b099585439f795fe35"
396 | },
397 | "dist": {
398 | "type": "zip",
399 | "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
400 | "reference": "e616d01114759c4c489f93b099585439f795fe35",
401 | "shasum": ""
402 | },
403 | "require": {
404 | "php": ">=7.0.0",
405 | "psr/http-message": "^1.0 || ^2.0"
406 | },
407 | "type": "library",
408 | "extra": {
409 | "branch-alias": {
410 | "dev-master": "1.0.x-dev"
411 | }
412 | },
413 | "autoload": {
414 | "psr-4": {
415 | "Psr\\Http\\Message\\": "src/"
416 | }
417 | },
418 | "notification-url": "https://packagist.org/downloads/",
419 | "license": [
420 | "MIT"
421 | ],
422 | "authors": [
423 | {
424 | "name": "PHP-FIG",
425 | "homepage": "https://www.php-fig.org/"
426 | }
427 | ],
428 | "description": "Common interfaces for PSR-7 HTTP message factories",
429 | "keywords": [
430 | "factory",
431 | "http",
432 | "message",
433 | "psr",
434 | "psr-17",
435 | "psr-7",
436 | "request",
437 | "response"
438 | ],
439 | "support": {
440 | "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
441 | },
442 | "time": "2023-04-10T20:10:41+00:00"
443 | },
444 | {
445 | "name": "psr/http-message",
446 | "version": "1.1",
447 | "source": {
448 | "type": "git",
449 | "url": "https://github.com/php-fig/http-message.git",
450 | "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
451 | },
452 | "dist": {
453 | "type": "zip",
454 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
455 | "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
456 | "shasum": ""
457 | },
458 | "require": {
459 | "php": "^7.2 || ^8.0"
460 | },
461 | "type": "library",
462 | "extra": {
463 | "branch-alias": {
464 | "dev-master": "1.1.x-dev"
465 | }
466 | },
467 | "autoload": {
468 | "psr-4": {
469 | "Psr\\Http\\Message\\": "src/"
470 | }
471 | },
472 | "notification-url": "https://packagist.org/downloads/",
473 | "license": [
474 | "MIT"
475 | ],
476 | "authors": [
477 | {
478 | "name": "PHP-FIG",
479 | "homepage": "http://www.php-fig.org/"
480 | }
481 | ],
482 | "description": "Common interface for HTTP messages",
483 | "homepage": "https://github.com/php-fig/http-message",
484 | "keywords": [
485 | "http",
486 | "http-message",
487 | "psr",
488 | "psr-7",
489 | "request",
490 | "response"
491 | ],
492 | "support": {
493 | "source": "https://github.com/php-fig/http-message/tree/1.1"
494 | },
495 | "time": "2023-04-04T09:50:52+00:00"
496 | },
497 | {
498 | "name": "ralouphie/getallheaders",
499 | "version": "3.0.3",
500 | "source": {
501 | "type": "git",
502 | "url": "https://github.com/ralouphie/getallheaders.git",
503 | "reference": "120b605dfeb996808c31b6477290a714d356e822"
504 | },
505 | "dist": {
506 | "type": "zip",
507 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
508 | "reference": "120b605dfeb996808c31b6477290a714d356e822",
509 | "shasum": ""
510 | },
511 | "require": {
512 | "php": ">=5.6"
513 | },
514 | "require-dev": {
515 | "php-coveralls/php-coveralls": "^2.1",
516 | "phpunit/phpunit": "^5 || ^6.5"
517 | },
518 | "type": "library",
519 | "autoload": {
520 | "files": [
521 | "src/getallheaders.php"
522 | ]
523 | },
524 | "notification-url": "https://packagist.org/downloads/",
525 | "license": [
526 | "MIT"
527 | ],
528 | "authors": [
529 | {
530 | "name": "Ralph Khattar",
531 | "email": "ralph.khattar@gmail.com"
532 | }
533 | ],
534 | "description": "A polyfill for getallheaders.",
535 | "support": {
536 | "issues": "https://github.com/ralouphie/getallheaders/issues",
537 | "source": "https://github.com/ralouphie/getallheaders/tree/develop"
538 | },
539 | "time": "2019-03-08T08:55:37+00:00"
540 | },
541 | {
542 | "name": "symfony/deprecation-contracts",
543 | "version": "v3.2.1",
544 | "source": {
545 | "type": "git",
546 | "url": "https://github.com/symfony/deprecation-contracts.git",
547 | "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
548 | },
549 | "dist": {
550 | "type": "zip",
551 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
552 | "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
553 | "shasum": ""
554 | },
555 | "require": {
556 | "php": ">=8.1"
557 | },
558 | "type": "library",
559 | "extra": {
560 | "branch-alias": {
561 | "dev-main": "3.3-dev"
562 | },
563 | "thanks": {
564 | "name": "symfony/contracts",
565 | "url": "https://github.com/symfony/contracts"
566 | }
567 | },
568 | "autoload": {
569 | "files": [
570 | "function.php"
571 | ]
572 | },
573 | "notification-url": "https://packagist.org/downloads/",
574 | "license": [
575 | "MIT"
576 | ],
577 | "authors": [
578 | {
579 | "name": "Nicolas Grekas",
580 | "email": "p@tchwork.com"
581 | },
582 | {
583 | "name": "Symfony Community",
584 | "homepage": "https://symfony.com/contributors"
585 | }
586 | ],
587 | "description": "A generic function and convention to trigger deprecation notices",
588 | "homepage": "https://symfony.com",
589 | "support": {
590 | "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
591 | },
592 | "funding": [
593 | {
594 | "url": "https://symfony.com/sponsor",
595 | "type": "custom"
596 | },
597 | {
598 | "url": "https://github.com/fabpot",
599 | "type": "github"
600 | },
601 | {
602 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
603 | "type": "tidelift"
604 | }
605 | ],
606 | "time": "2023-03-01T10:25:55+00:00"
607 | }
608 | ],
609 | "packages-dev": [
610 | {
611 | "name": "doctrine/instantiator",
612 | "version": "2.0.0",
613 | "source": {
614 | "type": "git",
615 | "url": "https://github.com/doctrine/instantiator.git",
616 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
617 | },
618 | "dist": {
619 | "type": "zip",
620 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
621 | "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
622 | "shasum": ""
623 | },
624 | "require": {
625 | "php": "^8.1"
626 | },
627 | "require-dev": {
628 | "doctrine/coding-standard": "^11",
629 | "ext-pdo": "*",
630 | "ext-phar": "*",
631 | "phpbench/phpbench": "^1.2",
632 | "phpstan/phpstan": "^1.9.4",
633 | "phpstan/phpstan-phpunit": "^1.3",
634 | "phpunit/phpunit": "^9.5.27",
635 | "vimeo/psalm": "^5.4"
636 | },
637 | "type": "library",
638 | "autoload": {
639 | "psr-4": {
640 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
641 | }
642 | },
643 | "notification-url": "https://packagist.org/downloads/",
644 | "license": [
645 | "MIT"
646 | ],
647 | "authors": [
648 | {
649 | "name": "Marco Pivetta",
650 | "email": "ocramius@gmail.com",
651 | "homepage": "https://ocramius.github.io/"
652 | }
653 | ],
654 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
655 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
656 | "keywords": [
657 | "constructor",
658 | "instantiate"
659 | ],
660 | "support": {
661 | "issues": "https://github.com/doctrine/instantiator/issues",
662 | "source": "https://github.com/doctrine/instantiator/tree/2.0.0"
663 | },
664 | "funding": [
665 | {
666 | "url": "https://www.doctrine-project.org/sponsorship.html",
667 | "type": "custom"
668 | },
669 | {
670 | "url": "https://www.patreon.com/phpdoctrine",
671 | "type": "patreon"
672 | },
673 | {
674 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
675 | "type": "tidelift"
676 | }
677 | ],
678 | "time": "2022-12-30T00:23:10+00:00"
679 | },
680 | {
681 | "name": "hamcrest/hamcrest-php",
682 | "version": "v2.0.1",
683 | "source": {
684 | "type": "git",
685 | "url": "https://github.com/hamcrest/hamcrest-php.git",
686 | "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
687 | },
688 | "dist": {
689 | "type": "zip",
690 | "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
691 | "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
692 | "shasum": ""
693 | },
694 | "require": {
695 | "php": "^5.3|^7.0|^8.0"
696 | },
697 | "replace": {
698 | "cordoval/hamcrest-php": "*",
699 | "davedevelopment/hamcrest-php": "*",
700 | "kodova/hamcrest-php": "*"
701 | },
702 | "require-dev": {
703 | "phpunit/php-file-iterator": "^1.4 || ^2.0",
704 | "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
705 | },
706 | "type": "library",
707 | "extra": {
708 | "branch-alias": {
709 | "dev-master": "2.1-dev"
710 | }
711 | },
712 | "autoload": {
713 | "classmap": [
714 | "hamcrest"
715 | ]
716 | },
717 | "notification-url": "https://packagist.org/downloads/",
718 | "license": [
719 | "BSD-3-Clause"
720 | ],
721 | "description": "This is the PHP port of Hamcrest Matchers",
722 | "keywords": [
723 | "test"
724 | ],
725 | "support": {
726 | "issues": "https://github.com/hamcrest/hamcrest-php/issues",
727 | "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
728 | },
729 | "time": "2020-07-09T08:09:16+00:00"
730 | },
731 | {
732 | "name": "mockery/mockery",
733 | "version": "1.5.1",
734 | "source": {
735 | "type": "git",
736 | "url": "https://github.com/mockery/mockery.git",
737 | "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e"
738 | },
739 | "dist": {
740 | "type": "zip",
741 | "url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e",
742 | "reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e",
743 | "shasum": ""
744 | },
745 | "require": {
746 | "hamcrest/hamcrest-php": "^2.0.1",
747 | "lib-pcre": ">=7.0",
748 | "php": "^7.3 || ^8.0"
749 | },
750 | "conflict": {
751 | "phpunit/phpunit": "<8.0"
752 | },
753 | "require-dev": {
754 | "phpunit/phpunit": "^8.5 || ^9.3"
755 | },
756 | "type": "library",
757 | "extra": {
758 | "branch-alias": {
759 | "dev-master": "1.4.x-dev"
760 | }
761 | },
762 | "autoload": {
763 | "psr-0": {
764 | "Mockery": "library/"
765 | }
766 | },
767 | "notification-url": "https://packagist.org/downloads/",
768 | "license": [
769 | "BSD-3-Clause"
770 | ],
771 | "authors": [
772 | {
773 | "name": "Pádraic Brady",
774 | "email": "padraic.brady@gmail.com",
775 | "homepage": "http://blog.astrumfutura.com"
776 | },
777 | {
778 | "name": "Dave Marshall",
779 | "email": "dave.marshall@atstsolutions.co.uk",
780 | "homepage": "http://davedevelopment.co.uk"
781 | }
782 | ],
783 | "description": "Mockery is a simple yet flexible PHP mock object framework",
784 | "homepage": "https://github.com/mockery/mockery",
785 | "keywords": [
786 | "BDD",
787 | "TDD",
788 | "library",
789 | "mock",
790 | "mock objects",
791 | "mockery",
792 | "stub",
793 | "test",
794 | "test double",
795 | "testing"
796 | ],
797 | "support": {
798 | "issues": "https://github.com/mockery/mockery/issues",
799 | "source": "https://github.com/mockery/mockery/tree/1.5.1"
800 | },
801 | "time": "2022-09-07T15:32:08+00:00"
802 | },
803 | {
804 | "name": "myclabs/deep-copy",
805 | "version": "1.11.1",
806 | "source": {
807 | "type": "git",
808 | "url": "https://github.com/myclabs/DeepCopy.git",
809 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
810 | },
811 | "dist": {
812 | "type": "zip",
813 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
814 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
815 | "shasum": ""
816 | },
817 | "require": {
818 | "php": "^7.1 || ^8.0"
819 | },
820 | "conflict": {
821 | "doctrine/collections": "<1.6.8",
822 | "doctrine/common": "<2.13.3 || >=3,<3.2.2"
823 | },
824 | "require-dev": {
825 | "doctrine/collections": "^1.6.8",
826 | "doctrine/common": "^2.13.3 || ^3.2.2",
827 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
828 | },
829 | "type": "library",
830 | "autoload": {
831 | "files": [
832 | "src/DeepCopy/deep_copy.php"
833 | ],
834 | "psr-4": {
835 | "DeepCopy\\": "src/DeepCopy/"
836 | }
837 | },
838 | "notification-url": "https://packagist.org/downloads/",
839 | "license": [
840 | "MIT"
841 | ],
842 | "description": "Create deep copies (clones) of your objects",
843 | "keywords": [
844 | "clone",
845 | "copy",
846 | "duplicate",
847 | "object",
848 | "object graph"
849 | ],
850 | "support": {
851 | "issues": "https://github.com/myclabs/DeepCopy/issues",
852 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
853 | },
854 | "funding": [
855 | {
856 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
857 | "type": "tidelift"
858 | }
859 | ],
860 | "time": "2023-03-08T13:26:56+00:00"
861 | },
862 | {
863 | "name": "nikic/php-parser",
864 | "version": "v4.15.4",
865 | "source": {
866 | "type": "git",
867 | "url": "https://github.com/nikic/PHP-Parser.git",
868 | "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
869 | },
870 | "dist": {
871 | "type": "zip",
872 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
873 | "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
874 | "shasum": ""
875 | },
876 | "require": {
877 | "ext-tokenizer": "*",
878 | "php": ">=7.0"
879 | },
880 | "require-dev": {
881 | "ircmaxell/php-yacc": "^0.0.7",
882 | "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
883 | },
884 | "bin": [
885 | "bin/php-parse"
886 | ],
887 | "type": "library",
888 | "extra": {
889 | "branch-alias": {
890 | "dev-master": "4.9-dev"
891 | }
892 | },
893 | "autoload": {
894 | "psr-4": {
895 | "PhpParser\\": "lib/PhpParser"
896 | }
897 | },
898 | "notification-url": "https://packagist.org/downloads/",
899 | "license": [
900 | "BSD-3-Clause"
901 | ],
902 | "authors": [
903 | {
904 | "name": "Nikita Popov"
905 | }
906 | ],
907 | "description": "A PHP parser written in PHP",
908 | "keywords": [
909 | "parser",
910 | "php"
911 | ],
912 | "support": {
913 | "issues": "https://github.com/nikic/PHP-Parser/issues",
914 | "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
915 | },
916 | "time": "2023-03-05T19:49:14+00:00"
917 | },
918 | {
919 | "name": "phar-io/manifest",
920 | "version": "2.0.3",
921 | "source": {
922 | "type": "git",
923 | "url": "https://github.com/phar-io/manifest.git",
924 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
925 | },
926 | "dist": {
927 | "type": "zip",
928 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
929 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
930 | "shasum": ""
931 | },
932 | "require": {
933 | "ext-dom": "*",
934 | "ext-phar": "*",
935 | "ext-xmlwriter": "*",
936 | "phar-io/version": "^3.0.1",
937 | "php": "^7.2 || ^8.0"
938 | },
939 | "type": "library",
940 | "extra": {
941 | "branch-alias": {
942 | "dev-master": "2.0.x-dev"
943 | }
944 | },
945 | "autoload": {
946 | "classmap": [
947 | "src/"
948 | ]
949 | },
950 | "notification-url": "https://packagist.org/downloads/",
951 | "license": [
952 | "BSD-3-Clause"
953 | ],
954 | "authors": [
955 | {
956 | "name": "Arne Blankerts",
957 | "email": "arne@blankerts.de",
958 | "role": "Developer"
959 | },
960 | {
961 | "name": "Sebastian Heuer",
962 | "email": "sebastian@phpeople.de",
963 | "role": "Developer"
964 | },
965 | {
966 | "name": "Sebastian Bergmann",
967 | "email": "sebastian@phpunit.de",
968 | "role": "Developer"
969 | }
970 | ],
971 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
972 | "support": {
973 | "issues": "https://github.com/phar-io/manifest/issues",
974 | "source": "https://github.com/phar-io/manifest/tree/2.0.3"
975 | },
976 | "time": "2021-07-20T11:28:43+00:00"
977 | },
978 | {
979 | "name": "phar-io/version",
980 | "version": "3.2.1",
981 | "source": {
982 | "type": "git",
983 | "url": "https://github.com/phar-io/version.git",
984 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
985 | },
986 | "dist": {
987 | "type": "zip",
988 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
989 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
990 | "shasum": ""
991 | },
992 | "require": {
993 | "php": "^7.2 || ^8.0"
994 | },
995 | "type": "library",
996 | "autoload": {
997 | "classmap": [
998 | "src/"
999 | ]
1000 | },
1001 | "notification-url": "https://packagist.org/downloads/",
1002 | "license": [
1003 | "BSD-3-Clause"
1004 | ],
1005 | "authors": [
1006 | {
1007 | "name": "Arne Blankerts",
1008 | "email": "arne@blankerts.de",
1009 | "role": "Developer"
1010 | },
1011 | {
1012 | "name": "Sebastian Heuer",
1013 | "email": "sebastian@phpeople.de",
1014 | "role": "Developer"
1015 | },
1016 | {
1017 | "name": "Sebastian Bergmann",
1018 | "email": "sebastian@phpunit.de",
1019 | "role": "Developer"
1020 | }
1021 | ],
1022 | "description": "Library for handling version information and constraints",
1023 | "support": {
1024 | "issues": "https://github.com/phar-io/version/issues",
1025 | "source": "https://github.com/phar-io/version/tree/3.2.1"
1026 | },
1027 | "time": "2022-02-21T01:04:05+00:00"
1028 | },
1029 | {
1030 | "name": "phpunit/php-code-coverage",
1031 | "version": "9.2.26",
1032 | "source": {
1033 | "type": "git",
1034 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
1035 | "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
1036 | },
1037 | "dist": {
1038 | "type": "zip",
1039 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
1040 | "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
1041 | "shasum": ""
1042 | },
1043 | "require": {
1044 | "ext-dom": "*",
1045 | "ext-libxml": "*",
1046 | "ext-xmlwriter": "*",
1047 | "nikic/php-parser": "^4.15",
1048 | "php": ">=7.3",
1049 | "phpunit/php-file-iterator": "^3.0.3",
1050 | "phpunit/php-text-template": "^2.0.2",
1051 | "sebastian/code-unit-reverse-lookup": "^2.0.2",
1052 | "sebastian/complexity": "^2.0",
1053 | "sebastian/environment": "^5.1.2",
1054 | "sebastian/lines-of-code": "^1.0.3",
1055 | "sebastian/version": "^3.0.1",
1056 | "theseer/tokenizer": "^1.2.0"
1057 | },
1058 | "require-dev": {
1059 | "phpunit/phpunit": "^9.3"
1060 | },
1061 | "suggest": {
1062 | "ext-pcov": "PHP extension that provides line coverage",
1063 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
1064 | },
1065 | "type": "library",
1066 | "extra": {
1067 | "branch-alias": {
1068 | "dev-master": "9.2-dev"
1069 | }
1070 | },
1071 | "autoload": {
1072 | "classmap": [
1073 | "src/"
1074 | ]
1075 | },
1076 | "notification-url": "https://packagist.org/downloads/",
1077 | "license": [
1078 | "BSD-3-Clause"
1079 | ],
1080 | "authors": [
1081 | {
1082 | "name": "Sebastian Bergmann",
1083 | "email": "sebastian@phpunit.de",
1084 | "role": "lead"
1085 | }
1086 | ],
1087 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
1088 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
1089 | "keywords": [
1090 | "coverage",
1091 | "testing",
1092 | "xunit"
1093 | ],
1094 | "support": {
1095 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
1096 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
1097 | },
1098 | "funding": [
1099 | {
1100 | "url": "https://github.com/sebastianbergmann",
1101 | "type": "github"
1102 | }
1103 | ],
1104 | "time": "2023-03-06T12:58:08+00:00"
1105 | },
1106 | {
1107 | "name": "phpunit/php-file-iterator",
1108 | "version": "3.0.6",
1109 | "source": {
1110 | "type": "git",
1111 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
1112 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf"
1113 | },
1114 | "dist": {
1115 | "type": "zip",
1116 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
1117 | "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf",
1118 | "shasum": ""
1119 | },
1120 | "require": {
1121 | "php": ">=7.3"
1122 | },
1123 | "require-dev": {
1124 | "phpunit/phpunit": "^9.3"
1125 | },
1126 | "type": "library",
1127 | "extra": {
1128 | "branch-alias": {
1129 | "dev-master": "3.0-dev"
1130 | }
1131 | },
1132 | "autoload": {
1133 | "classmap": [
1134 | "src/"
1135 | ]
1136 | },
1137 | "notification-url": "https://packagist.org/downloads/",
1138 | "license": [
1139 | "BSD-3-Clause"
1140 | ],
1141 | "authors": [
1142 | {
1143 | "name": "Sebastian Bergmann",
1144 | "email": "sebastian@phpunit.de",
1145 | "role": "lead"
1146 | }
1147 | ],
1148 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
1149 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
1150 | "keywords": [
1151 | "filesystem",
1152 | "iterator"
1153 | ],
1154 | "support": {
1155 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
1156 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6"
1157 | },
1158 | "funding": [
1159 | {
1160 | "url": "https://github.com/sebastianbergmann",
1161 | "type": "github"
1162 | }
1163 | ],
1164 | "time": "2021-12-02T12:48:52+00:00"
1165 | },
1166 | {
1167 | "name": "phpunit/php-invoker",
1168 | "version": "3.1.1",
1169 | "source": {
1170 | "type": "git",
1171 | "url": "https://github.com/sebastianbergmann/php-invoker.git",
1172 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67"
1173 | },
1174 | "dist": {
1175 | "type": "zip",
1176 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
1177 | "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67",
1178 | "shasum": ""
1179 | },
1180 | "require": {
1181 | "php": ">=7.3"
1182 | },
1183 | "require-dev": {
1184 | "ext-pcntl": "*",
1185 | "phpunit/phpunit": "^9.3"
1186 | },
1187 | "suggest": {
1188 | "ext-pcntl": "*"
1189 | },
1190 | "type": "library",
1191 | "extra": {
1192 | "branch-alias": {
1193 | "dev-master": "3.1-dev"
1194 | }
1195 | },
1196 | "autoload": {
1197 | "classmap": [
1198 | "src/"
1199 | ]
1200 | },
1201 | "notification-url": "https://packagist.org/downloads/",
1202 | "license": [
1203 | "BSD-3-Clause"
1204 | ],
1205 | "authors": [
1206 | {
1207 | "name": "Sebastian Bergmann",
1208 | "email": "sebastian@phpunit.de",
1209 | "role": "lead"
1210 | }
1211 | ],
1212 | "description": "Invoke callables with a timeout",
1213 | "homepage": "https://github.com/sebastianbergmann/php-invoker/",
1214 | "keywords": [
1215 | "process"
1216 | ],
1217 | "support": {
1218 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
1219 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1"
1220 | },
1221 | "funding": [
1222 | {
1223 | "url": "https://github.com/sebastianbergmann",
1224 | "type": "github"
1225 | }
1226 | ],
1227 | "time": "2020-09-28T05:58:55+00:00"
1228 | },
1229 | {
1230 | "name": "phpunit/php-text-template",
1231 | "version": "2.0.4",
1232 | "source": {
1233 | "type": "git",
1234 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
1235 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28"
1236 | },
1237 | "dist": {
1238 | "type": "zip",
1239 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
1240 | "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28",
1241 | "shasum": ""
1242 | },
1243 | "require": {
1244 | "php": ">=7.3"
1245 | },
1246 | "require-dev": {
1247 | "phpunit/phpunit": "^9.3"
1248 | },
1249 | "type": "library",
1250 | "extra": {
1251 | "branch-alias": {
1252 | "dev-master": "2.0-dev"
1253 | }
1254 | },
1255 | "autoload": {
1256 | "classmap": [
1257 | "src/"
1258 | ]
1259 | },
1260 | "notification-url": "https://packagist.org/downloads/",
1261 | "license": [
1262 | "BSD-3-Clause"
1263 | ],
1264 | "authors": [
1265 | {
1266 | "name": "Sebastian Bergmann",
1267 | "email": "sebastian@phpunit.de",
1268 | "role": "lead"
1269 | }
1270 | ],
1271 | "description": "Simple template engine.",
1272 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
1273 | "keywords": [
1274 | "template"
1275 | ],
1276 | "support": {
1277 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
1278 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4"
1279 | },
1280 | "funding": [
1281 | {
1282 | "url": "https://github.com/sebastianbergmann",
1283 | "type": "github"
1284 | }
1285 | ],
1286 | "time": "2020-10-26T05:33:50+00:00"
1287 | },
1288 | {
1289 | "name": "phpunit/php-timer",
1290 | "version": "5.0.3",
1291 | "source": {
1292 | "type": "git",
1293 | "url": "https://github.com/sebastianbergmann/php-timer.git",
1294 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2"
1295 | },
1296 | "dist": {
1297 | "type": "zip",
1298 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
1299 | "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2",
1300 | "shasum": ""
1301 | },
1302 | "require": {
1303 | "php": ">=7.3"
1304 | },
1305 | "require-dev": {
1306 | "phpunit/phpunit": "^9.3"
1307 | },
1308 | "type": "library",
1309 | "extra": {
1310 | "branch-alias": {
1311 | "dev-master": "5.0-dev"
1312 | }
1313 | },
1314 | "autoload": {
1315 | "classmap": [
1316 | "src/"
1317 | ]
1318 | },
1319 | "notification-url": "https://packagist.org/downloads/",
1320 | "license": [
1321 | "BSD-3-Clause"
1322 | ],
1323 | "authors": [
1324 | {
1325 | "name": "Sebastian Bergmann",
1326 | "email": "sebastian@phpunit.de",
1327 | "role": "lead"
1328 | }
1329 | ],
1330 | "description": "Utility class for timing",
1331 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
1332 | "keywords": [
1333 | "timer"
1334 | ],
1335 | "support": {
1336 | "issues": "https://github.com/sebastianbergmann/php-timer/issues",
1337 | "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3"
1338 | },
1339 | "funding": [
1340 | {
1341 | "url": "https://github.com/sebastianbergmann",
1342 | "type": "github"
1343 | }
1344 | ],
1345 | "time": "2020-10-26T13:16:10+00:00"
1346 | },
1347 | {
1348 | "name": "phpunit/phpunit",
1349 | "version": "9.6.4",
1350 | "source": {
1351 | "type": "git",
1352 | "url": "https://github.com/sebastianbergmann/phpunit.git",
1353 | "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d"
1354 | },
1355 | "dist": {
1356 | "type": "zip",
1357 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9125ee085b6d95e78277dc07aa1f46f9e0607b8d",
1358 | "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d",
1359 | "shasum": ""
1360 | },
1361 | "require": {
1362 | "doctrine/instantiator": "^1.3.1 || ^2",
1363 | "ext-dom": "*",
1364 | "ext-json": "*",
1365 | "ext-libxml": "*",
1366 | "ext-mbstring": "*",
1367 | "ext-xml": "*",
1368 | "ext-xmlwriter": "*",
1369 | "myclabs/deep-copy": "^1.10.1",
1370 | "phar-io/manifest": "^2.0.3",
1371 | "phar-io/version": "^3.0.2",
1372 | "php": ">=7.3",
1373 | "phpunit/php-code-coverage": "^9.2.13",
1374 | "phpunit/php-file-iterator": "^3.0.5",
1375 | "phpunit/php-invoker": "^3.1.1",
1376 | "phpunit/php-text-template": "^2.0.3",
1377 | "phpunit/php-timer": "^5.0.2",
1378 | "sebastian/cli-parser": "^1.0.1",
1379 | "sebastian/code-unit": "^1.0.6",
1380 | "sebastian/comparator": "^4.0.8",
1381 | "sebastian/diff": "^4.0.3",
1382 | "sebastian/environment": "^5.1.3",
1383 | "sebastian/exporter": "^4.0.5",
1384 | "sebastian/global-state": "^5.0.1",
1385 | "sebastian/object-enumerator": "^4.0.3",
1386 | "sebastian/resource-operations": "^3.0.3",
1387 | "sebastian/type": "^3.2",
1388 | "sebastian/version": "^3.0.2"
1389 | },
1390 | "suggest": {
1391 | "ext-soap": "*",
1392 | "ext-xdebug": "*"
1393 | },
1394 | "bin": [
1395 | "phpunit"
1396 | ],
1397 | "type": "library",
1398 | "extra": {
1399 | "branch-alias": {
1400 | "dev-master": "9.6-dev"
1401 | }
1402 | },
1403 | "autoload": {
1404 | "files": [
1405 | "src/Framework/Assert/Functions.php"
1406 | ],
1407 | "classmap": [
1408 | "src/"
1409 | ]
1410 | },
1411 | "notification-url": "https://packagist.org/downloads/",
1412 | "license": [
1413 | "BSD-3-Clause"
1414 | ],
1415 | "authors": [
1416 | {
1417 | "name": "Sebastian Bergmann",
1418 | "email": "sebastian@phpunit.de",
1419 | "role": "lead"
1420 | }
1421 | ],
1422 | "description": "The PHP Unit Testing framework.",
1423 | "homepage": "https://phpunit.de/",
1424 | "keywords": [
1425 | "phpunit",
1426 | "testing",
1427 | "xunit"
1428 | ],
1429 | "support": {
1430 | "issues": "https://github.com/sebastianbergmann/phpunit/issues",
1431 | "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.4"
1432 | },
1433 | "funding": [
1434 | {
1435 | "url": "https://phpunit.de/sponsors.html",
1436 | "type": "custom"
1437 | },
1438 | {
1439 | "url": "https://github.com/sebastianbergmann",
1440 | "type": "github"
1441 | },
1442 | {
1443 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
1444 | "type": "tidelift"
1445 | }
1446 | ],
1447 | "time": "2023-02-27T13:06:37+00:00"
1448 | },
1449 | {
1450 | "name": "sebastian/cli-parser",
1451 | "version": "1.0.1",
1452 | "source": {
1453 | "type": "git",
1454 | "url": "https://github.com/sebastianbergmann/cli-parser.git",
1455 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2"
1456 | },
1457 | "dist": {
1458 | "type": "zip",
1459 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2",
1460 | "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2",
1461 | "shasum": ""
1462 | },
1463 | "require": {
1464 | "php": ">=7.3"
1465 | },
1466 | "require-dev": {
1467 | "phpunit/phpunit": "^9.3"
1468 | },
1469 | "type": "library",
1470 | "extra": {
1471 | "branch-alias": {
1472 | "dev-master": "1.0-dev"
1473 | }
1474 | },
1475 | "autoload": {
1476 | "classmap": [
1477 | "src/"
1478 | ]
1479 | },
1480 | "notification-url": "https://packagist.org/downloads/",
1481 | "license": [
1482 | "BSD-3-Clause"
1483 | ],
1484 | "authors": [
1485 | {
1486 | "name": "Sebastian Bergmann",
1487 | "email": "sebastian@phpunit.de",
1488 | "role": "lead"
1489 | }
1490 | ],
1491 | "description": "Library for parsing CLI options",
1492 | "homepage": "https://github.com/sebastianbergmann/cli-parser",
1493 | "support": {
1494 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
1495 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1"
1496 | },
1497 | "funding": [
1498 | {
1499 | "url": "https://github.com/sebastianbergmann",
1500 | "type": "github"
1501 | }
1502 | ],
1503 | "time": "2020-09-28T06:08:49+00:00"
1504 | },
1505 | {
1506 | "name": "sebastian/code-unit",
1507 | "version": "1.0.8",
1508 | "source": {
1509 | "type": "git",
1510 | "url": "https://github.com/sebastianbergmann/code-unit.git",
1511 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120"
1512 | },
1513 | "dist": {
1514 | "type": "zip",
1515 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120",
1516 | "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120",
1517 | "shasum": ""
1518 | },
1519 | "require": {
1520 | "php": ">=7.3"
1521 | },
1522 | "require-dev": {
1523 | "phpunit/phpunit": "^9.3"
1524 | },
1525 | "type": "library",
1526 | "extra": {
1527 | "branch-alias": {
1528 | "dev-master": "1.0-dev"
1529 | }
1530 | },
1531 | "autoload": {
1532 | "classmap": [
1533 | "src/"
1534 | ]
1535 | },
1536 | "notification-url": "https://packagist.org/downloads/",
1537 | "license": [
1538 | "BSD-3-Clause"
1539 | ],
1540 | "authors": [
1541 | {
1542 | "name": "Sebastian Bergmann",
1543 | "email": "sebastian@phpunit.de",
1544 | "role": "lead"
1545 | }
1546 | ],
1547 | "description": "Collection of value objects that represent the PHP code units",
1548 | "homepage": "https://github.com/sebastianbergmann/code-unit",
1549 | "support": {
1550 | "issues": "https://github.com/sebastianbergmann/code-unit/issues",
1551 | "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8"
1552 | },
1553 | "funding": [
1554 | {
1555 | "url": "https://github.com/sebastianbergmann",
1556 | "type": "github"
1557 | }
1558 | ],
1559 | "time": "2020-10-26T13:08:54+00:00"
1560 | },
1561 | {
1562 | "name": "sebastian/code-unit-reverse-lookup",
1563 | "version": "2.0.3",
1564 | "source": {
1565 | "type": "git",
1566 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
1567 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5"
1568 | },
1569 | "dist": {
1570 | "type": "zip",
1571 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
1572 | "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5",
1573 | "shasum": ""
1574 | },
1575 | "require": {
1576 | "php": ">=7.3"
1577 | },
1578 | "require-dev": {
1579 | "phpunit/phpunit": "^9.3"
1580 | },
1581 | "type": "library",
1582 | "extra": {
1583 | "branch-alias": {
1584 | "dev-master": "2.0-dev"
1585 | }
1586 | },
1587 | "autoload": {
1588 | "classmap": [
1589 | "src/"
1590 | ]
1591 | },
1592 | "notification-url": "https://packagist.org/downloads/",
1593 | "license": [
1594 | "BSD-3-Clause"
1595 | ],
1596 | "authors": [
1597 | {
1598 | "name": "Sebastian Bergmann",
1599 | "email": "sebastian@phpunit.de"
1600 | }
1601 | ],
1602 | "description": "Looks up which function or method a line of code belongs to",
1603 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
1604 | "support": {
1605 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
1606 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3"
1607 | },
1608 | "funding": [
1609 | {
1610 | "url": "https://github.com/sebastianbergmann",
1611 | "type": "github"
1612 | }
1613 | ],
1614 | "time": "2020-09-28T05:30:19+00:00"
1615 | },
1616 | {
1617 | "name": "sebastian/comparator",
1618 | "version": "4.0.8",
1619 | "source": {
1620 | "type": "git",
1621 | "url": "https://github.com/sebastianbergmann/comparator.git",
1622 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a"
1623 | },
1624 | "dist": {
1625 | "type": "zip",
1626 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a",
1627 | "reference": "fa0f136dd2334583309d32b62544682ee972b51a",
1628 | "shasum": ""
1629 | },
1630 | "require": {
1631 | "php": ">=7.3",
1632 | "sebastian/diff": "^4.0",
1633 | "sebastian/exporter": "^4.0"
1634 | },
1635 | "require-dev": {
1636 | "phpunit/phpunit": "^9.3"
1637 | },
1638 | "type": "library",
1639 | "extra": {
1640 | "branch-alias": {
1641 | "dev-master": "4.0-dev"
1642 | }
1643 | },
1644 | "autoload": {
1645 | "classmap": [
1646 | "src/"
1647 | ]
1648 | },
1649 | "notification-url": "https://packagist.org/downloads/",
1650 | "license": [
1651 | "BSD-3-Clause"
1652 | ],
1653 | "authors": [
1654 | {
1655 | "name": "Sebastian Bergmann",
1656 | "email": "sebastian@phpunit.de"
1657 | },
1658 | {
1659 | "name": "Jeff Welch",
1660 | "email": "whatthejeff@gmail.com"
1661 | },
1662 | {
1663 | "name": "Volker Dusch",
1664 | "email": "github@wallbash.com"
1665 | },
1666 | {
1667 | "name": "Bernhard Schussek",
1668 | "email": "bschussek@2bepublished.at"
1669 | }
1670 | ],
1671 | "description": "Provides the functionality to compare PHP values for equality",
1672 | "homepage": "https://github.com/sebastianbergmann/comparator",
1673 | "keywords": [
1674 | "comparator",
1675 | "compare",
1676 | "equality"
1677 | ],
1678 | "support": {
1679 | "issues": "https://github.com/sebastianbergmann/comparator/issues",
1680 | "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8"
1681 | },
1682 | "funding": [
1683 | {
1684 | "url": "https://github.com/sebastianbergmann",
1685 | "type": "github"
1686 | }
1687 | ],
1688 | "time": "2022-09-14T12:41:17+00:00"
1689 | },
1690 | {
1691 | "name": "sebastian/complexity",
1692 | "version": "2.0.2",
1693 | "source": {
1694 | "type": "git",
1695 | "url": "https://github.com/sebastianbergmann/complexity.git",
1696 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
1697 | },
1698 | "dist": {
1699 | "type": "zip",
1700 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
1701 | "reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
1702 | "shasum": ""
1703 | },
1704 | "require": {
1705 | "nikic/php-parser": "^4.7",
1706 | "php": ">=7.3"
1707 | },
1708 | "require-dev": {
1709 | "phpunit/phpunit": "^9.3"
1710 | },
1711 | "type": "library",
1712 | "extra": {
1713 | "branch-alias": {
1714 | "dev-master": "2.0-dev"
1715 | }
1716 | },
1717 | "autoload": {
1718 | "classmap": [
1719 | "src/"
1720 | ]
1721 | },
1722 | "notification-url": "https://packagist.org/downloads/",
1723 | "license": [
1724 | "BSD-3-Clause"
1725 | ],
1726 | "authors": [
1727 | {
1728 | "name": "Sebastian Bergmann",
1729 | "email": "sebastian@phpunit.de",
1730 | "role": "lead"
1731 | }
1732 | ],
1733 | "description": "Library for calculating the complexity of PHP code units",
1734 | "homepage": "https://github.com/sebastianbergmann/complexity",
1735 | "support": {
1736 | "issues": "https://github.com/sebastianbergmann/complexity/issues",
1737 | "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
1738 | },
1739 | "funding": [
1740 | {
1741 | "url": "https://github.com/sebastianbergmann",
1742 | "type": "github"
1743 | }
1744 | ],
1745 | "time": "2020-10-26T15:52:27+00:00"
1746 | },
1747 | {
1748 | "name": "sebastian/diff",
1749 | "version": "4.0.4",
1750 | "source": {
1751 | "type": "git",
1752 | "url": "https://github.com/sebastianbergmann/diff.git",
1753 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
1754 | },
1755 | "dist": {
1756 | "type": "zip",
1757 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
1758 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
1759 | "shasum": ""
1760 | },
1761 | "require": {
1762 | "php": ">=7.3"
1763 | },
1764 | "require-dev": {
1765 | "phpunit/phpunit": "^9.3",
1766 | "symfony/process": "^4.2 || ^5"
1767 | },
1768 | "type": "library",
1769 | "extra": {
1770 | "branch-alias": {
1771 | "dev-master": "4.0-dev"
1772 | }
1773 | },
1774 | "autoload": {
1775 | "classmap": [
1776 | "src/"
1777 | ]
1778 | },
1779 | "notification-url": "https://packagist.org/downloads/",
1780 | "license": [
1781 | "BSD-3-Clause"
1782 | ],
1783 | "authors": [
1784 | {
1785 | "name": "Sebastian Bergmann",
1786 | "email": "sebastian@phpunit.de"
1787 | },
1788 | {
1789 | "name": "Kore Nordmann",
1790 | "email": "mail@kore-nordmann.de"
1791 | }
1792 | ],
1793 | "description": "Diff implementation",
1794 | "homepage": "https://github.com/sebastianbergmann/diff",
1795 | "keywords": [
1796 | "diff",
1797 | "udiff",
1798 | "unidiff",
1799 | "unified diff"
1800 | ],
1801 | "support": {
1802 | "issues": "https://github.com/sebastianbergmann/diff/issues",
1803 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
1804 | },
1805 | "funding": [
1806 | {
1807 | "url": "https://github.com/sebastianbergmann",
1808 | "type": "github"
1809 | }
1810 | ],
1811 | "time": "2020-10-26T13:10:38+00:00"
1812 | },
1813 | {
1814 | "name": "sebastian/environment",
1815 | "version": "5.1.5",
1816 | "source": {
1817 | "type": "git",
1818 | "url": "https://github.com/sebastianbergmann/environment.git",
1819 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed"
1820 | },
1821 | "dist": {
1822 | "type": "zip",
1823 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
1824 | "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed",
1825 | "shasum": ""
1826 | },
1827 | "require": {
1828 | "php": ">=7.3"
1829 | },
1830 | "require-dev": {
1831 | "phpunit/phpunit": "^9.3"
1832 | },
1833 | "suggest": {
1834 | "ext-posix": "*"
1835 | },
1836 | "type": "library",
1837 | "extra": {
1838 | "branch-alias": {
1839 | "dev-master": "5.1-dev"
1840 | }
1841 | },
1842 | "autoload": {
1843 | "classmap": [
1844 | "src/"
1845 | ]
1846 | },
1847 | "notification-url": "https://packagist.org/downloads/",
1848 | "license": [
1849 | "BSD-3-Clause"
1850 | ],
1851 | "authors": [
1852 | {
1853 | "name": "Sebastian Bergmann",
1854 | "email": "sebastian@phpunit.de"
1855 | }
1856 | ],
1857 | "description": "Provides functionality to handle HHVM/PHP environments",
1858 | "homepage": "http://www.github.com/sebastianbergmann/environment",
1859 | "keywords": [
1860 | "Xdebug",
1861 | "environment",
1862 | "hhvm"
1863 | ],
1864 | "support": {
1865 | "issues": "https://github.com/sebastianbergmann/environment/issues",
1866 | "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5"
1867 | },
1868 | "funding": [
1869 | {
1870 | "url": "https://github.com/sebastianbergmann",
1871 | "type": "github"
1872 | }
1873 | ],
1874 | "time": "2023-02-03T06:03:51+00:00"
1875 | },
1876 | {
1877 | "name": "sebastian/exporter",
1878 | "version": "4.0.5",
1879 | "source": {
1880 | "type": "git",
1881 | "url": "https://github.com/sebastianbergmann/exporter.git",
1882 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d"
1883 | },
1884 | "dist": {
1885 | "type": "zip",
1886 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
1887 | "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d",
1888 | "shasum": ""
1889 | },
1890 | "require": {
1891 | "php": ">=7.3",
1892 | "sebastian/recursion-context": "^4.0"
1893 | },
1894 | "require-dev": {
1895 | "ext-mbstring": "*",
1896 | "phpunit/phpunit": "^9.3"
1897 | },
1898 | "type": "library",
1899 | "extra": {
1900 | "branch-alias": {
1901 | "dev-master": "4.0-dev"
1902 | }
1903 | },
1904 | "autoload": {
1905 | "classmap": [
1906 | "src/"
1907 | ]
1908 | },
1909 | "notification-url": "https://packagist.org/downloads/",
1910 | "license": [
1911 | "BSD-3-Clause"
1912 | ],
1913 | "authors": [
1914 | {
1915 | "name": "Sebastian Bergmann",
1916 | "email": "sebastian@phpunit.de"
1917 | },
1918 | {
1919 | "name": "Jeff Welch",
1920 | "email": "whatthejeff@gmail.com"
1921 | },
1922 | {
1923 | "name": "Volker Dusch",
1924 | "email": "github@wallbash.com"
1925 | },
1926 | {
1927 | "name": "Adam Harvey",
1928 | "email": "aharvey@php.net"
1929 | },
1930 | {
1931 | "name": "Bernhard Schussek",
1932 | "email": "bschussek@gmail.com"
1933 | }
1934 | ],
1935 | "description": "Provides the functionality to export PHP variables for visualization",
1936 | "homepage": "https://www.github.com/sebastianbergmann/exporter",
1937 | "keywords": [
1938 | "export",
1939 | "exporter"
1940 | ],
1941 | "support": {
1942 | "issues": "https://github.com/sebastianbergmann/exporter/issues",
1943 | "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5"
1944 | },
1945 | "funding": [
1946 | {
1947 | "url": "https://github.com/sebastianbergmann",
1948 | "type": "github"
1949 | }
1950 | ],
1951 | "time": "2022-09-14T06:03:37+00:00"
1952 | },
1953 | {
1954 | "name": "sebastian/global-state",
1955 | "version": "5.0.5",
1956 | "source": {
1957 | "type": "git",
1958 | "url": "https://github.com/sebastianbergmann/global-state.git",
1959 | "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
1960 | },
1961 | "dist": {
1962 | "type": "zip",
1963 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
1964 | "reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
1965 | "shasum": ""
1966 | },
1967 | "require": {
1968 | "php": ">=7.3",
1969 | "sebastian/object-reflector": "^2.0",
1970 | "sebastian/recursion-context": "^4.0"
1971 | },
1972 | "require-dev": {
1973 | "ext-dom": "*",
1974 | "phpunit/phpunit": "^9.3"
1975 | },
1976 | "suggest": {
1977 | "ext-uopz": "*"
1978 | },
1979 | "type": "library",
1980 | "extra": {
1981 | "branch-alias": {
1982 | "dev-master": "5.0-dev"
1983 | }
1984 | },
1985 | "autoload": {
1986 | "classmap": [
1987 | "src/"
1988 | ]
1989 | },
1990 | "notification-url": "https://packagist.org/downloads/",
1991 | "license": [
1992 | "BSD-3-Clause"
1993 | ],
1994 | "authors": [
1995 | {
1996 | "name": "Sebastian Bergmann",
1997 | "email": "sebastian@phpunit.de"
1998 | }
1999 | ],
2000 | "description": "Snapshotting of global state",
2001 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
2002 | "keywords": [
2003 | "global state"
2004 | ],
2005 | "support": {
2006 | "issues": "https://github.com/sebastianbergmann/global-state/issues",
2007 | "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.5"
2008 | },
2009 | "funding": [
2010 | {
2011 | "url": "https://github.com/sebastianbergmann",
2012 | "type": "github"
2013 | }
2014 | ],
2015 | "time": "2022-02-14T08:28:10+00:00"
2016 | },
2017 | {
2018 | "name": "sebastian/lines-of-code",
2019 | "version": "1.0.3",
2020 | "source": {
2021 | "type": "git",
2022 | "url": "https://github.com/sebastianbergmann/lines-of-code.git",
2023 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
2024 | },
2025 | "dist": {
2026 | "type": "zip",
2027 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
2028 | "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
2029 | "shasum": ""
2030 | },
2031 | "require": {
2032 | "nikic/php-parser": "^4.6",
2033 | "php": ">=7.3"
2034 | },
2035 | "require-dev": {
2036 | "phpunit/phpunit": "^9.3"
2037 | },
2038 | "type": "library",
2039 | "extra": {
2040 | "branch-alias": {
2041 | "dev-master": "1.0-dev"
2042 | }
2043 | },
2044 | "autoload": {
2045 | "classmap": [
2046 | "src/"
2047 | ]
2048 | },
2049 | "notification-url": "https://packagist.org/downloads/",
2050 | "license": [
2051 | "BSD-3-Clause"
2052 | ],
2053 | "authors": [
2054 | {
2055 | "name": "Sebastian Bergmann",
2056 | "email": "sebastian@phpunit.de",
2057 | "role": "lead"
2058 | }
2059 | ],
2060 | "description": "Library for counting the lines of code in PHP source code",
2061 | "homepage": "https://github.com/sebastianbergmann/lines-of-code",
2062 | "support": {
2063 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
2064 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
2065 | },
2066 | "funding": [
2067 | {
2068 | "url": "https://github.com/sebastianbergmann",
2069 | "type": "github"
2070 | }
2071 | ],
2072 | "time": "2020-11-28T06:42:11+00:00"
2073 | },
2074 | {
2075 | "name": "sebastian/object-enumerator",
2076 | "version": "4.0.4",
2077 | "source": {
2078 | "type": "git",
2079 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
2080 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71"
2081 | },
2082 | "dist": {
2083 | "type": "zip",
2084 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71",
2085 | "reference": "5c9eeac41b290a3712d88851518825ad78f45c71",
2086 | "shasum": ""
2087 | },
2088 | "require": {
2089 | "php": ">=7.3",
2090 | "sebastian/object-reflector": "^2.0",
2091 | "sebastian/recursion-context": "^4.0"
2092 | },
2093 | "require-dev": {
2094 | "phpunit/phpunit": "^9.3"
2095 | },
2096 | "type": "library",
2097 | "extra": {
2098 | "branch-alias": {
2099 | "dev-master": "4.0-dev"
2100 | }
2101 | },
2102 | "autoload": {
2103 | "classmap": [
2104 | "src/"
2105 | ]
2106 | },
2107 | "notification-url": "https://packagist.org/downloads/",
2108 | "license": [
2109 | "BSD-3-Clause"
2110 | ],
2111 | "authors": [
2112 | {
2113 | "name": "Sebastian Bergmann",
2114 | "email": "sebastian@phpunit.de"
2115 | }
2116 | ],
2117 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
2118 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
2119 | "support": {
2120 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
2121 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4"
2122 | },
2123 | "funding": [
2124 | {
2125 | "url": "https://github.com/sebastianbergmann",
2126 | "type": "github"
2127 | }
2128 | ],
2129 | "time": "2020-10-26T13:12:34+00:00"
2130 | },
2131 | {
2132 | "name": "sebastian/object-reflector",
2133 | "version": "2.0.4",
2134 | "source": {
2135 | "type": "git",
2136 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
2137 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7"
2138 | },
2139 | "dist": {
2140 | "type": "zip",
2141 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
2142 | "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7",
2143 | "shasum": ""
2144 | },
2145 | "require": {
2146 | "php": ">=7.3"
2147 | },
2148 | "require-dev": {
2149 | "phpunit/phpunit": "^9.3"
2150 | },
2151 | "type": "library",
2152 | "extra": {
2153 | "branch-alias": {
2154 | "dev-master": "2.0-dev"
2155 | }
2156 | },
2157 | "autoload": {
2158 | "classmap": [
2159 | "src/"
2160 | ]
2161 | },
2162 | "notification-url": "https://packagist.org/downloads/",
2163 | "license": [
2164 | "BSD-3-Clause"
2165 | ],
2166 | "authors": [
2167 | {
2168 | "name": "Sebastian Bergmann",
2169 | "email": "sebastian@phpunit.de"
2170 | }
2171 | ],
2172 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
2173 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
2174 | "support": {
2175 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
2176 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4"
2177 | },
2178 | "funding": [
2179 | {
2180 | "url": "https://github.com/sebastianbergmann",
2181 | "type": "github"
2182 | }
2183 | ],
2184 | "time": "2020-10-26T13:14:26+00:00"
2185 | },
2186 | {
2187 | "name": "sebastian/recursion-context",
2188 | "version": "4.0.5",
2189 | "source": {
2190 | "type": "git",
2191 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
2192 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1"
2193 | },
2194 | "dist": {
2195 | "type": "zip",
2196 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
2197 | "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1",
2198 | "shasum": ""
2199 | },
2200 | "require": {
2201 | "php": ">=7.3"
2202 | },
2203 | "require-dev": {
2204 | "phpunit/phpunit": "^9.3"
2205 | },
2206 | "type": "library",
2207 | "extra": {
2208 | "branch-alias": {
2209 | "dev-master": "4.0-dev"
2210 | }
2211 | },
2212 | "autoload": {
2213 | "classmap": [
2214 | "src/"
2215 | ]
2216 | },
2217 | "notification-url": "https://packagist.org/downloads/",
2218 | "license": [
2219 | "BSD-3-Clause"
2220 | ],
2221 | "authors": [
2222 | {
2223 | "name": "Sebastian Bergmann",
2224 | "email": "sebastian@phpunit.de"
2225 | },
2226 | {
2227 | "name": "Jeff Welch",
2228 | "email": "whatthejeff@gmail.com"
2229 | },
2230 | {
2231 | "name": "Adam Harvey",
2232 | "email": "aharvey@php.net"
2233 | }
2234 | ],
2235 | "description": "Provides functionality to recursively process PHP variables",
2236 | "homepage": "https://github.com/sebastianbergmann/recursion-context",
2237 | "support": {
2238 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
2239 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5"
2240 | },
2241 | "funding": [
2242 | {
2243 | "url": "https://github.com/sebastianbergmann",
2244 | "type": "github"
2245 | }
2246 | ],
2247 | "time": "2023-02-03T06:07:39+00:00"
2248 | },
2249 | {
2250 | "name": "sebastian/resource-operations",
2251 | "version": "3.0.3",
2252 | "source": {
2253 | "type": "git",
2254 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
2255 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
2256 | },
2257 | "dist": {
2258 | "type": "zip",
2259 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
2260 | "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
2261 | "shasum": ""
2262 | },
2263 | "require": {
2264 | "php": ">=7.3"
2265 | },
2266 | "require-dev": {
2267 | "phpunit/phpunit": "^9.0"
2268 | },
2269 | "type": "library",
2270 | "extra": {
2271 | "branch-alias": {
2272 | "dev-master": "3.0-dev"
2273 | }
2274 | },
2275 | "autoload": {
2276 | "classmap": [
2277 | "src/"
2278 | ]
2279 | },
2280 | "notification-url": "https://packagist.org/downloads/",
2281 | "license": [
2282 | "BSD-3-Clause"
2283 | ],
2284 | "authors": [
2285 | {
2286 | "name": "Sebastian Bergmann",
2287 | "email": "sebastian@phpunit.de"
2288 | }
2289 | ],
2290 | "description": "Provides a list of PHP built-in functions that operate on resources",
2291 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
2292 | "support": {
2293 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
2294 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
2295 | },
2296 | "funding": [
2297 | {
2298 | "url": "https://github.com/sebastianbergmann",
2299 | "type": "github"
2300 | }
2301 | ],
2302 | "time": "2020-09-28T06:45:17+00:00"
2303 | },
2304 | {
2305 | "name": "sebastian/type",
2306 | "version": "3.2.1",
2307 | "source": {
2308 | "type": "git",
2309 | "url": "https://github.com/sebastianbergmann/type.git",
2310 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7"
2311 | },
2312 | "dist": {
2313 | "type": "zip",
2314 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
2315 | "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7",
2316 | "shasum": ""
2317 | },
2318 | "require": {
2319 | "php": ">=7.3"
2320 | },
2321 | "require-dev": {
2322 | "phpunit/phpunit": "^9.5"
2323 | },
2324 | "type": "library",
2325 | "extra": {
2326 | "branch-alias": {
2327 | "dev-master": "3.2-dev"
2328 | }
2329 | },
2330 | "autoload": {
2331 | "classmap": [
2332 | "src/"
2333 | ]
2334 | },
2335 | "notification-url": "https://packagist.org/downloads/",
2336 | "license": [
2337 | "BSD-3-Clause"
2338 | ],
2339 | "authors": [
2340 | {
2341 | "name": "Sebastian Bergmann",
2342 | "email": "sebastian@phpunit.de",
2343 | "role": "lead"
2344 | }
2345 | ],
2346 | "description": "Collection of value objects that represent the types of the PHP type system",
2347 | "homepage": "https://github.com/sebastianbergmann/type",
2348 | "support": {
2349 | "issues": "https://github.com/sebastianbergmann/type/issues",
2350 | "source": "https://github.com/sebastianbergmann/type/tree/3.2.1"
2351 | },
2352 | "funding": [
2353 | {
2354 | "url": "https://github.com/sebastianbergmann",
2355 | "type": "github"
2356 | }
2357 | ],
2358 | "time": "2023-02-03T06:13:03+00:00"
2359 | },
2360 | {
2361 | "name": "sebastian/version",
2362 | "version": "3.0.2",
2363 | "source": {
2364 | "type": "git",
2365 | "url": "https://github.com/sebastianbergmann/version.git",
2366 | "reference": "c6c1022351a901512170118436c764e473f6de8c"
2367 | },
2368 | "dist": {
2369 | "type": "zip",
2370 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c",
2371 | "reference": "c6c1022351a901512170118436c764e473f6de8c",
2372 | "shasum": ""
2373 | },
2374 | "require": {
2375 | "php": ">=7.3"
2376 | },
2377 | "type": "library",
2378 | "extra": {
2379 | "branch-alias": {
2380 | "dev-master": "3.0-dev"
2381 | }
2382 | },
2383 | "autoload": {
2384 | "classmap": [
2385 | "src/"
2386 | ]
2387 | },
2388 | "notification-url": "https://packagist.org/downloads/",
2389 | "license": [
2390 | "BSD-3-Clause"
2391 | ],
2392 | "authors": [
2393 | {
2394 | "name": "Sebastian Bergmann",
2395 | "email": "sebastian@phpunit.de",
2396 | "role": "lead"
2397 | }
2398 | ],
2399 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
2400 | "homepage": "https://github.com/sebastianbergmann/version",
2401 | "support": {
2402 | "issues": "https://github.com/sebastianbergmann/version/issues",
2403 | "source": "https://github.com/sebastianbergmann/version/tree/3.0.2"
2404 | },
2405 | "funding": [
2406 | {
2407 | "url": "https://github.com/sebastianbergmann",
2408 | "type": "github"
2409 | }
2410 | ],
2411 | "time": "2020-09-28T06:39:44+00:00"
2412 | },
2413 | {
2414 | "name": "theseer/tokenizer",
2415 | "version": "1.2.1",
2416 | "source": {
2417 | "type": "git",
2418 | "url": "https://github.com/theseer/tokenizer.git",
2419 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
2420 | },
2421 | "dist": {
2422 | "type": "zip",
2423 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
2424 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
2425 | "shasum": ""
2426 | },
2427 | "require": {
2428 | "ext-dom": "*",
2429 | "ext-tokenizer": "*",
2430 | "ext-xmlwriter": "*",
2431 | "php": "^7.2 || ^8.0"
2432 | },
2433 | "type": "library",
2434 | "autoload": {
2435 | "classmap": [
2436 | "src/"
2437 | ]
2438 | },
2439 | "notification-url": "https://packagist.org/downloads/",
2440 | "license": [
2441 | "BSD-3-Clause"
2442 | ],
2443 | "authors": [
2444 | {
2445 | "name": "Arne Blankerts",
2446 | "email": "arne@blankerts.de",
2447 | "role": "Developer"
2448 | }
2449 | ],
2450 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
2451 | "support": {
2452 | "issues": "https://github.com/theseer/tokenizer/issues",
2453 | "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
2454 | },
2455 | "funding": [
2456 | {
2457 | "url": "https://github.com/theseer",
2458 | "type": "github"
2459 | }
2460 | ],
2461 | "time": "2021-07-28T10:34:58+00:00"
2462 | }
2463 | ],
2464 | "aliases": [],
2465 | "minimum-stability": "stable",
2466 | "stability-flags": [],
2467 | "prefer-stable": false,
2468 | "prefer-lowest": false,
2469 | "platform": {
2470 | "php": "^7.4|^8.0",
2471 | "ext-json": "*"
2472 | },
2473 | "platform-dev": [],
2474 | "plugin-api-version": "2.3.0"
2475 | }
2476 |
--------------------------------------------------------------------------------