├── .gitignore
├── LICENSE
├── README.md
├── bootstrap
├── build.sh
├── composer.json
├── composer.lock
├── php.ini
├── phpunit.xml.dist
└── tests
├── BootstrapTest.php
├── _files
└── public
│ ├── index.php
│ └── subdir
│ └── index.php
└── php.ini
/.gitignore:
--------------------------------------------------------------------------------
1 | php73.zip
2 | php74.zip
3 | phpunit.xml
4 | vendor
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------
2 | The PHP License, version 3.01
3 | Copyright (c) 1999 - 2019 The PHP Group. All rights reserved.
4 | --------------------------------------------------------------------
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, is permitted provided that the following conditions
8 | are met:
9 |
10 | 1. Redistributions of source code must retain the above copyright
11 | notice, this list of conditions and the following disclaimer.
12 |
13 | 2. Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in
15 | the documentation and/or other materials provided with the
16 | distribution.
17 |
18 | 3. The name "PHP" must not be used to endorse or promote products
19 | derived from this software without prior written permission. For
20 | written permission, please contact group@php.net.
21 |
22 | 4. Products derived from this software may not be called "PHP", nor
23 | may "PHP" appear in their name, without prior written permission
24 | from group@php.net. You may indicate that your software works in
25 | conjunction with PHP by saying "Foo for PHP" instead of calling
26 | it "PHP Foo" or "phpfoo"
27 |
28 | 5. The PHP Group may publish revised and/or new versions of the
29 | license from time to time. Each version will be given a
30 | distinguishing version number.
31 | Once covered code has been published under a particular version
32 | of the license, you may always continue to use it under the terms
33 | of that version. You may also choose to use such covered code
34 | under the terms of any subsequent version of the license
35 | published by the PHP Group. No one other than the PHP Group has
36 | the right to modify the terms applicable to covered code created
37 | under this License.
38 |
39 | 6. Redistributions of any form whatsoever must retain the following
40 | acknowledgment:
41 | "This product includes PHP software, freely available from
42 | ".
43 |
44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
55 | OF THE POSSIBILITY OF SUCH DAMAGE.
56 |
57 | --------------------------------------------------------------------
58 |
59 | This software consists of voluntary contributions made by many
60 | individuals on behalf of the PHP Group.
61 |
62 | The PHP Group can be contacted via Email at group@php.net.
63 |
64 | For more information on the PHP Group and the PHP project,
65 | please see .
66 |
67 | PHP includes the Zend Engine, freely available at
68 | .
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP Runtime Layer for AWS Lambda
2 |
3 | This runtime layer aims to replicate the typical web server environment for executing PHP scripts within AWS Lambda, so that an Lambda function can be used as an alternative hosting environment for a PHP-based site.
4 |
5 | This layer utilises the PHP CGI runtime to replicate the environment offered in other runtime setups such as PHP FPM.
6 |
7 | ## PHP 8
8 |
9 | This repo has been updated to PHP 8.0 and with the intention of fully supporting the `arm64` Lambda environment.
10 |
11 | Because of this this version now uses Amazon Linux Extras for a PHP 8.0 build. At the time of development, despite the stable release of 8.1, no repositories for x86 and arm64 builds of PHP 8.x were available. Should this change in the future it is hoped to update this project accordingly.
12 |
13 | ## Usage
14 |
15 | ### General Usage
16 |
17 | A Lambda function using this runtime layer is intended to sit behind either an Application Load Balancer or API Gateway, providing an HTTP interface in to the function.
18 |
19 | The layer runs a PHP CGI process for each incoming request, executing either a PHP script whose path matches the incoming HTTP request or alternatively using the script at the path configured as the handler for the Lambda function.
20 |
21 | The bootstrap is responsible for obtaining an incoming request, re-formatting it from the ALB/Gateway event object in to a format that the PHP process understands, executing the script, then re-formatting the response back to a format that AWS can return to the originating requester.
22 |
23 | ### Configuration
24 |
25 | The layer will attempt to load `php.ini` from inside your Lambda function distribution at the root level.
26 |
27 | You can enable access logging by declaring the environment variable `ACCESS_LOG` and setting the value to `true`.
28 |
29 | You can optionally format the output by declaring the `ACCESS_FORMAT` variable. It can take the following arguments:
30 |
31 | ```
32 | %m: request method
33 | %r: the request URI (without the query string, see %q and %Q)
34 | %Q: the '?' character if query string exists
35 | %q: the query string
36 | %s: status (response code)
37 | %f: script filename
38 | %d: time taken to serve the request in seconds
39 | %e: an environment variable (same as $_ENV or $_SERVER)
40 | it must be associated with embraces to specify the name of the env
41 | variable. Some exemples:
42 | - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
43 | - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
44 | ```
45 |
46 | The default value is `"%m %r%Q%q" %s %f %d`.
47 |
48 | ### Extensions
49 | The following extensions are built into the layer and available in `/opt/lib/php/8.0/modules`:
50 |
51 | ```
52 | bz2.so
53 | calendar.so
54 | ctype.so
55 | curl.so
56 | dom.so
57 | exif.so
58 | fileinfo.so
59 | ftp.so
60 | gettext.so
61 | iconv.so
62 | mbstring.so
63 | mysqli.so
64 | mysqlnd.so
65 | pdo.so
66 | pdo_mysql.so
67 | pdo_sqlite.so
68 | phar.so
69 | simplexml.so
70 | sockets.so
71 | sqlite3.so
72 | tokenizer.so
73 | xml.so
74 | xmlreader.so
75 | xmlwriter.so
76 | xsl.so
77 | zip.so
78 | ```
79 |
80 | These extensions are not loaded by default. You must add the extension to a php.ini file to use it:
81 |
82 | ```ini
83 | extension=dom.so
84 | ```
85 |
86 | It is recommended that custom extensions be provided by a separate Lambda Layer with the extension .so files placed in `/lib/php/8.0/modules/` so they can be loaded alongside the built-in extensions listed above.
87 |
88 | ### Amazon Linux 2
89 |
90 | The PHP 8.0 layer is targeted to an environment running Amazon Linux 2, which at the time of development was the default environment for Lambda functions.
91 |
92 | When the build script is run under an arm64 environment, it will produce a layer suitable for running within an arm64 Lambda. Similarly running under x86 will provide an x86 layer.
93 |
94 | ## Development
95 |
96 | ### Building
97 |
98 | To build the layer zip package you will need to launch an EC2 instance running Amazon Linux v2. Choose the architecture based on which you wish to use for your final Lambda functions.
99 |
100 | Once your EC2 instance is booted and you have a copy of this repo available, run the `build.sh` script.
101 |
102 | This will build a layer zip file (`php80.zip`) for you in the current directory.
103 |
104 | ### Testing
105 |
106 | A basic PHPUnit functional test case is provided for the bootstrap. You can run this locally although this will target your local machines PHP environment.
107 |
108 | ```
109 | composer test
110 | ```
111 |
112 | This assumes you have PHP and composer setup in your local environment.
113 |
114 | ## Disclaimer
115 |
116 | > THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
117 | > ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
118 | > THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
119 | > PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
120 | > DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
121 | > INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
122 | > (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
123 | > SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
124 | > HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
125 | > STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
126 | > ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
127 | > OF THE POSSIBILITY OF SUCH DAMAGE.
128 |
--------------------------------------------------------------------------------
/bootstrap:
--------------------------------------------------------------------------------
1 | #!/opt/bin/php -c/opt/php.ini
2 | 'POST',
20 | '/invocation/next' => 'GET',
21 | '/invocation/response' => 'POST',
22 | '/invocation/error' => 'POST'
23 | ]
24 | ); // maps API routes to which HTTP method they use
25 | define('MAX_EXECUTION_TIME', intval(getenv('MAX_EXECUTION_TIME') ?: 10)); // how long PHP should be allow to run for
26 | define('ACCESS_LOG', getenv('ACCESS_LOG')); // should access log string be output
27 | define('ACCESS_FORMAT', getenv('ACCESS_FORMAT') ?: '"%m %r%Q%q" %s %f %d'); // format access log string takes
28 |
29 | /** Test required environment variables are set */
30 | foreach ([AWS_LAMBDA_RUNTIME_API, HANDLER, LAMBDA_TASK_ROOT] as $value) {
31 | if ($value !== false) {
32 | continue;
33 | }
34 | echo 'Environment variables AWS_LAMBDA_RUNTIME_API, HANDLER and LAMBDA_TASK_ROOT must all be set';
35 | exit(1);
36 | }
37 |
38 | /**
39 | * Sends events to the Lambda custom runtime API.
40 | * @return array
41 | */
42 | function sendRuntimeEvent(string $type, array $body = null, array $invocation = null): array
43 | {
44 | $isValidEventType = array_key_exists($type, EVENT_TYPE_METHODS);
45 | if (!$isValidEventType) {
46 | throw new Exception("Unrecognised runtime event type: ${type}");
47 | }
48 | $method = EVENT_TYPE_METHODS[$type];
49 | if ($method === 'GET' && $body !== null) {
50 | throw new Exception('Cannot set body on a GET event request');
51 | }
52 | $host = AWS_LAMBDA_RUNTIME_API;
53 | $version = LAMBDA_RUNTIME_API_VERSION;
54 | if ($invocation !== null && array_key_exists('id', $invocation)) {
55 | $invocationId = $invocation['id'];
56 | $type = str_replace('/invocation/', "/invocation/${invocationId}/", $type);
57 | }
58 | $url = "http://${host}/${version}/runtime${type}";
59 |
60 | $ch = curl_init($url);
61 |
62 | curl_setopt($ch, CURLOPT_FAILONERROR, true);
63 | if ($method === 'POST') {
64 | $bodyString = json_encode($body);
65 | curl_setopt($ch, CURLOPT_POST, 1);
66 | curl_setopt($ch, CURLOPT_POSTFIELDS, $bodyString);
67 | curl_setopt($ch, CURLOPT_HTTPHEADER, [
68 | 'Content-Type: application/json',
69 | 'Content-Length: ' . strlen($bodyString),
70 | ]);
71 | }
72 |
73 | $responseHeaders = [];
74 | curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$responseHeaders) {
75 | $isValidHeader = preg_match('/:\s*/', $header);
76 | if (!$isValidHeader) {
77 | return strlen($header);
78 | }
79 | [$rawKey, $value] = preg_split('/:\s*/', $header, 2);
80 | $key = strtolower($rawKey);
81 | if (!array_key_exists($key, $responseHeaders)) {
82 | $responseHeaders[$key] = [];
83 | }
84 | $responseHeaders[$key][] = trim($value);
85 | return strlen($header);
86 | });
87 |
88 | $responseBody = '';
89 | curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($ch, $chunk) use (&$responseBody) {
90 | $responseBody .= $chunk;
91 | return strlen($chunk);
92 | });
93 |
94 | curl_exec($ch);
95 |
96 | if (curl_error($ch)) {
97 | $error = curl_error($ch);
98 | throw new Exception("Failed to reach Lambda runtime API: ${error}");
99 | }
100 |
101 | curl_close($ch);
102 |
103 | $response = ['headers' => $responseHeaders, 'body' => $responseBody];
104 |
105 | return $response;
106 | }
107 |
108 | /**
109 | * If the runtime encounters an error during initialization, it posts an error
110 | * message to the initialization error path.
111 | */
112 | function sendInitilizationError($message): array
113 | {
114 | $body = ['errorMessage' => $message, 'errorType' => 'InitError'];
115 | $response = sendRuntimeEvent('/init/error', $body);
116 |
117 | return $response;
118 | }
119 |
120 | /**
121 | * If the function returns an error, the runtime formats the error into a JSON
122 | * document, and posts it to the invocation error path.
123 | */
124 | function sendInvocationError($invocation, $message): array
125 | {
126 | $body = ['errorMessage' => $message, 'errorType' => 'InvocationError'];
127 | $response = sendRuntimeEvent('/invocation/error', $body, $invocation);
128 |
129 | return $response;
130 | }
131 |
132 | /**
133 | * Calls the Lambda runtime API to obtain details of the next invocation.
134 | */
135 | function getNextInvocation(): array
136 | {
137 | try {
138 | $response = sendRuntimeEvent('/invocation/next');
139 | } catch (Exception $error) {
140 | $message = $error->getMessage();
141 | throw new Exception("Failed to fetch next Lambda invocation: ${message}");
142 | }
143 | ['headers' => $headers, 'body' => $body] = $response;
144 | if (!array_key_exists('lambda-runtime-aws-request-id', $headers) ||
145 | count($headers['lambda-runtime-aws-request-id']) !== 1) {
146 | throw new Exception('Failed to determine Lambda invocation ID');
147 | }
148 | $id = $headers['lambda-runtime-aws-request-id'][0];
149 | if (empty($body)) {
150 | throw new Exception('Empty Lambda invocation response');
151 | }
152 |
153 | $event = (array) json_decode($body, true);
154 | $invocation = ['id' => $id, 'event' => $event];
155 |
156 | return $invocation;
157 | }
158 |
159 | /**
160 | * Formats an incoming invocation event in to an HTTP request object for passing
161 | * to the CGI process.
162 | *
163 | * @param array $event The invocation's API Gateway or ALB event object.
164 | * @return array HTTP request object.
165 | */
166 | function createRequest(array $invocation): array
167 | {
168 | ['event' => $event] = $invocation;
169 | ['httpMethod' => $method, 'path' => $path, 'body' => $body] = $event;
170 |
171 | $eventHeader = @$event['multiValueHeaders'] ?: @$event['headers'] ?: [];
172 | $headers = array_map(
173 | function ($value) {
174 | return is_array($value) ? @$value[0] : $value;
175 | },
176 | $eventHeader
177 | );
178 | $eventQueryParameters = @$event['multiValueQueryStringParameters'] ?: @$event['queryStringParameters'] ?: [];
179 | $queryString = implode(
180 | '&',
181 | array_reduce(
182 | array_keys($eventQueryParameters),
183 | function ($carry, $key) use ($eventQueryParameters) {
184 | $values = is_array($eventQueryParameters[$key]) ? $eventQueryParameters[$key] : [$eventQueryParameters[$key]];
185 | foreach ($values as $value) {
186 | $carry[] = sprintf('%s=%s', $key, $value);
187 | }
188 | return $carry;
189 | },
190 | []
191 | )
192 | );
193 |
194 | $isBase64Encoded = array_key_exists('isBase64Encoded', $event) && $event['isBase64Encoded'];
195 | if ($isBase64Encoded) {
196 | $body = base64_decode($body);
197 | }
198 |
199 | $request = [
200 | 'method' => $method,
201 | 'path' => preg_replace('/\/{2,}/', '/', $path), // basic normalisation of the path
202 | 'query_string' => $queryString,
203 | 'headers' => $headers,
204 | 'body' => $body,
205 | ];
206 |
207 | return $request;
208 | }
209 |
210 | /**
211 | * Takes an incoming request object, formats it suitable for the PHP CGI
212 | * process, opens a process and awaits the response.
213 | */
214 | function performRequest(array $request): array
215 | {
216 | [
217 | 'method' => $method,
218 | 'path' => $path,
219 | 'query_string' => $queryString,
220 | 'headers' => $requestHeaders,
221 | 'body' => $requestBody,
222 | ] = $request;
223 |
224 | $taskRoot = LAMBDA_TASK_ROOT;
225 | $configPath = CONFIG_PATH;
226 | $extensionDir = EXTENSION_DIR;
227 | $absolutePath = $taskRoot . '/' . dirname(HANDLER);
228 | $scriptFilename = $absolutePath . $path;
229 | if (!is_file($scriptFilename)) {
230 | $scriptFilename = rtrim($scriptFilename, '/') . '/index.php';
231 | if (!is_file($scriptFilename)) {
232 | $scriptFilename = ltrim(HANDLER, '/');
233 | } else if (substr($path, -1) !== '/') {
234 | return [
235 | 'status' => '301 Moved Permanently',
236 | 'statusCode' => 301,
237 | 'headers' => [
238 | 'Location' => [$path . '/'],
239 | ],
240 | 'body' => '',
241 | ];
242 | }
243 | }
244 | $scriptName = str_replace($absolutePath, '', $scriptFilename);
245 |
246 | if (file_exists('/opt/bin/php-cgi')) {
247 | $cgiPath = '/opt/bin/';
248 | } else {
249 | $cgiPath = '';
250 | }
251 |
252 | $cmd = "${cgiPath}php-cgi -c \"${configPath}\" -d extension_dir=\"${extensionDir}\"";
253 | $descriptorSpec = [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']];
254 | $cwd = LAMBDA_TASK_ROOT;
255 | $env = array_merge(
256 | $_SERVER,
257 | [
258 | 'CONTENT_LENGTH' => strlen($requestBody),
259 | 'CONTENT_TYPE' => (@$requestHeaders['content-type'] ?: ''),
260 | 'QUERY_STRING' => $queryString,
261 | 'REDIRECT_STATUS' => 200,
262 | 'REQUEST_METHOD' => $method,
263 | 'REQUEST_URI' => $path,
264 | 'SCRIPT_FILENAME' => $scriptFilename,
265 | 'SCRIPT_NAME' => $scriptName,
266 | 'SERVER_PROTOCOL' => 'HTTP/1.1',
267 | ]
268 | );
269 | unset($env['argv']);
270 | foreach ($requestHeaders as $rawKey => $value) {
271 | $key = 'HTTP_' . str_replace('-', '_', strtoupper($rawKey));
272 | $env[$key] = $value;
273 | }
274 |
275 | $process = proc_open($cmd, $descriptorSpec, $pipes, $cwd, $env);
276 | if (!is_resource($process)) {
277 | $exception = new Exception('Failed to launch PHP process');
278 | throw $exception;
279 | }
280 |
281 | fwrite($pipes[0], $requestBody);
282 | fclose($pipes[0]);
283 |
284 | stream_set_blocking($pipes[1], false);
285 | stream_set_blocking($pipes[2], false);
286 | $responseRaw = '';
287 | $err = '';
288 |
289 | $start = microtime(true);
290 | $timeout = false;
291 | while (!feof($pipes[1])) {
292 | $duration = (microtime(true) - $start);
293 | if ($duration > MAX_EXECUTION_TIME) {
294 | $timeout = true;
295 | break;
296 | }
297 | $responseRaw .= fread($pipes[1], 1024);
298 | $err .= fread($pipes[2], 1024);
299 | usleep(100);
300 | }
301 |
302 | fclose($pipes[1]);
303 | fclose($pipes[2]);
304 |
305 | proc_terminate($process, 2);
306 |
307 | if ($err !== '') {
308 | echo $err;
309 | }
310 |
311 | if ($timeout) {
312 | $maxExecutionTime = MAX_EXECUTION_TIME;
313 | echo "PHP took longer than $maxExecutionTime seconds to return response";
314 |
315 | return [
316 | 'status' => '500 Internal Server Error',
317 | 'statusCode' => 500,
318 | 'headers' => [],
319 | 'body' => 'Internal Server Error',
320 | ];
321 | }
322 |
323 | $status = '200 OK';
324 | [$responseHeadersRaw, $responseBody] = explode("\r\n\r\n", $responseRaw, 2);
325 | $responseHeaders = array_reduce(
326 | explode(PHP_EOL, $responseHeadersRaw),
327 | function ($carry, $line) use (&$status) {
328 | [$key, $value] = array_map('trim', explode(':', $line, 2));
329 | if ($key === 'Status') {
330 | $status = $value;
331 | return $carry;
332 | }
333 | if (!array_key_exists($key, $carry)) {
334 | $carry[$key] = [];
335 | }
336 | $carry[$key][] = $value;
337 | return $carry;
338 | },
339 | []
340 | );
341 | [$statusCode,] = explode(' ', $status, 2);
342 |
343 | if (ACCESS_LOG) {
344 | $patterns = [
345 | '%m',
346 | '%r',
347 | '%Q',
348 | '%q',
349 | '%s',
350 | '%f',
351 | '%d',
352 | ];
353 | $replacements = [
354 | $method,
355 | $path,
356 | ($queryString ? '?' : ''),
357 | ($queryString ?: ''),
358 | $statusCode,
359 | $scriptName,
360 | sprintf('%.4f', $duration),
361 | ];
362 | echo preg_replace_callback(
363 | '/%{(.+?)}e/',
364 | function ($matches) use ($env) {
365 | $key = $matches[1];
366 | return @$env[$key] ?: '-';
367 | },
368 | str_replace($patterns, $replacements, ACCESS_FORMAT)
369 | ) . PHP_EOL;
370 | }
371 |
372 | return [
373 | 'status' => $status,
374 | 'statusCode' => $statusCode,
375 | 'headers' => $responseHeaders,
376 | 'body' => $responseBody,
377 | ];
378 | }
379 |
380 | /**
381 | * Formats an HTTP response in to an AWS (API Gateway Proxy or ALB) response
382 | * object.
383 | */
384 | function createResponse(array $invocation, array $httpResponse): array
385 | {
386 | ['event' => $event] = $invocation;
387 | ['status' => $status, 'statusCode' => $statusCode, 'headers' => $headers, 'body' => $body] = $httpResponse;
388 | $response = ['statusCode' => (int) $statusCode, 'body' => $body];
389 |
390 | $isApplicationLoadBalancerRequest = array_key_exists('requestContext', $event);
391 | if ($isApplicationLoadBalancerRequest) {
392 | $response['statusDescription'] = $status;
393 | }
394 |
395 | $hasMultiValueHeaders = array_key_exists('multiValueHeaders', $event);
396 | if ($hasMultiValueHeaders) {
397 | $response['multiValueHeaders'] = $headers;
398 | } else {
399 | $response['headers'] = array_map(
400 | function ($value) {
401 | return $value[0];
402 | },
403 | $headers
404 | );
405 | }
406 |
407 | $hasBase64EncodingProperty = array_key_exists('isBase64Encoded', $event);
408 | if ($hasBase64EncodingProperty) {
409 | $response['isBase64Encoded'] = false;
410 | }
411 |
412 | if ($isApplicationLoadBalancerRequest) {
413 | $size = strlen(json_encode($response));
414 | if ($size > 1000000) {
415 | echo "Response size is too large for ALB ($size bytes)" . PHP_EOL;
416 |
417 | $errorResponse = [
418 | 'status' => '500 Internal Server Error',
419 | 'statusCode' => 500,
420 | 'headers' => [],
421 | 'body' => 'Internal Server Error',
422 | ];
423 |
424 | return createResponse($invocation, $errorResponse);
425 | }
426 | }
427 |
428 | return $response;
429 | }
430 |
431 | /**
432 | * Forwards the Lambda-formatted response object to the Lambda runtime API.
433 | */
434 | function sendLambdaResponse(array $invocation, array $lambdaResponse): array
435 | {
436 | return sendRuntimeEvent('/invocation/response', $lambdaResponse, $invocation);
437 | }
438 |
439 | /**
440 | * Performs the tasks required to handle an individual incoming request.
441 | */
442 | function handleNextRequest(): void
443 | {
444 | try {
445 | $invocation = getNextInvocation();
446 | } catch (Exception $e) {
447 | sendInitilizationError($e->getMessage());
448 | return;
449 | }
450 |
451 | try {
452 | $request = createRequest($invocation);
453 | $httpResponse = performRequest($request);
454 | $lambdaResponse = createResponse($invocation, $httpResponse);
455 | sendLambdaResponse($invocation, $lambdaResponse);
456 | } catch (Exception $e) {
457 | sendInvocationError($invocation, $e->getMessage());
458 | }
459 | }
460 |
461 | while (isset($loop) ? $loop : true) {
462 | handleNextRequest();
463 | }
464 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | REQUIRED_LIB_FILES="/usr/lib64/libedit.so.0
4 | /usr/lib64/libonig.so.2"
5 |
6 | echo "Building layer for PHP 8 - using Amazon Linux Extras"
7 |
8 | amazon-linux-extras enable php8.0
9 | yum install -y php-cli php-dom php-mbstring php-mysqlnd
10 |
11 | mkdir /tmp/layer
12 | cd /tmp/layer
13 | cp /opt/layer/php.ini .
14 | cp /opt/layer/bootstrap .
15 | chmod a+x bootstrap
16 |
17 | mkdir bin
18 | cp /usr/bin/php bin/
19 | cp /usr/bin/php-cgi bin/
20 |
21 | mkdir lib
22 | for LIB in $REQUIRED_LIB_FILES; do
23 | cp $LIB lib/
24 | done
25 |
26 | mkdir -p lib/php/8.0
27 | cp -a /usr/lib64/php/modules lib/php/8.0/
28 |
29 | zip -r /opt/layer/php80.zip .
30 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aiir/php-lambda-layer",
3 | "type": "project",
4 | "description": "PHP runtime layer for AWS Lambda, allowing PHP scripts to be executed in an environment as close to a standard web server as possible",
5 | "keywords": [
6 | "lambda",
7 | "php",
8 | "runtime",
9 | "layer"
10 | ],
11 | "authors": [
12 | {
13 | "name": "Andy Buckingham",
14 | "email": "andy@aiir.com",
15 | "homepage": "https://twitter.com/andybee"
16 | }
17 | ],
18 | "require": {},
19 | "require-dev": {
20 | "slim/slim": "^3.12",
21 | "phpunit/phpunit": "^8.0",
22 | "donatj/mock-webserver": "^2.0"
23 | },
24 | "scripts": {
25 | "test": [
26 | "phpunit"
27 | ]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/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": "f890551350407d9d12e95b2664a4cb1e",
8 | "packages": [],
9 | "packages-dev": [
10 | {
11 | "name": "doctrine/instantiator",
12 | "version": "1.4.1",
13 | "source": {
14 | "type": "git",
15 | "url": "https://github.com/doctrine/instantiator.git",
16 | "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc"
17 | },
18 | "dist": {
19 | "type": "zip",
20 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc",
21 | "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc",
22 | "shasum": ""
23 | },
24 | "require": {
25 | "php": "^7.1 || ^8.0"
26 | },
27 | "require-dev": {
28 | "doctrine/coding-standard": "^9",
29 | "ext-pdo": "*",
30 | "ext-phar": "*",
31 | "phpbench/phpbench": "^0.16 || ^1",
32 | "phpstan/phpstan": "^1.4",
33 | "phpstan/phpstan-phpunit": "^1",
34 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
35 | "vimeo/psalm": "^4.22"
36 | },
37 | "type": "library",
38 | "autoload": {
39 | "psr-4": {
40 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
41 | }
42 | },
43 | "notification-url": "https://packagist.org/downloads/",
44 | "license": [
45 | "MIT"
46 | ],
47 | "authors": [
48 | {
49 | "name": "Marco Pivetta",
50 | "email": "ocramius@gmail.com",
51 | "homepage": "https://ocramius.github.io/"
52 | }
53 | ],
54 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
55 | "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
56 | "keywords": [
57 | "constructor",
58 | "instantiate"
59 | ],
60 | "support": {
61 | "issues": "https://github.com/doctrine/instantiator/issues",
62 | "source": "https://github.com/doctrine/instantiator/tree/1.4.1"
63 | },
64 | "funding": [
65 | {
66 | "url": "https://www.doctrine-project.org/sponsorship.html",
67 | "type": "custom"
68 | },
69 | {
70 | "url": "https://www.patreon.com/phpdoctrine",
71 | "type": "patreon"
72 | },
73 | {
74 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
75 | "type": "tidelift"
76 | }
77 | ],
78 | "time": "2022-03-03T08:28:38+00:00"
79 | },
80 | {
81 | "name": "donatj/mock-webserver",
82 | "version": "v2.4.1",
83 | "source": {
84 | "type": "git",
85 | "url": "https://github.com/donatj/mock-webserver.git",
86 | "reference": "bcec733923068135bd4a2da359aa038a93697c5b"
87 | },
88 | "dist": {
89 | "type": "zip",
90 | "url": "https://api.github.com/repos/donatj/mock-webserver/zipball/bcec733923068135bd4a2da359aa038a93697c5b",
91 | "reference": "bcec733923068135bd4a2da359aa038a93697c5b",
92 | "shasum": ""
93 | },
94 | "require": {
95 | "ext-json": "*",
96 | "ext-sockets": "*",
97 | "php": ">=5.4",
98 | "ralouphie/getallheaders": "~2.0 || ~3.0"
99 | },
100 | "require-dev": {
101 | "donatj/drop": "^1.0",
102 | "phpunit/phpunit": "~4|~9"
103 | },
104 | "type": "library",
105 | "autoload": {
106 | "psr-4": {
107 | "donatj\\MockWebServer\\": "src/"
108 | }
109 | },
110 | "notification-url": "https://packagist.org/downloads/",
111 | "license": [
112 | "MIT"
113 | ],
114 | "authors": [
115 | {
116 | "name": "Jesse G. Donat",
117 | "email": "donatj@gmail.com",
118 | "homepage": "https://donatstudios.com",
119 | "role": "Lead"
120 | }
121 | ],
122 | "description": "Simple mock web server for unit testing",
123 | "support": {
124 | "issues": "https://github.com/donatj/mock-webserver/issues",
125 | "source": "https://github.com/donatj/mock-webserver/tree/v2.4.1"
126 | },
127 | "funding": [
128 | {
129 | "url": "https://www.paypal.me/donatj/5",
130 | "type": "custom"
131 | },
132 | {
133 | "url": "https://github.com/donatj",
134 | "type": "github"
135 | }
136 | ],
137 | "time": "2022-01-19T16:59:52+00:00"
138 | },
139 | {
140 | "name": "myclabs/deep-copy",
141 | "version": "1.11.0",
142 | "source": {
143 | "type": "git",
144 | "url": "https://github.com/myclabs/DeepCopy.git",
145 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
146 | },
147 | "dist": {
148 | "type": "zip",
149 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
150 | "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
151 | "shasum": ""
152 | },
153 | "require": {
154 | "php": "^7.1 || ^8.0"
155 | },
156 | "conflict": {
157 | "doctrine/collections": "<1.6.8",
158 | "doctrine/common": "<2.13.3 || >=3,<3.2.2"
159 | },
160 | "require-dev": {
161 | "doctrine/collections": "^1.6.8",
162 | "doctrine/common": "^2.13.3 || ^3.2.2",
163 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
164 | },
165 | "type": "library",
166 | "autoload": {
167 | "files": [
168 | "src/DeepCopy/deep_copy.php"
169 | ],
170 | "psr-4": {
171 | "DeepCopy\\": "src/DeepCopy/"
172 | }
173 | },
174 | "notification-url": "https://packagist.org/downloads/",
175 | "license": [
176 | "MIT"
177 | ],
178 | "description": "Create deep copies (clones) of your objects",
179 | "keywords": [
180 | "clone",
181 | "copy",
182 | "duplicate",
183 | "object",
184 | "object graph"
185 | ],
186 | "support": {
187 | "issues": "https://github.com/myclabs/DeepCopy/issues",
188 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
189 | },
190 | "funding": [
191 | {
192 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
193 | "type": "tidelift"
194 | }
195 | ],
196 | "time": "2022-03-03T13:19:32+00:00"
197 | },
198 | {
199 | "name": "nikic/fast-route",
200 | "version": "v1.3.0",
201 | "source": {
202 | "type": "git",
203 | "url": "https://github.com/nikic/FastRoute.git",
204 | "reference": "181d480e08d9476e61381e04a71b34dc0432e812"
205 | },
206 | "dist": {
207 | "type": "zip",
208 | "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812",
209 | "reference": "181d480e08d9476e61381e04a71b34dc0432e812",
210 | "shasum": ""
211 | },
212 | "require": {
213 | "php": ">=5.4.0"
214 | },
215 | "require-dev": {
216 | "phpunit/phpunit": "^4.8.35|~5.7"
217 | },
218 | "type": "library",
219 | "autoload": {
220 | "files": [
221 | "src/functions.php"
222 | ],
223 | "psr-4": {
224 | "FastRoute\\": "src/"
225 | }
226 | },
227 | "notification-url": "https://packagist.org/downloads/",
228 | "license": [
229 | "BSD-3-Clause"
230 | ],
231 | "authors": [
232 | {
233 | "name": "Nikita Popov",
234 | "email": "nikic@php.net"
235 | }
236 | ],
237 | "description": "Fast request router for PHP",
238 | "keywords": [
239 | "router",
240 | "routing"
241 | ],
242 | "support": {
243 | "issues": "https://github.com/nikic/FastRoute/issues",
244 | "source": "https://github.com/nikic/FastRoute/tree/master"
245 | },
246 | "time": "2018-02-13T20:26:39+00:00"
247 | },
248 | {
249 | "name": "phar-io/manifest",
250 | "version": "2.0.3",
251 | "source": {
252 | "type": "git",
253 | "url": "https://github.com/phar-io/manifest.git",
254 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53"
255 | },
256 | "dist": {
257 | "type": "zip",
258 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53",
259 | "reference": "97803eca37d319dfa7826cc2437fc020857acb53",
260 | "shasum": ""
261 | },
262 | "require": {
263 | "ext-dom": "*",
264 | "ext-phar": "*",
265 | "ext-xmlwriter": "*",
266 | "phar-io/version": "^3.0.1",
267 | "php": "^7.2 || ^8.0"
268 | },
269 | "type": "library",
270 | "extra": {
271 | "branch-alias": {
272 | "dev-master": "2.0.x-dev"
273 | }
274 | },
275 | "autoload": {
276 | "classmap": [
277 | "src/"
278 | ]
279 | },
280 | "notification-url": "https://packagist.org/downloads/",
281 | "license": [
282 | "BSD-3-Clause"
283 | ],
284 | "authors": [
285 | {
286 | "name": "Arne Blankerts",
287 | "email": "arne@blankerts.de",
288 | "role": "Developer"
289 | },
290 | {
291 | "name": "Sebastian Heuer",
292 | "email": "sebastian@phpeople.de",
293 | "role": "Developer"
294 | },
295 | {
296 | "name": "Sebastian Bergmann",
297 | "email": "sebastian@phpunit.de",
298 | "role": "Developer"
299 | }
300 | ],
301 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
302 | "support": {
303 | "issues": "https://github.com/phar-io/manifest/issues",
304 | "source": "https://github.com/phar-io/manifest/tree/2.0.3"
305 | },
306 | "time": "2021-07-20T11:28:43+00:00"
307 | },
308 | {
309 | "name": "phar-io/version",
310 | "version": "3.2.1",
311 | "source": {
312 | "type": "git",
313 | "url": "https://github.com/phar-io/version.git",
314 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
315 | },
316 | "dist": {
317 | "type": "zip",
318 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
319 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
320 | "shasum": ""
321 | },
322 | "require": {
323 | "php": "^7.2 || ^8.0"
324 | },
325 | "type": "library",
326 | "autoload": {
327 | "classmap": [
328 | "src/"
329 | ]
330 | },
331 | "notification-url": "https://packagist.org/downloads/",
332 | "license": [
333 | "BSD-3-Clause"
334 | ],
335 | "authors": [
336 | {
337 | "name": "Arne Blankerts",
338 | "email": "arne@blankerts.de",
339 | "role": "Developer"
340 | },
341 | {
342 | "name": "Sebastian Heuer",
343 | "email": "sebastian@phpeople.de",
344 | "role": "Developer"
345 | },
346 | {
347 | "name": "Sebastian Bergmann",
348 | "email": "sebastian@phpunit.de",
349 | "role": "Developer"
350 | }
351 | ],
352 | "description": "Library for handling version information and constraints",
353 | "support": {
354 | "issues": "https://github.com/phar-io/version/issues",
355 | "source": "https://github.com/phar-io/version/tree/3.2.1"
356 | },
357 | "time": "2022-02-21T01:04:05+00:00"
358 | },
359 | {
360 | "name": "phpdocumentor/reflection-common",
361 | "version": "2.2.0",
362 | "source": {
363 | "type": "git",
364 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
365 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
366 | },
367 | "dist": {
368 | "type": "zip",
369 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
370 | "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
371 | "shasum": ""
372 | },
373 | "require": {
374 | "php": "^7.2 || ^8.0"
375 | },
376 | "type": "library",
377 | "extra": {
378 | "branch-alias": {
379 | "dev-2.x": "2.x-dev"
380 | }
381 | },
382 | "autoload": {
383 | "psr-4": {
384 | "phpDocumentor\\Reflection\\": "src/"
385 | }
386 | },
387 | "notification-url": "https://packagist.org/downloads/",
388 | "license": [
389 | "MIT"
390 | ],
391 | "authors": [
392 | {
393 | "name": "Jaap van Otterdijk",
394 | "email": "opensource@ijaap.nl"
395 | }
396 | ],
397 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
398 | "homepage": "http://www.phpdoc.org",
399 | "keywords": [
400 | "FQSEN",
401 | "phpDocumentor",
402 | "phpdoc",
403 | "reflection",
404 | "static analysis"
405 | ],
406 | "support": {
407 | "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
408 | "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
409 | },
410 | "time": "2020-06-27T09:03:43+00:00"
411 | },
412 | {
413 | "name": "phpdocumentor/reflection-docblock",
414 | "version": "5.3.0",
415 | "source": {
416 | "type": "git",
417 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
418 | "reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
419 | },
420 | "dist": {
421 | "type": "zip",
422 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
423 | "reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
424 | "shasum": ""
425 | },
426 | "require": {
427 | "ext-filter": "*",
428 | "php": "^7.2 || ^8.0",
429 | "phpdocumentor/reflection-common": "^2.2",
430 | "phpdocumentor/type-resolver": "^1.3",
431 | "webmozart/assert": "^1.9.1"
432 | },
433 | "require-dev": {
434 | "mockery/mockery": "~1.3.2",
435 | "psalm/phar": "^4.8"
436 | },
437 | "type": "library",
438 | "extra": {
439 | "branch-alias": {
440 | "dev-master": "5.x-dev"
441 | }
442 | },
443 | "autoload": {
444 | "psr-4": {
445 | "phpDocumentor\\Reflection\\": "src"
446 | }
447 | },
448 | "notification-url": "https://packagist.org/downloads/",
449 | "license": [
450 | "MIT"
451 | ],
452 | "authors": [
453 | {
454 | "name": "Mike van Riel",
455 | "email": "me@mikevanriel.com"
456 | },
457 | {
458 | "name": "Jaap van Otterdijk",
459 | "email": "account@ijaap.nl"
460 | }
461 | ],
462 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
463 | "support": {
464 | "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
465 | "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0"
466 | },
467 | "time": "2021-10-19T17:43:47+00:00"
468 | },
469 | {
470 | "name": "phpdocumentor/type-resolver",
471 | "version": "1.6.1",
472 | "source": {
473 | "type": "git",
474 | "url": "https://github.com/phpDocumentor/TypeResolver.git",
475 | "reference": "77a32518733312af16a44300404e945338981de3"
476 | },
477 | "dist": {
478 | "type": "zip",
479 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3",
480 | "reference": "77a32518733312af16a44300404e945338981de3",
481 | "shasum": ""
482 | },
483 | "require": {
484 | "php": "^7.2 || ^8.0",
485 | "phpdocumentor/reflection-common": "^2.0"
486 | },
487 | "require-dev": {
488 | "ext-tokenizer": "*",
489 | "psalm/phar": "^4.8"
490 | },
491 | "type": "library",
492 | "extra": {
493 | "branch-alias": {
494 | "dev-1.x": "1.x-dev"
495 | }
496 | },
497 | "autoload": {
498 | "psr-4": {
499 | "phpDocumentor\\Reflection\\": "src"
500 | }
501 | },
502 | "notification-url": "https://packagist.org/downloads/",
503 | "license": [
504 | "MIT"
505 | ],
506 | "authors": [
507 | {
508 | "name": "Mike van Riel",
509 | "email": "me@mikevanriel.com"
510 | }
511 | ],
512 | "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
513 | "support": {
514 | "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
515 | "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1"
516 | },
517 | "time": "2022-03-15T21:29:03+00:00"
518 | },
519 | {
520 | "name": "phpspec/prophecy",
521 | "version": "v1.15.0",
522 | "source": {
523 | "type": "git",
524 | "url": "https://github.com/phpspec/prophecy.git",
525 | "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
526 | },
527 | "dist": {
528 | "type": "zip",
529 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
530 | "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
531 | "shasum": ""
532 | },
533 | "require": {
534 | "doctrine/instantiator": "^1.2",
535 | "php": "^7.2 || ~8.0, <8.2",
536 | "phpdocumentor/reflection-docblock": "^5.2",
537 | "sebastian/comparator": "^3.0 || ^4.0",
538 | "sebastian/recursion-context": "^3.0 || ^4.0"
539 | },
540 | "require-dev": {
541 | "phpspec/phpspec": "^6.0 || ^7.0",
542 | "phpunit/phpunit": "^8.0 || ^9.0"
543 | },
544 | "type": "library",
545 | "extra": {
546 | "branch-alias": {
547 | "dev-master": "1.x-dev"
548 | }
549 | },
550 | "autoload": {
551 | "psr-4": {
552 | "Prophecy\\": "src/Prophecy"
553 | }
554 | },
555 | "notification-url": "https://packagist.org/downloads/",
556 | "license": [
557 | "MIT"
558 | ],
559 | "authors": [
560 | {
561 | "name": "Konstantin Kudryashov",
562 | "email": "ever.zet@gmail.com",
563 | "homepage": "http://everzet.com"
564 | },
565 | {
566 | "name": "Marcello Duarte",
567 | "email": "marcello.duarte@gmail.com"
568 | }
569 | ],
570 | "description": "Highly opinionated mocking framework for PHP 5.3+",
571 | "homepage": "https://github.com/phpspec/prophecy",
572 | "keywords": [
573 | "Double",
574 | "Dummy",
575 | "fake",
576 | "mock",
577 | "spy",
578 | "stub"
579 | ],
580 | "support": {
581 | "issues": "https://github.com/phpspec/prophecy/issues",
582 | "source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
583 | },
584 | "time": "2021-12-08T12:19:24+00:00"
585 | },
586 | {
587 | "name": "phpunit/php-code-coverage",
588 | "version": "7.0.15",
589 | "source": {
590 | "type": "git",
591 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
592 | "reference": "819f92bba8b001d4363065928088de22f25a3a48"
593 | },
594 | "dist": {
595 | "type": "zip",
596 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/819f92bba8b001d4363065928088de22f25a3a48",
597 | "reference": "819f92bba8b001d4363065928088de22f25a3a48",
598 | "shasum": ""
599 | },
600 | "require": {
601 | "ext-dom": "*",
602 | "ext-xmlwriter": "*",
603 | "php": ">=7.2",
604 | "phpunit/php-file-iterator": "^2.0.2",
605 | "phpunit/php-text-template": "^1.2.1",
606 | "phpunit/php-token-stream": "^3.1.3 || ^4.0",
607 | "sebastian/code-unit-reverse-lookup": "^1.0.1",
608 | "sebastian/environment": "^4.2.2",
609 | "sebastian/version": "^2.0.1",
610 | "theseer/tokenizer": "^1.1.3"
611 | },
612 | "require-dev": {
613 | "phpunit/phpunit": "^8.2.2"
614 | },
615 | "suggest": {
616 | "ext-xdebug": "^2.7.2"
617 | },
618 | "type": "library",
619 | "extra": {
620 | "branch-alias": {
621 | "dev-master": "7.0-dev"
622 | }
623 | },
624 | "autoload": {
625 | "classmap": [
626 | "src/"
627 | ]
628 | },
629 | "notification-url": "https://packagist.org/downloads/",
630 | "license": [
631 | "BSD-3-Clause"
632 | ],
633 | "authors": [
634 | {
635 | "name": "Sebastian Bergmann",
636 | "email": "sebastian@phpunit.de",
637 | "role": "lead"
638 | }
639 | ],
640 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
641 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
642 | "keywords": [
643 | "coverage",
644 | "testing",
645 | "xunit"
646 | ],
647 | "support": {
648 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
649 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/7.0.15"
650 | },
651 | "funding": [
652 | {
653 | "url": "https://github.com/sebastianbergmann",
654 | "type": "github"
655 | }
656 | ],
657 | "time": "2021-07-26T12:20:09+00:00"
658 | },
659 | {
660 | "name": "phpunit/php-file-iterator",
661 | "version": "2.0.5",
662 | "source": {
663 | "type": "git",
664 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
665 | "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5"
666 | },
667 | "dist": {
668 | "type": "zip",
669 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5",
670 | "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5",
671 | "shasum": ""
672 | },
673 | "require": {
674 | "php": ">=7.1"
675 | },
676 | "require-dev": {
677 | "phpunit/phpunit": "^8.5"
678 | },
679 | "type": "library",
680 | "extra": {
681 | "branch-alias": {
682 | "dev-master": "2.0.x-dev"
683 | }
684 | },
685 | "autoload": {
686 | "classmap": [
687 | "src/"
688 | ]
689 | },
690 | "notification-url": "https://packagist.org/downloads/",
691 | "license": [
692 | "BSD-3-Clause"
693 | ],
694 | "authors": [
695 | {
696 | "name": "Sebastian Bergmann",
697 | "email": "sebastian@phpunit.de",
698 | "role": "lead"
699 | }
700 | ],
701 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
702 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
703 | "keywords": [
704 | "filesystem",
705 | "iterator"
706 | ],
707 | "support": {
708 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
709 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/2.0.5"
710 | },
711 | "funding": [
712 | {
713 | "url": "https://github.com/sebastianbergmann",
714 | "type": "github"
715 | }
716 | ],
717 | "time": "2021-12-02T12:42:26+00:00"
718 | },
719 | {
720 | "name": "phpunit/php-text-template",
721 | "version": "1.2.1",
722 | "source": {
723 | "type": "git",
724 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
725 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
726 | },
727 | "dist": {
728 | "type": "zip",
729 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
730 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
731 | "shasum": ""
732 | },
733 | "require": {
734 | "php": ">=5.3.3"
735 | },
736 | "type": "library",
737 | "autoload": {
738 | "classmap": [
739 | "src/"
740 | ]
741 | },
742 | "notification-url": "https://packagist.org/downloads/",
743 | "license": [
744 | "BSD-3-Clause"
745 | ],
746 | "authors": [
747 | {
748 | "name": "Sebastian Bergmann",
749 | "email": "sebastian@phpunit.de",
750 | "role": "lead"
751 | }
752 | ],
753 | "description": "Simple template engine.",
754 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
755 | "keywords": [
756 | "template"
757 | ],
758 | "support": {
759 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
760 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
761 | },
762 | "time": "2015-06-21T13:50:34+00:00"
763 | },
764 | {
765 | "name": "phpunit/php-timer",
766 | "version": "2.1.3",
767 | "source": {
768 | "type": "git",
769 | "url": "https://github.com/sebastianbergmann/php-timer.git",
770 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662"
771 | },
772 | "dist": {
773 | "type": "zip",
774 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662",
775 | "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662",
776 | "shasum": ""
777 | },
778 | "require": {
779 | "php": ">=7.1"
780 | },
781 | "require-dev": {
782 | "phpunit/phpunit": "^8.5"
783 | },
784 | "type": "library",
785 | "extra": {
786 | "branch-alias": {
787 | "dev-master": "2.1-dev"
788 | }
789 | },
790 | "autoload": {
791 | "classmap": [
792 | "src/"
793 | ]
794 | },
795 | "notification-url": "https://packagist.org/downloads/",
796 | "license": [
797 | "BSD-3-Clause"
798 | ],
799 | "authors": [
800 | {
801 | "name": "Sebastian Bergmann",
802 | "email": "sebastian@phpunit.de",
803 | "role": "lead"
804 | }
805 | ],
806 | "description": "Utility class for timing",
807 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
808 | "keywords": [
809 | "timer"
810 | ],
811 | "support": {
812 | "issues": "https://github.com/sebastianbergmann/php-timer/issues",
813 | "source": "https://github.com/sebastianbergmann/php-timer/tree/2.1.3"
814 | },
815 | "funding": [
816 | {
817 | "url": "https://github.com/sebastianbergmann",
818 | "type": "github"
819 | }
820 | ],
821 | "time": "2020-11-30T08:20:02+00:00"
822 | },
823 | {
824 | "name": "phpunit/php-token-stream",
825 | "version": "4.0.4",
826 | "source": {
827 | "type": "git",
828 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
829 | "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3"
830 | },
831 | "dist": {
832 | "type": "zip",
833 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/a853a0e183b9db7eed023d7933a858fa1c8d25a3",
834 | "reference": "a853a0e183b9db7eed023d7933a858fa1c8d25a3",
835 | "shasum": ""
836 | },
837 | "require": {
838 | "ext-tokenizer": "*",
839 | "php": "^7.3 || ^8.0"
840 | },
841 | "require-dev": {
842 | "phpunit/phpunit": "^9.0"
843 | },
844 | "type": "library",
845 | "extra": {
846 | "branch-alias": {
847 | "dev-master": "4.0-dev"
848 | }
849 | },
850 | "autoload": {
851 | "classmap": [
852 | "src/"
853 | ]
854 | },
855 | "notification-url": "https://packagist.org/downloads/",
856 | "license": [
857 | "BSD-3-Clause"
858 | ],
859 | "authors": [
860 | {
861 | "name": "Sebastian Bergmann",
862 | "email": "sebastian@phpunit.de"
863 | }
864 | ],
865 | "description": "Wrapper around PHP's tokenizer extension.",
866 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
867 | "keywords": [
868 | "tokenizer"
869 | ],
870 | "support": {
871 | "issues": "https://github.com/sebastianbergmann/php-token-stream/issues",
872 | "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master"
873 | },
874 | "funding": [
875 | {
876 | "url": "https://github.com/sebastianbergmann",
877 | "type": "github"
878 | }
879 | ],
880 | "abandoned": true,
881 | "time": "2020-08-04T08:28:15+00:00"
882 | },
883 | {
884 | "name": "phpunit/phpunit",
885 | "version": "8.5.26",
886 | "source": {
887 | "type": "git",
888 | "url": "https://github.com/sebastianbergmann/phpunit.git",
889 | "reference": "ef117c59fc4c54a979021b26d08a3373e386606d"
890 | },
891 | "dist": {
892 | "type": "zip",
893 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ef117c59fc4c54a979021b26d08a3373e386606d",
894 | "reference": "ef117c59fc4c54a979021b26d08a3373e386606d",
895 | "shasum": ""
896 | },
897 | "require": {
898 | "doctrine/instantiator": "^1.3.1",
899 | "ext-dom": "*",
900 | "ext-json": "*",
901 | "ext-libxml": "*",
902 | "ext-mbstring": "*",
903 | "ext-xml": "*",
904 | "ext-xmlwriter": "*",
905 | "myclabs/deep-copy": "^1.10.0",
906 | "phar-io/manifest": "^2.0.3",
907 | "phar-io/version": "^3.0.2",
908 | "php": ">=7.2",
909 | "phpspec/prophecy": "^1.10.3",
910 | "phpunit/php-code-coverage": "^7.0.12",
911 | "phpunit/php-file-iterator": "^2.0.4",
912 | "phpunit/php-text-template": "^1.2.1",
913 | "phpunit/php-timer": "^2.1.2",
914 | "sebastian/comparator": "^3.0.2",
915 | "sebastian/diff": "^3.0.2",
916 | "sebastian/environment": "^4.2.3",
917 | "sebastian/exporter": "^3.1.2",
918 | "sebastian/global-state": "^3.0.0",
919 | "sebastian/object-enumerator": "^3.0.3",
920 | "sebastian/resource-operations": "^2.0.1",
921 | "sebastian/type": "^1.1.3",
922 | "sebastian/version": "^2.0.1"
923 | },
924 | "require-dev": {
925 | "ext-pdo": "*"
926 | },
927 | "suggest": {
928 | "ext-soap": "*",
929 | "ext-xdebug": "*",
930 | "phpunit/php-invoker": "^2.0.0"
931 | },
932 | "bin": [
933 | "phpunit"
934 | ],
935 | "type": "library",
936 | "extra": {
937 | "branch-alias": {
938 | "dev-master": "8.5-dev"
939 | }
940 | },
941 | "autoload": {
942 | "classmap": [
943 | "src/"
944 | ]
945 | },
946 | "notification-url": "https://packagist.org/downloads/",
947 | "license": [
948 | "BSD-3-Clause"
949 | ],
950 | "authors": [
951 | {
952 | "name": "Sebastian Bergmann",
953 | "email": "sebastian@phpunit.de",
954 | "role": "lead"
955 | }
956 | ],
957 | "description": "The PHP Unit Testing framework.",
958 | "homepage": "https://phpunit.de/",
959 | "keywords": [
960 | "phpunit",
961 | "testing",
962 | "xunit"
963 | ],
964 | "support": {
965 | "issues": "https://github.com/sebastianbergmann/phpunit/issues",
966 | "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.26"
967 | },
968 | "funding": [
969 | {
970 | "url": "https://phpunit.de/sponsors.html",
971 | "type": "custom"
972 | },
973 | {
974 | "url": "https://github.com/sebastianbergmann",
975 | "type": "github"
976 | }
977 | ],
978 | "time": "2022-04-01T12:34:39+00:00"
979 | },
980 | {
981 | "name": "pimple/pimple",
982 | "version": "v3.5.0",
983 | "source": {
984 | "type": "git",
985 | "url": "https://github.com/silexphp/Pimple.git",
986 | "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed"
987 | },
988 | "dist": {
989 | "type": "zip",
990 | "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed",
991 | "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed",
992 | "shasum": ""
993 | },
994 | "require": {
995 | "php": ">=7.2.5",
996 | "psr/container": "^1.1 || ^2.0"
997 | },
998 | "require-dev": {
999 | "symfony/phpunit-bridge": "^5.4@dev"
1000 | },
1001 | "type": "library",
1002 | "extra": {
1003 | "branch-alias": {
1004 | "dev-master": "3.4.x-dev"
1005 | }
1006 | },
1007 | "autoload": {
1008 | "psr-0": {
1009 | "Pimple": "src/"
1010 | }
1011 | },
1012 | "notification-url": "https://packagist.org/downloads/",
1013 | "license": [
1014 | "MIT"
1015 | ],
1016 | "authors": [
1017 | {
1018 | "name": "Fabien Potencier",
1019 | "email": "fabien@symfony.com"
1020 | }
1021 | ],
1022 | "description": "Pimple, a simple Dependency Injection Container",
1023 | "homepage": "https://pimple.symfony.com",
1024 | "keywords": [
1025 | "container",
1026 | "dependency injection"
1027 | ],
1028 | "support": {
1029 | "source": "https://github.com/silexphp/Pimple/tree/v3.5.0"
1030 | },
1031 | "time": "2021-10-28T11:13:42+00:00"
1032 | },
1033 | {
1034 | "name": "psr/container",
1035 | "version": "1.1.2",
1036 | "source": {
1037 | "type": "git",
1038 | "url": "https://github.com/php-fig/container.git",
1039 | "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
1040 | },
1041 | "dist": {
1042 | "type": "zip",
1043 | "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
1044 | "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
1045 | "shasum": ""
1046 | },
1047 | "require": {
1048 | "php": ">=7.4.0"
1049 | },
1050 | "type": "library",
1051 | "autoload": {
1052 | "psr-4": {
1053 | "Psr\\Container\\": "src/"
1054 | }
1055 | },
1056 | "notification-url": "https://packagist.org/downloads/",
1057 | "license": [
1058 | "MIT"
1059 | ],
1060 | "authors": [
1061 | {
1062 | "name": "PHP-FIG",
1063 | "homepage": "https://www.php-fig.org/"
1064 | }
1065 | ],
1066 | "description": "Common Container Interface (PHP FIG PSR-11)",
1067 | "homepage": "https://github.com/php-fig/container",
1068 | "keywords": [
1069 | "PSR-11",
1070 | "container",
1071 | "container-interface",
1072 | "container-interop",
1073 | "psr"
1074 | ],
1075 | "support": {
1076 | "issues": "https://github.com/php-fig/container/issues",
1077 | "source": "https://github.com/php-fig/container/tree/1.1.2"
1078 | },
1079 | "time": "2021-11-05T16:50:12+00:00"
1080 | },
1081 | {
1082 | "name": "psr/http-message",
1083 | "version": "1.0.1",
1084 | "source": {
1085 | "type": "git",
1086 | "url": "https://github.com/php-fig/http-message.git",
1087 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
1088 | },
1089 | "dist": {
1090 | "type": "zip",
1091 | "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
1092 | "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
1093 | "shasum": ""
1094 | },
1095 | "require": {
1096 | "php": ">=5.3.0"
1097 | },
1098 | "type": "library",
1099 | "extra": {
1100 | "branch-alias": {
1101 | "dev-master": "1.0.x-dev"
1102 | }
1103 | },
1104 | "autoload": {
1105 | "psr-4": {
1106 | "Psr\\Http\\Message\\": "src/"
1107 | }
1108 | },
1109 | "notification-url": "https://packagist.org/downloads/",
1110 | "license": [
1111 | "MIT"
1112 | ],
1113 | "authors": [
1114 | {
1115 | "name": "PHP-FIG",
1116 | "homepage": "http://www.php-fig.org/"
1117 | }
1118 | ],
1119 | "description": "Common interface for HTTP messages",
1120 | "homepage": "https://github.com/php-fig/http-message",
1121 | "keywords": [
1122 | "http",
1123 | "http-message",
1124 | "psr",
1125 | "psr-7",
1126 | "request",
1127 | "response"
1128 | ],
1129 | "support": {
1130 | "source": "https://github.com/php-fig/http-message/tree/master"
1131 | },
1132 | "time": "2016-08-06T14:39:51+00:00"
1133 | },
1134 | {
1135 | "name": "ralouphie/getallheaders",
1136 | "version": "3.0.3",
1137 | "source": {
1138 | "type": "git",
1139 | "url": "https://github.com/ralouphie/getallheaders.git",
1140 | "reference": "120b605dfeb996808c31b6477290a714d356e822"
1141 | },
1142 | "dist": {
1143 | "type": "zip",
1144 | "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
1145 | "reference": "120b605dfeb996808c31b6477290a714d356e822",
1146 | "shasum": ""
1147 | },
1148 | "require": {
1149 | "php": ">=5.6"
1150 | },
1151 | "require-dev": {
1152 | "php-coveralls/php-coveralls": "^2.1",
1153 | "phpunit/phpunit": "^5 || ^6.5"
1154 | },
1155 | "type": "library",
1156 | "autoload": {
1157 | "files": [
1158 | "src/getallheaders.php"
1159 | ]
1160 | },
1161 | "notification-url": "https://packagist.org/downloads/",
1162 | "license": [
1163 | "MIT"
1164 | ],
1165 | "authors": [
1166 | {
1167 | "name": "Ralph Khattar",
1168 | "email": "ralph.khattar@gmail.com"
1169 | }
1170 | ],
1171 | "description": "A polyfill for getallheaders.",
1172 | "support": {
1173 | "issues": "https://github.com/ralouphie/getallheaders/issues",
1174 | "source": "https://github.com/ralouphie/getallheaders/tree/develop"
1175 | },
1176 | "time": "2019-03-08T08:55:37+00:00"
1177 | },
1178 | {
1179 | "name": "sebastian/code-unit-reverse-lookup",
1180 | "version": "1.0.2",
1181 | "source": {
1182 | "type": "git",
1183 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
1184 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619"
1185 | },
1186 | "dist": {
1187 | "type": "zip",
1188 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619",
1189 | "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619",
1190 | "shasum": ""
1191 | },
1192 | "require": {
1193 | "php": ">=5.6"
1194 | },
1195 | "require-dev": {
1196 | "phpunit/phpunit": "^8.5"
1197 | },
1198 | "type": "library",
1199 | "extra": {
1200 | "branch-alias": {
1201 | "dev-master": "1.0.x-dev"
1202 | }
1203 | },
1204 | "autoload": {
1205 | "classmap": [
1206 | "src/"
1207 | ]
1208 | },
1209 | "notification-url": "https://packagist.org/downloads/",
1210 | "license": [
1211 | "BSD-3-Clause"
1212 | ],
1213 | "authors": [
1214 | {
1215 | "name": "Sebastian Bergmann",
1216 | "email": "sebastian@phpunit.de"
1217 | }
1218 | ],
1219 | "description": "Looks up which function or method a line of code belongs to",
1220 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
1221 | "support": {
1222 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
1223 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
1224 | },
1225 | "funding": [
1226 | {
1227 | "url": "https://github.com/sebastianbergmann",
1228 | "type": "github"
1229 | }
1230 | ],
1231 | "time": "2020-11-30T08:15:22+00:00"
1232 | },
1233 | {
1234 | "name": "sebastian/comparator",
1235 | "version": "3.0.3",
1236 | "source": {
1237 | "type": "git",
1238 | "url": "https://github.com/sebastianbergmann/comparator.git",
1239 | "reference": "1071dfcef776a57013124ff35e1fc41ccd294758"
1240 | },
1241 | "dist": {
1242 | "type": "zip",
1243 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758",
1244 | "reference": "1071dfcef776a57013124ff35e1fc41ccd294758",
1245 | "shasum": ""
1246 | },
1247 | "require": {
1248 | "php": ">=7.1",
1249 | "sebastian/diff": "^3.0",
1250 | "sebastian/exporter": "^3.1"
1251 | },
1252 | "require-dev": {
1253 | "phpunit/phpunit": "^8.5"
1254 | },
1255 | "type": "library",
1256 | "extra": {
1257 | "branch-alias": {
1258 | "dev-master": "3.0-dev"
1259 | }
1260 | },
1261 | "autoload": {
1262 | "classmap": [
1263 | "src/"
1264 | ]
1265 | },
1266 | "notification-url": "https://packagist.org/downloads/",
1267 | "license": [
1268 | "BSD-3-Clause"
1269 | ],
1270 | "authors": [
1271 | {
1272 | "name": "Sebastian Bergmann",
1273 | "email": "sebastian@phpunit.de"
1274 | },
1275 | {
1276 | "name": "Jeff Welch",
1277 | "email": "whatthejeff@gmail.com"
1278 | },
1279 | {
1280 | "name": "Volker Dusch",
1281 | "email": "github@wallbash.com"
1282 | },
1283 | {
1284 | "name": "Bernhard Schussek",
1285 | "email": "bschussek@2bepublished.at"
1286 | }
1287 | ],
1288 | "description": "Provides the functionality to compare PHP values for equality",
1289 | "homepage": "https://github.com/sebastianbergmann/comparator",
1290 | "keywords": [
1291 | "comparator",
1292 | "compare",
1293 | "equality"
1294 | ],
1295 | "support": {
1296 | "issues": "https://github.com/sebastianbergmann/comparator/issues",
1297 | "source": "https://github.com/sebastianbergmann/comparator/tree/3.0.3"
1298 | },
1299 | "funding": [
1300 | {
1301 | "url": "https://github.com/sebastianbergmann",
1302 | "type": "github"
1303 | }
1304 | ],
1305 | "time": "2020-11-30T08:04:30+00:00"
1306 | },
1307 | {
1308 | "name": "sebastian/diff",
1309 | "version": "3.0.3",
1310 | "source": {
1311 | "type": "git",
1312 | "url": "https://github.com/sebastianbergmann/diff.git",
1313 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211"
1314 | },
1315 | "dist": {
1316 | "type": "zip",
1317 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
1318 | "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211",
1319 | "shasum": ""
1320 | },
1321 | "require": {
1322 | "php": ">=7.1"
1323 | },
1324 | "require-dev": {
1325 | "phpunit/phpunit": "^7.5 || ^8.0",
1326 | "symfony/process": "^2 || ^3.3 || ^4"
1327 | },
1328 | "type": "library",
1329 | "extra": {
1330 | "branch-alias": {
1331 | "dev-master": "3.0-dev"
1332 | }
1333 | },
1334 | "autoload": {
1335 | "classmap": [
1336 | "src/"
1337 | ]
1338 | },
1339 | "notification-url": "https://packagist.org/downloads/",
1340 | "license": [
1341 | "BSD-3-Clause"
1342 | ],
1343 | "authors": [
1344 | {
1345 | "name": "Sebastian Bergmann",
1346 | "email": "sebastian@phpunit.de"
1347 | },
1348 | {
1349 | "name": "Kore Nordmann",
1350 | "email": "mail@kore-nordmann.de"
1351 | }
1352 | ],
1353 | "description": "Diff implementation",
1354 | "homepage": "https://github.com/sebastianbergmann/diff",
1355 | "keywords": [
1356 | "diff",
1357 | "udiff",
1358 | "unidiff",
1359 | "unified diff"
1360 | ],
1361 | "support": {
1362 | "issues": "https://github.com/sebastianbergmann/diff/issues",
1363 | "source": "https://github.com/sebastianbergmann/diff/tree/3.0.3"
1364 | },
1365 | "funding": [
1366 | {
1367 | "url": "https://github.com/sebastianbergmann",
1368 | "type": "github"
1369 | }
1370 | ],
1371 | "time": "2020-11-30T07:59:04+00:00"
1372 | },
1373 | {
1374 | "name": "sebastian/environment",
1375 | "version": "4.2.4",
1376 | "source": {
1377 | "type": "git",
1378 | "url": "https://github.com/sebastianbergmann/environment.git",
1379 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0"
1380 | },
1381 | "dist": {
1382 | "type": "zip",
1383 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
1384 | "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0",
1385 | "shasum": ""
1386 | },
1387 | "require": {
1388 | "php": ">=7.1"
1389 | },
1390 | "require-dev": {
1391 | "phpunit/phpunit": "^7.5"
1392 | },
1393 | "suggest": {
1394 | "ext-posix": "*"
1395 | },
1396 | "type": "library",
1397 | "extra": {
1398 | "branch-alias": {
1399 | "dev-master": "4.2-dev"
1400 | }
1401 | },
1402 | "autoload": {
1403 | "classmap": [
1404 | "src/"
1405 | ]
1406 | },
1407 | "notification-url": "https://packagist.org/downloads/",
1408 | "license": [
1409 | "BSD-3-Clause"
1410 | ],
1411 | "authors": [
1412 | {
1413 | "name": "Sebastian Bergmann",
1414 | "email": "sebastian@phpunit.de"
1415 | }
1416 | ],
1417 | "description": "Provides functionality to handle HHVM/PHP environments",
1418 | "homepage": "http://www.github.com/sebastianbergmann/environment",
1419 | "keywords": [
1420 | "Xdebug",
1421 | "environment",
1422 | "hhvm"
1423 | ],
1424 | "support": {
1425 | "issues": "https://github.com/sebastianbergmann/environment/issues",
1426 | "source": "https://github.com/sebastianbergmann/environment/tree/4.2.4"
1427 | },
1428 | "funding": [
1429 | {
1430 | "url": "https://github.com/sebastianbergmann",
1431 | "type": "github"
1432 | }
1433 | ],
1434 | "time": "2020-11-30T07:53:42+00:00"
1435 | },
1436 | {
1437 | "name": "sebastian/exporter",
1438 | "version": "3.1.4",
1439 | "source": {
1440 | "type": "git",
1441 | "url": "https://github.com/sebastianbergmann/exporter.git",
1442 | "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db"
1443 | },
1444 | "dist": {
1445 | "type": "zip",
1446 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
1447 | "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
1448 | "shasum": ""
1449 | },
1450 | "require": {
1451 | "php": ">=7.0",
1452 | "sebastian/recursion-context": "^3.0"
1453 | },
1454 | "require-dev": {
1455 | "ext-mbstring": "*",
1456 | "phpunit/phpunit": "^8.5"
1457 | },
1458 | "type": "library",
1459 | "extra": {
1460 | "branch-alias": {
1461 | "dev-master": "3.1.x-dev"
1462 | }
1463 | },
1464 | "autoload": {
1465 | "classmap": [
1466 | "src/"
1467 | ]
1468 | },
1469 | "notification-url": "https://packagist.org/downloads/",
1470 | "license": [
1471 | "BSD-3-Clause"
1472 | ],
1473 | "authors": [
1474 | {
1475 | "name": "Sebastian Bergmann",
1476 | "email": "sebastian@phpunit.de"
1477 | },
1478 | {
1479 | "name": "Jeff Welch",
1480 | "email": "whatthejeff@gmail.com"
1481 | },
1482 | {
1483 | "name": "Volker Dusch",
1484 | "email": "github@wallbash.com"
1485 | },
1486 | {
1487 | "name": "Adam Harvey",
1488 | "email": "aharvey@php.net"
1489 | },
1490 | {
1491 | "name": "Bernhard Schussek",
1492 | "email": "bschussek@gmail.com"
1493 | }
1494 | ],
1495 | "description": "Provides the functionality to export PHP variables for visualization",
1496 | "homepage": "http://www.github.com/sebastianbergmann/exporter",
1497 | "keywords": [
1498 | "export",
1499 | "exporter"
1500 | ],
1501 | "support": {
1502 | "issues": "https://github.com/sebastianbergmann/exporter/issues",
1503 | "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4"
1504 | },
1505 | "funding": [
1506 | {
1507 | "url": "https://github.com/sebastianbergmann",
1508 | "type": "github"
1509 | }
1510 | ],
1511 | "time": "2021-11-11T13:51:24+00:00"
1512 | },
1513 | {
1514 | "name": "sebastian/global-state",
1515 | "version": "3.0.2",
1516 | "source": {
1517 | "type": "git",
1518 | "url": "https://github.com/sebastianbergmann/global-state.git",
1519 | "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921"
1520 | },
1521 | "dist": {
1522 | "type": "zip",
1523 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/de036ec91d55d2a9e0db2ba975b512cdb1c23921",
1524 | "reference": "de036ec91d55d2a9e0db2ba975b512cdb1c23921",
1525 | "shasum": ""
1526 | },
1527 | "require": {
1528 | "php": ">=7.2",
1529 | "sebastian/object-reflector": "^1.1.1",
1530 | "sebastian/recursion-context": "^3.0"
1531 | },
1532 | "require-dev": {
1533 | "ext-dom": "*",
1534 | "phpunit/phpunit": "^8.0"
1535 | },
1536 | "suggest": {
1537 | "ext-uopz": "*"
1538 | },
1539 | "type": "library",
1540 | "extra": {
1541 | "branch-alias": {
1542 | "dev-master": "3.0-dev"
1543 | }
1544 | },
1545 | "autoload": {
1546 | "classmap": [
1547 | "src/"
1548 | ]
1549 | },
1550 | "notification-url": "https://packagist.org/downloads/",
1551 | "license": [
1552 | "BSD-3-Clause"
1553 | ],
1554 | "authors": [
1555 | {
1556 | "name": "Sebastian Bergmann",
1557 | "email": "sebastian@phpunit.de"
1558 | }
1559 | ],
1560 | "description": "Snapshotting of global state",
1561 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1562 | "keywords": [
1563 | "global state"
1564 | ],
1565 | "support": {
1566 | "issues": "https://github.com/sebastianbergmann/global-state/issues",
1567 | "source": "https://github.com/sebastianbergmann/global-state/tree/3.0.2"
1568 | },
1569 | "funding": [
1570 | {
1571 | "url": "https://github.com/sebastianbergmann",
1572 | "type": "github"
1573 | }
1574 | ],
1575 | "time": "2022-02-10T06:55:38+00:00"
1576 | },
1577 | {
1578 | "name": "sebastian/object-enumerator",
1579 | "version": "3.0.4",
1580 | "source": {
1581 | "type": "git",
1582 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1583 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2"
1584 | },
1585 | "dist": {
1586 | "type": "zip",
1587 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
1588 | "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2",
1589 | "shasum": ""
1590 | },
1591 | "require": {
1592 | "php": ">=7.0",
1593 | "sebastian/object-reflector": "^1.1.1",
1594 | "sebastian/recursion-context": "^3.0"
1595 | },
1596 | "require-dev": {
1597 | "phpunit/phpunit": "^6.0"
1598 | },
1599 | "type": "library",
1600 | "extra": {
1601 | "branch-alias": {
1602 | "dev-master": "3.0.x-dev"
1603 | }
1604 | },
1605 | "autoload": {
1606 | "classmap": [
1607 | "src/"
1608 | ]
1609 | },
1610 | "notification-url": "https://packagist.org/downloads/",
1611 | "license": [
1612 | "BSD-3-Clause"
1613 | ],
1614 | "authors": [
1615 | {
1616 | "name": "Sebastian Bergmann",
1617 | "email": "sebastian@phpunit.de"
1618 | }
1619 | ],
1620 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1621 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1622 | "support": {
1623 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
1624 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4"
1625 | },
1626 | "funding": [
1627 | {
1628 | "url": "https://github.com/sebastianbergmann",
1629 | "type": "github"
1630 | }
1631 | ],
1632 | "time": "2020-11-30T07:40:27+00:00"
1633 | },
1634 | {
1635 | "name": "sebastian/object-reflector",
1636 | "version": "1.1.2",
1637 | "source": {
1638 | "type": "git",
1639 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
1640 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d"
1641 | },
1642 | "dist": {
1643 | "type": "zip",
1644 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
1645 | "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d",
1646 | "shasum": ""
1647 | },
1648 | "require": {
1649 | "php": ">=7.0"
1650 | },
1651 | "require-dev": {
1652 | "phpunit/phpunit": "^6.0"
1653 | },
1654 | "type": "library",
1655 | "extra": {
1656 | "branch-alias": {
1657 | "dev-master": "1.1-dev"
1658 | }
1659 | },
1660 | "autoload": {
1661 | "classmap": [
1662 | "src/"
1663 | ]
1664 | },
1665 | "notification-url": "https://packagist.org/downloads/",
1666 | "license": [
1667 | "BSD-3-Clause"
1668 | ],
1669 | "authors": [
1670 | {
1671 | "name": "Sebastian Bergmann",
1672 | "email": "sebastian@phpunit.de"
1673 | }
1674 | ],
1675 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
1676 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1677 | "support": {
1678 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
1679 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2"
1680 | },
1681 | "funding": [
1682 | {
1683 | "url": "https://github.com/sebastianbergmann",
1684 | "type": "github"
1685 | }
1686 | ],
1687 | "time": "2020-11-30T07:37:18+00:00"
1688 | },
1689 | {
1690 | "name": "sebastian/recursion-context",
1691 | "version": "3.0.1",
1692 | "source": {
1693 | "type": "git",
1694 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1695 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb"
1696 | },
1697 | "dist": {
1698 | "type": "zip",
1699 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb",
1700 | "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb",
1701 | "shasum": ""
1702 | },
1703 | "require": {
1704 | "php": ">=7.0"
1705 | },
1706 | "require-dev": {
1707 | "phpunit/phpunit": "^6.0"
1708 | },
1709 | "type": "library",
1710 | "extra": {
1711 | "branch-alias": {
1712 | "dev-master": "3.0.x-dev"
1713 | }
1714 | },
1715 | "autoload": {
1716 | "classmap": [
1717 | "src/"
1718 | ]
1719 | },
1720 | "notification-url": "https://packagist.org/downloads/",
1721 | "license": [
1722 | "BSD-3-Clause"
1723 | ],
1724 | "authors": [
1725 | {
1726 | "name": "Sebastian Bergmann",
1727 | "email": "sebastian@phpunit.de"
1728 | },
1729 | {
1730 | "name": "Jeff Welch",
1731 | "email": "whatthejeff@gmail.com"
1732 | },
1733 | {
1734 | "name": "Adam Harvey",
1735 | "email": "aharvey@php.net"
1736 | }
1737 | ],
1738 | "description": "Provides functionality to recursively process PHP variables",
1739 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1740 | "support": {
1741 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
1742 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1"
1743 | },
1744 | "funding": [
1745 | {
1746 | "url": "https://github.com/sebastianbergmann",
1747 | "type": "github"
1748 | }
1749 | ],
1750 | "time": "2020-11-30T07:34:24+00:00"
1751 | },
1752 | {
1753 | "name": "sebastian/resource-operations",
1754 | "version": "2.0.2",
1755 | "source": {
1756 | "type": "git",
1757 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1758 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3"
1759 | },
1760 | "dist": {
1761 | "type": "zip",
1762 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3",
1763 | "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3",
1764 | "shasum": ""
1765 | },
1766 | "require": {
1767 | "php": ">=7.1"
1768 | },
1769 | "type": "library",
1770 | "extra": {
1771 | "branch-alias": {
1772 | "dev-master": "2.0-dev"
1773 | }
1774 | },
1775 | "autoload": {
1776 | "classmap": [
1777 | "src/"
1778 | ]
1779 | },
1780 | "notification-url": "https://packagist.org/downloads/",
1781 | "license": [
1782 | "BSD-3-Clause"
1783 | ],
1784 | "authors": [
1785 | {
1786 | "name": "Sebastian Bergmann",
1787 | "email": "sebastian@phpunit.de"
1788 | }
1789 | ],
1790 | "description": "Provides a list of PHP built-in functions that operate on resources",
1791 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1792 | "support": {
1793 | "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
1794 | "source": "https://github.com/sebastianbergmann/resource-operations/tree/2.0.2"
1795 | },
1796 | "funding": [
1797 | {
1798 | "url": "https://github.com/sebastianbergmann",
1799 | "type": "github"
1800 | }
1801 | ],
1802 | "time": "2020-11-30T07:30:19+00:00"
1803 | },
1804 | {
1805 | "name": "sebastian/type",
1806 | "version": "1.1.4",
1807 | "source": {
1808 | "type": "git",
1809 | "url": "https://github.com/sebastianbergmann/type.git",
1810 | "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4"
1811 | },
1812 | "dist": {
1813 | "type": "zip",
1814 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/0150cfbc4495ed2df3872fb31b26781e4e077eb4",
1815 | "reference": "0150cfbc4495ed2df3872fb31b26781e4e077eb4",
1816 | "shasum": ""
1817 | },
1818 | "require": {
1819 | "php": ">=7.2"
1820 | },
1821 | "require-dev": {
1822 | "phpunit/phpunit": "^8.2"
1823 | },
1824 | "type": "library",
1825 | "extra": {
1826 | "branch-alias": {
1827 | "dev-master": "1.1-dev"
1828 | }
1829 | },
1830 | "autoload": {
1831 | "classmap": [
1832 | "src/"
1833 | ]
1834 | },
1835 | "notification-url": "https://packagist.org/downloads/",
1836 | "license": [
1837 | "BSD-3-Clause"
1838 | ],
1839 | "authors": [
1840 | {
1841 | "name": "Sebastian Bergmann",
1842 | "email": "sebastian@phpunit.de",
1843 | "role": "lead"
1844 | }
1845 | ],
1846 | "description": "Collection of value objects that represent the types of the PHP type system",
1847 | "homepage": "https://github.com/sebastianbergmann/type",
1848 | "support": {
1849 | "issues": "https://github.com/sebastianbergmann/type/issues",
1850 | "source": "https://github.com/sebastianbergmann/type/tree/1.1.4"
1851 | },
1852 | "funding": [
1853 | {
1854 | "url": "https://github.com/sebastianbergmann",
1855 | "type": "github"
1856 | }
1857 | ],
1858 | "time": "2020-11-30T07:25:11+00:00"
1859 | },
1860 | {
1861 | "name": "sebastian/version",
1862 | "version": "2.0.1",
1863 | "source": {
1864 | "type": "git",
1865 | "url": "https://github.com/sebastianbergmann/version.git",
1866 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
1867 | },
1868 | "dist": {
1869 | "type": "zip",
1870 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
1871 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
1872 | "shasum": ""
1873 | },
1874 | "require": {
1875 | "php": ">=5.6"
1876 | },
1877 | "type": "library",
1878 | "extra": {
1879 | "branch-alias": {
1880 | "dev-master": "2.0.x-dev"
1881 | }
1882 | },
1883 | "autoload": {
1884 | "classmap": [
1885 | "src/"
1886 | ]
1887 | },
1888 | "notification-url": "https://packagist.org/downloads/",
1889 | "license": [
1890 | "BSD-3-Clause"
1891 | ],
1892 | "authors": [
1893 | {
1894 | "name": "Sebastian Bergmann",
1895 | "email": "sebastian@phpunit.de",
1896 | "role": "lead"
1897 | }
1898 | ],
1899 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1900 | "homepage": "https://github.com/sebastianbergmann/version",
1901 | "support": {
1902 | "issues": "https://github.com/sebastianbergmann/version/issues",
1903 | "source": "https://github.com/sebastianbergmann/version/tree/master"
1904 | },
1905 | "time": "2016-10-03T07:35:21+00:00"
1906 | },
1907 | {
1908 | "name": "slim/slim",
1909 | "version": "3.12.3",
1910 | "source": {
1911 | "type": "git",
1912 | "url": "https://github.com/slimphp/Slim.git",
1913 | "reference": "1c9318a84ffb890900901136d620b4f03a59da38"
1914 | },
1915 | "dist": {
1916 | "type": "zip",
1917 | "url": "https://api.github.com/repos/slimphp/Slim/zipball/1c9318a84ffb890900901136d620b4f03a59da38",
1918 | "reference": "1c9318a84ffb890900901136d620b4f03a59da38",
1919 | "shasum": ""
1920 | },
1921 | "require": {
1922 | "ext-json": "*",
1923 | "ext-libxml": "*",
1924 | "ext-simplexml": "*",
1925 | "nikic/fast-route": "^1.0",
1926 | "php": ">=5.5.0",
1927 | "pimple/pimple": "^3.0",
1928 | "psr/container": "^1.0",
1929 | "psr/http-message": "^1.0"
1930 | },
1931 | "provide": {
1932 | "psr/http-message-implementation": "1.0"
1933 | },
1934 | "require-dev": {
1935 | "phpunit/phpunit": "^4.0",
1936 | "squizlabs/php_codesniffer": "^2.5"
1937 | },
1938 | "type": "library",
1939 | "autoload": {
1940 | "psr-4": {
1941 | "Slim\\": "Slim"
1942 | }
1943 | },
1944 | "notification-url": "https://packagist.org/downloads/",
1945 | "license": [
1946 | "MIT"
1947 | ],
1948 | "authors": [
1949 | {
1950 | "name": "Josh Lockhart",
1951 | "email": "hello@joshlockhart.com",
1952 | "homepage": "https://joshlockhart.com"
1953 | },
1954 | {
1955 | "name": "Andrew Smith",
1956 | "email": "a.smith@silentworks.co.uk",
1957 | "homepage": "http://silentworks.co.uk"
1958 | },
1959 | {
1960 | "name": "Rob Allen",
1961 | "email": "rob@akrabat.com",
1962 | "homepage": "http://akrabat.com"
1963 | },
1964 | {
1965 | "name": "Gabriel Manricks",
1966 | "email": "gmanricks@me.com",
1967 | "homepage": "http://gabrielmanricks.com"
1968 | }
1969 | ],
1970 | "description": "Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs",
1971 | "homepage": "https://slimframework.com",
1972 | "keywords": [
1973 | "api",
1974 | "framework",
1975 | "micro",
1976 | "router"
1977 | ],
1978 | "support": {
1979 | "issues": "https://github.com/slimphp/Slim/issues",
1980 | "source": "https://github.com/slimphp/Slim/tree/3.x"
1981 | },
1982 | "time": "2019-11-28T17:40:33+00:00"
1983 | },
1984 | {
1985 | "name": "symfony/polyfill-ctype",
1986 | "version": "v1.25.0",
1987 | "source": {
1988 | "type": "git",
1989 | "url": "https://github.com/symfony/polyfill-ctype.git",
1990 | "reference": "30885182c981ab175d4d034db0f6f469898070ab"
1991 | },
1992 | "dist": {
1993 | "type": "zip",
1994 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
1995 | "reference": "30885182c981ab175d4d034db0f6f469898070ab",
1996 | "shasum": ""
1997 | },
1998 | "require": {
1999 | "php": ">=7.1"
2000 | },
2001 | "provide": {
2002 | "ext-ctype": "*"
2003 | },
2004 | "suggest": {
2005 | "ext-ctype": "For best performance"
2006 | },
2007 | "type": "library",
2008 | "extra": {
2009 | "branch-alias": {
2010 | "dev-main": "1.23-dev"
2011 | },
2012 | "thanks": {
2013 | "name": "symfony/polyfill",
2014 | "url": "https://github.com/symfony/polyfill"
2015 | }
2016 | },
2017 | "autoload": {
2018 | "files": [
2019 | "bootstrap.php"
2020 | ],
2021 | "psr-4": {
2022 | "Symfony\\Polyfill\\Ctype\\": ""
2023 | }
2024 | },
2025 | "notification-url": "https://packagist.org/downloads/",
2026 | "license": [
2027 | "MIT"
2028 | ],
2029 | "authors": [
2030 | {
2031 | "name": "Gert de Pagter",
2032 | "email": "BackEndTea@gmail.com"
2033 | },
2034 | {
2035 | "name": "Symfony Community",
2036 | "homepage": "https://symfony.com/contributors"
2037 | }
2038 | ],
2039 | "description": "Symfony polyfill for ctype functions",
2040 | "homepage": "https://symfony.com",
2041 | "keywords": [
2042 | "compatibility",
2043 | "ctype",
2044 | "polyfill",
2045 | "portable"
2046 | ],
2047 | "support": {
2048 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
2049 | },
2050 | "funding": [
2051 | {
2052 | "url": "https://symfony.com/sponsor",
2053 | "type": "custom"
2054 | },
2055 | {
2056 | "url": "https://github.com/fabpot",
2057 | "type": "github"
2058 | },
2059 | {
2060 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
2061 | "type": "tidelift"
2062 | }
2063 | ],
2064 | "time": "2021-10-20T20:35:02+00:00"
2065 | },
2066 | {
2067 | "name": "theseer/tokenizer",
2068 | "version": "1.2.1",
2069 | "source": {
2070 | "type": "git",
2071 | "url": "https://github.com/theseer/tokenizer.git",
2072 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
2073 | },
2074 | "dist": {
2075 | "type": "zip",
2076 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
2077 | "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
2078 | "shasum": ""
2079 | },
2080 | "require": {
2081 | "ext-dom": "*",
2082 | "ext-tokenizer": "*",
2083 | "ext-xmlwriter": "*",
2084 | "php": "^7.2 || ^8.0"
2085 | },
2086 | "type": "library",
2087 | "autoload": {
2088 | "classmap": [
2089 | "src/"
2090 | ]
2091 | },
2092 | "notification-url": "https://packagist.org/downloads/",
2093 | "license": [
2094 | "BSD-3-Clause"
2095 | ],
2096 | "authors": [
2097 | {
2098 | "name": "Arne Blankerts",
2099 | "email": "arne@blankerts.de",
2100 | "role": "Developer"
2101 | }
2102 | ],
2103 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
2104 | "support": {
2105 | "issues": "https://github.com/theseer/tokenizer/issues",
2106 | "source": "https://github.com/theseer/tokenizer/tree/1.2.1"
2107 | },
2108 | "funding": [
2109 | {
2110 | "url": "https://github.com/theseer",
2111 | "type": "github"
2112 | }
2113 | ],
2114 | "time": "2021-07-28T10:34:58+00:00"
2115 | },
2116 | {
2117 | "name": "webmozart/assert",
2118 | "version": "1.10.0",
2119 | "source": {
2120 | "type": "git",
2121 | "url": "https://github.com/webmozarts/assert.git",
2122 | "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
2123 | },
2124 | "dist": {
2125 | "type": "zip",
2126 | "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
2127 | "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
2128 | "shasum": ""
2129 | },
2130 | "require": {
2131 | "php": "^7.2 || ^8.0",
2132 | "symfony/polyfill-ctype": "^1.8"
2133 | },
2134 | "conflict": {
2135 | "phpstan/phpstan": "<0.12.20",
2136 | "vimeo/psalm": "<4.6.1 || 4.6.2"
2137 | },
2138 | "require-dev": {
2139 | "phpunit/phpunit": "^8.5.13"
2140 | },
2141 | "type": "library",
2142 | "extra": {
2143 | "branch-alias": {
2144 | "dev-master": "1.10-dev"
2145 | }
2146 | },
2147 | "autoload": {
2148 | "psr-4": {
2149 | "Webmozart\\Assert\\": "src/"
2150 | }
2151 | },
2152 | "notification-url": "https://packagist.org/downloads/",
2153 | "license": [
2154 | "MIT"
2155 | ],
2156 | "authors": [
2157 | {
2158 | "name": "Bernhard Schussek",
2159 | "email": "bschussek@gmail.com"
2160 | }
2161 | ],
2162 | "description": "Assertions to validate method input/output with nice error messages.",
2163 | "keywords": [
2164 | "assert",
2165 | "check",
2166 | "validate"
2167 | ],
2168 | "support": {
2169 | "issues": "https://github.com/webmozarts/assert/issues",
2170 | "source": "https://github.com/webmozarts/assert/tree/1.10.0"
2171 | },
2172 | "time": "2021-03-09T10:59:23+00:00"
2173 | }
2174 | ],
2175 | "aliases": [],
2176 | "minimum-stability": "stable",
2177 | "stability-flags": [],
2178 | "prefer-stable": false,
2179 | "prefer-lowest": false,
2180 | "platform": [],
2181 | "platform-dev": [],
2182 | "plugin-api-version": "2.3.0"
2183 | }
2184 |
--------------------------------------------------------------------------------
/php.ini:
--------------------------------------------------------------------------------
1 | display_errors=On
2 | extension=curl.so
3 | extension_dir=/opt/lib/php/8.0/modules
4 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | ./tests/
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/tests/BootstrapTest.php:
--------------------------------------------------------------------------------
1 | start();
24 |
25 | putenv('AWS_LAMBDA_RUNTIME_API=' . self::$server->getHost() . ':' . self::$server->getPort());
26 | putenv('LAMBDA_TASK_ROOT=' . __DIR__ . '/_files');
27 | putenv('MAX_EXECUTION_TIME=5');
28 | if (!file_exists('/var/task/php.ini')) {
29 | // use default environment settings if not running inside test container
30 | putenv('CONFIG_PATH=/dev/null');
31 | }
32 | putenv('_HANDLER=public/index.php');
33 | $loop = false;
34 |
35 | ob_start(); // capture output to avoid echo'ing the shebang at top of bootstrap
36 | if (file_exists('/opt/bootstrap')) {
37 | require '/opt/bootstrap';
38 | } else {
39 | require __DIR__ . '/../bootstrap';
40 | }
41 | ob_end_clean();
42 | }
43 |
44 | /**
45 | * Helper function for setting the next response from the Lambda runtime API
46 | * next invocation endpoint.
47 | */
48 | private function setNextInvocation(array $response): string
49 | {
50 | $id = sprintf(
51 | '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
52 | mt_rand(0, 0xffff),
53 | mt_rand(0, 0xffff),
54 | mt_rand(0, 0xffff),
55 | mt_rand(0, 0x0fff) | 0x4000,
56 | mt_rand(0, 0x3fff) | 0x8000,
57 | mt_rand(0, 0xffff),
58 | mt_rand(0, 0xffff),
59 | mt_rand(0, 0xffff)
60 | );
61 |
62 | $body = json_encode($response);
63 | $headers = ['Lambda-Runtime-Aws-Request-Id' => $id];
64 | self::$server->setResponseOfPath(
65 | '/' . LAMBDA_RUNTIME_API_VERSION . '/runtime/invocation/next',
66 | new Response($body, $headers, 200)
67 | );
68 |
69 | return $id;
70 | }
71 |
72 | /**
73 | * Helper function for getting and decoding the last response sent to the
74 | * Lambda runtime API invocation response endpoint.
75 | */
76 | private function getInvocationResponse(): \stdclass
77 | {
78 | $request = self::$server->getLastRequest();
79 | if ($request === null) {
80 | $error = new \Exception('No request received');
81 | throw $error;
82 | }
83 |
84 | $body = $request->getInput();
85 | $response = json_decode($body);
86 |
87 | return $response;
88 | }
89 |
90 | /**
91 | * Calls the root path and ensures we get the home page.
92 | */
93 | public function testGetRoot(): void
94 | {
95 | $this->setNextInvocation([
96 | 'requestContext' => [],
97 | 'httpMethod' => 'GET',
98 | 'path' => '/',
99 | 'body' => '',
100 | ]);
101 |
102 | $invocation = handleNextRequest();
103 |
104 | $response = $this->getInvocationResponse();
105 | $this->assertEquals(200, $response->statusCode);
106 | $this->assertEquals('Home', $response->body);
107 | }
108 |
109 | /**
110 | * ???
111 | */
112 | public function testPostRoot(): void
113 | {
114 | $this->setNextInvocation([
115 | 'requestContext' => [],
116 | 'httpMethod' => 'POST',
117 | 'headers' => [
118 | 'content-type' => 'application/x-www-form-urlencoded; charset=UTF-8',
119 | ],
120 | 'path' => '/',
121 | 'body' => 'foo=bar&bar=foo',
122 | ]);
123 |
124 | $invocation = handleNextRequest();
125 |
126 | $response = $this->getInvocationResponse();
127 | $this->assertEquals('barfoo', $response->body);
128 | }
129 |
130 | /**
131 | * Calls a path handled by the dynamic routing and ensure the correct
132 | * response is returned.
133 | */
134 | public function testRouterPath(): void
135 | {
136 | $this->setNextInvocation([
137 | 'requestContext' => [],
138 | 'httpMethod' => 'GET',
139 | 'path' => '/foo/bar/baz',
140 | 'body' => '',
141 | ]);
142 |
143 | $invocation = handleNextRequest();
144 |
145 | $response = $this->getInvocationResponse();
146 | $this->assertEquals('foo/bar/baz', $response->body);
147 | }
148 |
149 | /**
150 | * Calls a path that refers to an explicit PHP script and ensure the
151 | * correct response is returned.
152 | */
153 | public function testExplicitPath(): void
154 | {
155 | $this->setNextInvocation([
156 | 'requestContext' => [],
157 | 'httpMethod' => 'GET',
158 | 'path' => '/subdir/index.php',
159 | 'body' => '',
160 | ]);
161 |
162 | $invocation = handleNextRequest();
163 |
164 | $response = $this->getInvocationResponse();
165 | $this->assertEquals(200, $response->statusCode);
166 | $this->assertEquals('subdir', $response->body);
167 | }
168 |
169 | /**
170 | * Calls the boostrap as if the incoming event is coming from API Gateway
171 | * (no `requestContext` property), ensuring `statusDescription` is not set.
172 | */
173 | public function testNoStatusDescriptionHeaderForAPIGateway(): void
174 | {
175 | $this->setNextInvocation([
176 | 'httpMethod' => 'GET',
177 | 'path' => '/',
178 | 'body' => '',
179 | ]);
180 |
181 | $invocation = handleNextRequest();
182 |
183 | $response = $this->getInvocationResponse();
184 | $this->assertFalse(property_exists($response, 'statusDescription'));
185 | }
186 |
187 | /**
188 | * Calls the boostrap as if the incoming event is coming from ALB (includes
189 | * a `requestContext` property), ensuring `statusDescription` is set.
190 | */
191 | public function testStatusDescriptionHeaderForALBRequest(): void
192 | {
193 | $this->setNextInvocation([
194 | 'requestContext' => [],
195 | 'httpMethod' => 'GET',
196 | 'path' => '/',
197 | 'body' => '',
198 | ]);
199 |
200 | $invocation = handleNextRequest();
201 |
202 | $response = $this->getInvocationResponse();
203 | $this->assertEquals('200 OK', $response->statusDescription);
204 | }
205 |
206 | /**
207 | * Calls the bootstrap with a path that matches a directory inside the
208 | * handler path that contains an index.php script.
209 | */
210 | public function testSubDirectoryWithIndex(): void
211 | {
212 | $this->setNextInvocation([
213 | 'requestContext' => [],
214 | 'httpMethod' => 'GET',
215 | 'path' => '/subdir/',
216 | 'body' => '',
217 | ]);
218 |
219 | $invocation = handleNextRequest();
220 |
221 | $response = $this->getInvocationResponse();
222 | $this->assertEquals('subdir', $response->body);
223 | }
224 |
225 | /**
226 | * Requests a path that matches a directory inside the handler path that
227 | * contains an index.php script, but without a trailing slash.
228 | */
229 | public function testSubDirectoryWithIndexWithoutTrailingSlash(): void
230 | {
231 | $this->setNextInvocation([
232 | 'requestContext' => [],
233 | 'httpMethod' => 'GET',
234 | 'path' => '/subdir',
235 | 'body' => '',
236 | ]);
237 |
238 | $invocation = handleNextRequest();
239 |
240 | $response = $this->getInvocationResponse();
241 | $this->assertEquals(301, $response->statusCode);
242 | $this->assertEquals('/subdir/', $response->headers->Location);
243 | }
244 |
245 | /**
246 | * Requests a path that returns slower than the declared max execution time.
247 | */
248 | public function testSlowRequest(): void
249 | {
250 | $this->setNextInvocation([
251 | 'requestContext' => [],
252 | 'httpMethod' => 'GET',
253 | 'path' => '/slow',
254 | 'body' => '',
255 | ]);
256 |
257 | $this->expectOutputString('PHP took longer than 5 seconds to return response');
258 | $invocation = handleNextRequest();
259 |
260 | $response = $this->getInvocationResponse();
261 | $this->assertEquals(500, $response->statusCode);
262 | }
263 |
264 | /**
265 | * Performs request with a `multiValueHeaders` property.
266 | */
267 | public function testMultiValueHeaderRequest(): void
268 | {
269 | $this->setNextInvocation([
270 | 'requestContext' => [],
271 | 'httpMethod' => 'GET',
272 | 'path' => '/multi-value-header',
273 | 'multiValueHeaders' => [
274 | 'Test' => [
275 | 'foo',
276 | 'bar',
277 | ],
278 | ],
279 | 'body' => '',
280 | ]);
281 |
282 | $invocation = handleNextRequest();
283 |
284 | $response = $this->getInvocationResponse();
285 | $this->assertEquals('foo', $response->body);
286 | }
287 |
288 | /**
289 | * Receive a response with multiple values for the same header key.
290 | */
291 | public function testMultiValueHeaderResponse(): void
292 | {
293 | $this->setNextInvocation([
294 | 'requestContext' => [],
295 | 'httpMethod' => 'GET',
296 | 'path' => '/multi-value-header',
297 | 'multiValueHeaders' => [],
298 | 'body' => '',
299 | ]);
300 |
301 | $invocation = handleNextRequest();
302 |
303 | $response = $this->getInvocationResponse();
304 | $this->assertEquals(['foo', 'bar'], $response->multiValueHeaders->Test);
305 | }
306 |
307 | /**
308 | * Performs a request with a standard query string.
309 | */
310 | public function testQueryString(): void
311 | {
312 | $this->setNextInvocation([
313 | 'requestContext' => [],
314 | 'httpMethod' => 'GET',
315 | 'path' => '/echo',
316 | 'multiValueHeaders' => [],
317 | 'queryStringParameters' => [
318 | 'a' => 'foo',
319 | 'b' => 'bar',
320 | ],
321 | 'body' => '',
322 | ]);
323 |
324 | $invocation = handleNextRequest();
325 |
326 | $response = $this->getInvocationResponse();
327 | $this->assertEquals('{"a":"foo","b":"bar"}', $response->body);
328 | }
329 |
330 | /**
331 | * Performs a request with a query string with multiple values against the
332 | * same key.
333 | */
334 | public function testMultiValueQueryString(): void
335 | {
336 | $this->setNextInvocation([
337 | 'requestContext' => [],
338 | 'httpMethod' => 'GET',
339 | 'path' => '/echo',
340 | 'multiValueHeaders' => [],
341 | 'multiValueQueryStringParameters' => [
342 | 'a' => [
343 | 'foo',
344 | 'bar',
345 | ],
346 | ],
347 | 'body' => '',
348 | ]);
349 |
350 | $invocation = handleNextRequest();
351 |
352 | $response = $this->getInvocationResponse();
353 | $this->assertEquals('{"a":"bar"}', $response->body);
354 | }
355 |
356 | /**
357 | * Performs a request with a PHP-specific array style query string.
358 | */
359 | public function testArrayQueryString(): void
360 | {
361 | $this->setNextInvocation([
362 | 'requestContext' => [],
363 | 'httpMethod' => 'GET',
364 | 'path' => '/echo',
365 | 'multiValueHeaders' => [],
366 | 'multiValueQueryStringParameters' => [
367 | 'a' => [
368 | 'foo',
369 | ],
370 | 'b[]' => [
371 | 'foo',
372 | 'bar',
373 | ],
374 | 'c[a]' => [
375 | 'foo',
376 | ],
377 | 'c[b]' => [
378 | 'bar',
379 | ],
380 | ],
381 | 'body' => '',
382 | ]);
383 |
384 | $invocation = handleNextRequest();
385 |
386 | $response = $this->getInvocationResponse();
387 | $this->assertEquals(
388 | '{"a":"foo","b":["foo","bar"],"c":{"a":"foo","b":"bar"}}',
389 | $response->body
390 | );
391 | }
392 |
393 | /**
394 | * Requests an endpoint which returns an oversized response.
395 | */
396 | public function testLargeResponse(): void
397 | {
398 | $this->setNextInvocation([
399 | 'requestContext' => [],
400 | 'httpMethod' => 'GET',
401 | 'path' => '/large-response',
402 | 'body' => '',
403 | ]);
404 |
405 | $this->expectOutputRegex('/^Response size is too large for ALB \([0-9]{7,} bytes\)\n$/');
406 | $invocation = handleNextRequest();
407 |
408 | $response = $this->getInvocationResponse();
409 | $this->assertEquals(500, $response->statusCode);
410 | }
411 |
412 | public function testCurl(): void
413 | {
414 | $url = 'https://www.google.com/';
415 |
416 | $ch = curl_init();
417 | curl_setopt($ch, CURLOPT_URL, $url);
418 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
419 |
420 | curl_exec($ch);
421 |
422 | $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
423 |
424 | curl_close($ch);
425 |
426 | $this->assertEquals(200, $status);
427 | }
428 |
429 | /**
430 | * Stops the mock server to ensure the address can be re-used.
431 | */
432 | public static function tearDownAfterClass(): void
433 | {
434 | self::$server->stop();
435 | }
436 | }
437 |
--------------------------------------------------------------------------------
/tests/_files/public/index.php:
--------------------------------------------------------------------------------
1 | get('/', function ($request, $response) {
12 | return $response->getBody()->write('Home');
13 | });
14 |
15 | $app->post('/', function ($request, $response) {
16 | $post = $request->getParsedBody();
17 | $string = $post['foo'] . $post['bar'];
18 | return $response->getBody()->write($string);
19 | });
20 |
21 | $app->get('/foo/{bar}/baz', function ($request, $response, array $args) {
22 | ['bar' => $bar] = $args;
23 | return $response->getBody()->write("foo/${bar}/baz");
24 | });
25 |
26 | $app->get('/slow', function ($request, $response) {
27 | sleep(10);
28 | });
29 |
30 | $app->get('/multi-value-header', function ($request, $response) {
31 | $body = $response->getBody();
32 | $body->write($request->getHeaderLine('Test'));
33 | return $response
34 | ->withAddedHeader('Test', 'foo')
35 | ->withAddedHeader('Test', 'bar');
36 | });
37 |
38 | $app->get('/echo', function ($request, $response) {
39 | return $response->withJson($request->getQueryParams());
40 | });
41 |
42 | $app->get('/large-response', function ($request, $response) {
43 | $body = $response->getBody();
44 | while ($body->getSize() < 1000000) {
45 | $body->write(' ');
46 | }
47 | return $response;
48 | });
49 |
50 | $app->run();
51 |
--------------------------------------------------------------------------------
/tests/_files/public/subdir/index.php:
--------------------------------------------------------------------------------
1 |