From 5501f2f27fa0b563f2e228ea35afa15a660c072a Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Tue, 14 Dec 2021 20:34:43 +0000 Subject: [PATCH 01/11] Added failing test for a nested HTTP span inside the file_get_contents span --- src/Agent.php | 1 + tests/Integration/AgentTest.php | 81 ++++++++++++++++++++++++++++++++ tests/Integration/TestHelper.php | 24 ++++++++++ 3 files changed, 106 insertions(+) diff --git a/src/Agent.php b/src/Agent.php index c781110c..88162ccc 100644 --- a/src/Agent.php +++ b/src/Agent.php @@ -448,6 +448,7 @@ public function send(): bool $this->registerIfRequired(); $this->sendMetadataIfRequired(); + $this->addSpansFromExtension(); $this->request->stopIfRunning(); $shouldLogContent = $this->config->get(ConfigKey::LOG_PAYLOAD_CONTENT); diff --git a/tests/Integration/AgentTest.php b/tests/Integration/AgentTest.php index f2337217..ac7c620a 100644 --- a/tests/Integration/AgentTest.php +++ b/tests/Integration/AgentTest.php @@ -21,6 +21,9 @@ use Scoutapm\UnitTests\TestLogger; use function assert; +use function curl_exec; +use function curl_init; +use function curl_setopt; use function extension_loaded; use function file_get_contents; use function fopen; @@ -37,6 +40,9 @@ use function str_repeat; use function uniqid; +use const CURLOPT_RETURNTRANSFER; +use const CURLOPT_URL; + /** * @psalm-import-type UnserializedCapturedMessagesList from MessageCapturingConnectorDelegator * @coversNothing @@ -462,4 +468,79 @@ static function (array $commands): bool { null ); } + + /** @noinspection PhpExpressionResultUnusedInspection */ + public function testHttpSpansArePromoted(): void + { + if (! TestHelper::scoutApmExtensionAvailable()) { + self::markTestSkipped('scoutapm extension must be enabled for HTTP spans'); + } + + $this->setUpWithConfiguration(Config::fromArray([ + ConfigKey::APPLICATION_NAME => self::APPLICATION_NAME, + ConfigKey::MONITORING_ENABLED => true, + ])); + + $httpUrl = 'http://scoutapm.com/robots.txt'; + $httpsUrl = 'https://scoutapm.com/robots.txt'; + + file_get_contents($httpUrl); + file_get_contents($httpsUrl); + + $httpCurl = curl_init(); + curl_setopt($httpCurl, CURLOPT_URL, $httpUrl); + curl_setopt($httpCurl, CURLOPT_RETURNTRANSFER, true); + curl_exec($httpCurl); + + $httpsCurl = curl_init(); + curl_setopt($httpsCurl, CURLOPT_URL, $httpsUrl); + curl_setopt($httpsCurl, CURLOPT_RETURNTRANSFER, true); + curl_exec($httpsCurl); + + self::assertTrue($this->agent->send(), 'Failed to send messages. ' . $this->formatCapturedLogMessages()); + + $unserialized = $this->connector->sentMessages; + reset($unserialized); // Skip Register event + next($unserialized); // Skip Metadata event + + $assertSpanContainingHttpSpan = + /** + * @psalm-param list>> $commands + */ + static function (&$commands, string $outerOperation, string $url): void { + $fileGetContentsSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => $outerOperation], next($commands), 'span_id'); + + $httpSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'HTTP', 'parent_id' => $fileGetContentsSpanId], next($commands), 'span_id'); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $httpSpanId, 'tag' => 'uri', 'value' => $url], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $httpSpanId], TestHelper::skipBacktraceTagIfNext($commands), null); + + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => $url]], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $fileGetContentsSpanId], TestHelper::skipBacktraceTagIfNext($commands), null); + }; + + TestHelper::assertUnserializedCommandContainsPayload( + 'BatchCommand', + [ + 'commands' => + /** @psalm-param UnserializedCapturedMessagesList $commands */ + static function (array $commands) use ($assertSpanContainingHttpSpan, $httpUrl, $httpsUrl): bool { + TestHelper::assertUnserializedCommandContainsPayload('StartRequest', [], reset($commands), null); + + $assertSpanContainingHttpSpan($commands, 'file_get_contents', $httpUrl); + $assertSpanContainingHttpSpan($commands, 'file_get_contents', $httpsUrl); + $assertSpanContainingHttpSpan($commands, 'curl_exec', $httpUrl); + $assertSpanContainingHttpSpan($commands, 'curl_exec', $httpsUrl); + + TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'memory_delta'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'path'], next($commands), null); + + TestHelper::assertUnserializedCommandContainsPayload('FinishRequest', [], next($commands), null); + + return true; + }, + ], + next($unserialized), + null + ); + } } diff --git a/tests/Integration/TestHelper.php b/tests/Integration/TestHelper.php index da8f8b70..4a0ee053 100644 --- a/tests/Integration/TestHelper.php +++ b/tests/Integration/TestHelper.php @@ -9,11 +9,14 @@ use PHPUnit\Framework\Assert; use Scoutapm\Helper\Timer; +use function array_key_exists; use function array_keys; use function extension_loaded; use function implode; +use function is_array; use function is_callable; use function is_string; +use function next; use function sprintf; use function var_export; @@ -24,6 +27,27 @@ public static function scoutApmExtensionAvailable(): bool return extension_loaded('scoutapm'); } + /** + * @param list>> $commands + * + * @return array> + */ + public static function skipBacktraceTagIfNext(array &$commands): array + { + $nextCommand = next($commands); + if ( + array_key_exists('TagSpan', $nextCommand) + && is_array($nextCommand['TagSpan']) + && $nextCommand['TagSpan']['tag'] === 'stack' + ) { + // In this case, the request was slow (can happen occasionally), so the stack trace was added. + // We're not interested in stack traces in this test, so just skip it. + $nextCommand = next($commands); + } + + return $nextCommand; + } + /** * @param string[]|callable[]|array $keysAndValuesToExpect * @param mixed[][]|array> $actualCommand From 47e545d8876ad16d8328e93bee321573934f4ca0 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Tue, 14 Dec 2021 22:07:37 +0000 Subject: [PATCH 02/11] For HTTP calls, add an additional HTTP span inside file_get_contents or curl_exec calls --- src/Agent.php | 7 ++++++ src/Events/Tag/Tag.php | 1 + src/Extension/RecordedCall.php | 29 +++++++++++++++++++++-- tests/Unit/AgentTest.php | 43 ++++++++++++++++++++-------------- 4 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/Agent.php b/src/Agent.php index 88162ccc..2867b63c 100644 --- a/src/Agent.php +++ b/src/Agent.php @@ -320,6 +320,13 @@ function (): ?Span { foreach ($this->phpExtension->getCalls() as $recordedCall) { $callSpan = $this->request->startSpan($recordedCall->functionName(), $recordedCall->timeEntered()); + $maybeHttpUrl = $recordedCall->maybeHttpUrl(); + if ($maybeHttpUrl !== null) { + $httpSpan = $this->request->startSpan('HTTP', $recordedCall->timeEntered()); + $httpSpan->tag(Tag::TAG_URI, $maybeHttpUrl); + $this->request->stopSpan($recordedCall->timeExited()); + } + $arguments = $recordedCall->filteredArguments(); if (count($arguments) > 0) { diff --git a/src/Events/Tag/Tag.php b/src/Events/Tag/Tag.php index 9fe190d6..417c1417 100644 --- a/src/Events/Tag/Tag.php +++ b/src/Events/Tag/Tag.php @@ -18,6 +18,7 @@ abstract class Tag implements Command public const TAG_REQUEST_PATH = 'path'; public const TAG_QUEUE_TIME = 'scout.queue_time_ns'; public const TAG_REACHED_SPAN_CAP = 'scout.reached_span_cap'; + public const TAG_URI = 'uri'; /** @var RequestId */ protected $requestId; diff --git a/src/Extension/RecordedCall.php b/src/Extension/RecordedCall.php index 4b396962..c541e852 100644 --- a/src/Extension/RecordedCall.php +++ b/src/Extension/RecordedCall.php @@ -6,6 +6,10 @@ use Webmozart\Assert\Assert; +use function array_key_exists; +use function in_array; +use function stripos; + final class RecordedCall { /** @var string */ @@ -87,11 +91,11 @@ public function timeExited(): float * avoid potentially spilling personally identifiable information. Another reason to only return specific arguments * is to avoid sending loads of data unnecessarily. * - * @return mixed[] + * @return list|array{url: string} */ public function filteredArguments(): array { - if ($this->function === 'file_get_contents') { + if ($this->function === 'file_get_contents' || $this->function === 'curl_exec') { return [ 'url' => (string) $this->arguments[0], ]; @@ -99,4 +103,25 @@ public function filteredArguments(): array return []; } + + public function maybeHttpUrl(): ?string + { + if (! in_array($this->function, ['file_get_contents', 'curl_exec'], true)) { + return null; + } + + $arguments = $this->filteredArguments(); + + if (! array_key_exists('url', $arguments)) { + return null; + } + + $url = $arguments['url']; + + if (stripos($url, 'http://') !== 0 && stripos($url, 'https://') !== 0) { + return null; + } + + return $url; + } } diff --git a/tests/Unit/AgentTest.php b/tests/Unit/AgentTest.php index 96496a69..5553dc2a 100644 --- a/tests/Unit/AgentTest.php +++ b/tests/Unit/AgentTest.php @@ -219,24 +219,31 @@ public function testFullAgentSequence(): void TestHelper::assertUnserializedCommandContainsPayload( 'BatchCommand', [ - 'commands' => static function (array $commands): bool { - TestHelper::assertUnserializedCommandContainsPayload('StartRequest', [], reset($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'file_get_contents'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'args', 'value' => ['url' => 'http://some-url']], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'stack'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'Controller/Test'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'SQL/Query'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'sql.query', 'value' => 'select * from foo'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'uri', 'value' => 'example.com/foo/bar.php'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'memory_delta'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'path'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('FinishRequest', [], next($commands), null); - - return true; - }, + 'commands' => + /** + * @psalm-param list>> $commands + */ + static function (array $commands): bool { + TestHelper::assertUnserializedCommandContainsPayload('StartRequest', [], reset($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'file_get_contents'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'HTTP'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'uri', 'value' => 'http://some-url'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], TestHelper::skipBacktraceTagIfNext($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'args', 'value' => ['url' => 'http://some-url']], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'stack'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'Controller/Test'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'SQL/Query'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'sql.query', 'value' => 'select * from foo'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'uri', 'value' => 'example.com/foo/bar.php'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'memory_delta'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'path'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('FinishRequest', [], next($commands), null); + + return true; + }, ], $request->jsonSerialize(), null From 2d5a49e285f5e09f8a21327338f22deee2034f25 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Tue, 14 Dec 2021 22:37:26 +0000 Subject: [PATCH 03/11] Wrap integration test in a fake controller --- tests/Integration/AgentTest.php | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/tests/Integration/AgentTest.php b/tests/Integration/AgentTest.php index ac7c620a..f21eb405 100644 --- a/tests/Integration/AgentTest.php +++ b/tests/Integration/AgentTest.php @@ -484,18 +484,23 @@ public function testHttpSpansArePromoted(): void $httpUrl = 'http://scoutapm.com/robots.txt'; $httpsUrl = 'https://scoutapm.com/robots.txt'; - file_get_contents($httpUrl); - file_get_contents($httpsUrl); - - $httpCurl = curl_init(); - curl_setopt($httpCurl, CURLOPT_URL, $httpUrl); - curl_setopt($httpCurl, CURLOPT_RETURNTRANSFER, true); - curl_exec($httpCurl); - - $httpsCurl = curl_init(); - curl_setopt($httpsCurl, CURLOPT_URL, $httpsUrl); - curl_setopt($httpsCurl, CURLOPT_RETURNTRANSFER, true); - curl_exec($httpsCurl); + $this->agent->webTransaction( + 'TestingHttpSpans', + static function () use ($httpUrl, $httpsUrl): void { + file_get_contents($httpUrl); + file_get_contents($httpsUrl); + + $httpCurl = curl_init(); + curl_setopt($httpCurl, CURLOPT_URL, $httpUrl); + curl_setopt($httpCurl, CURLOPT_RETURNTRANSFER, true); + curl_exec($httpCurl); + + $httpsCurl = curl_init(); + curl_setopt($httpsCurl, CURLOPT_URL, $httpsUrl); + curl_setopt($httpsCurl, CURLOPT_RETURNTRANSFER, true); + curl_exec($httpsCurl); + } + ); self::assertTrue($this->agent->send(), 'Failed to send messages. ' . $this->formatCapturedLogMessages()); @@ -526,11 +531,15 @@ static function (&$commands, string $outerOperation, string $url): void { static function (array $commands) use ($assertSpanContainingHttpSpan, $httpUrl, $httpsUrl): bool { TestHelper::assertUnserializedCommandContainsPayload('StartRequest', [], reset($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'Controller/TestingHttpSpans'], next($commands), null); + $assertSpanContainingHttpSpan($commands, 'file_get_contents', $httpUrl); $assertSpanContainingHttpSpan($commands, 'file_get_contents', $httpsUrl); $assertSpanContainingHttpSpan($commands, 'curl_exec', $httpUrl); $assertSpanContainingHttpSpan($commands, 'curl_exec', $httpsUrl); + TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'memory_delta'], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('TagRequest', ['tag' => 'path'], next($commands), null); From 362db607ae7318454a139c32255b6fcd9b926fe8 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Wed, 15 Dec 2021 08:20:51 +0000 Subject: [PATCH 04/11] Ensure curl is installed for http test --- .github/workflows/tests.yml | 14 +++++++++++--- tests/Integration/AgentTest.php | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 84d84be8..65cf4d2a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,9 +16,8 @@ jobs: fail-fast: false matrix: dependencies: ["lowest", "highest"] + scout-ext: ["with-scout-ext", "no-scout-ext"] extensions: [ - "scoutapm", - "scoutapm, mongodb", "", "mongodb" ] @@ -43,7 +42,16 @@ jobs: coverage: "none" php-version: "${{ matrix.php-version }}" tools: pecl - extensions: ${{ matrix.extensions }} + extensions: "curl, ${{ matrix.extensions }}" + # Normally, we'd just add "scoutapm" to the above extensions in shivammathur/setup-php, but libcurl appears to + # be missing wherever the extension is built (not immediately obvious), so install it first + - name: "Install scoutapm extension" + if: ${{ matrix.scout-ext == 'with-scout-ext' }} + run: | + sudo apt-get install -y libcurl4-openssl-dev + sudo mkdir -p /tmp/pear/temp + sudo pecl update-channels + yes | sudo pecl install -f scoutapm - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} run: "composer update --prefer-lowest --prefer-dist --no-interaction --no-progress" diff --git a/tests/Integration/AgentTest.php b/tests/Integration/AgentTest.php index f21eb405..03119431 100644 --- a/tests/Integration/AgentTest.php +++ b/tests/Integration/AgentTest.php @@ -476,6 +476,10 @@ public function testHttpSpansArePromoted(): void self::markTestSkipped('scoutapm extension must be enabled for HTTP spans'); } + if (! extension_loaded('curl')) { + self::markTestSkipped('curl extension must be enabled for HTTP spans'); + } + $this->setUpWithConfiguration(Config::fromArray([ ConfigKey::APPLICATION_NAME => self::APPLICATION_NAME, ConfigKey::MONITORING_ENABLED => true, From b7463f3ca11989f4fd1c8210b5f10137d3a3e2e1 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Thu, 23 Dec 2021 12:07:37 +0000 Subject: [PATCH 05/11] Warn when extension version is older than 1.5.0 --- src/Agent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Agent.php b/src/Agent.php index 2867b63c..70440e17 100644 --- a/src/Agent.php +++ b/src/Agent.php @@ -51,7 +51,7 @@ final class Agent implements ScoutApmAgent private const METADATA_CACHE_TTL_SECONDS = 600; - private const WARN_WHEN_EXTENSION_IS_OLDER_THAN = '1.4.0'; + private const WARN_WHEN_EXTENSION_IS_OLDER_THAN = '1.5.0'; /** @var Config */ private $config; From 85ca56b8602828b623057d6e943d5ec4e28af4ee Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Thu, 23 Dec 2021 13:31:36 +0000 Subject: [PATCH 06/11] Added HTTP verb to HTTP spans, if available --- src/Agent.php | 3 +- src/Extension/RecordedCall.php | 60 ++++++++++++++++++++++- tests/Integration/AgentTest.php | 39 ++++++++++----- tests/Unit/AgentTest.php | 6 +-- tests/Unit/Extension/RecordedCallTest.php | 2 +- 5 files changed, 92 insertions(+), 18 deletions(-) diff --git a/src/Agent.php b/src/Agent.php index 70440e17..7e1eca96 100644 --- a/src/Agent.php +++ b/src/Agent.php @@ -322,7 +322,8 @@ function (): ?Span { $maybeHttpUrl = $recordedCall->maybeHttpUrl(); if ($maybeHttpUrl !== null) { - $httpSpan = $this->request->startSpan('HTTP', $recordedCall->timeEntered()); + $httpMethod = $recordedCall->maybeHttpMethod() ?: 'GET'; + $httpSpan = $this->request->startSpan('HTTP/' . $httpMethod, $recordedCall->timeEntered()); $httpSpan->tag(Tag::TAG_URI, $maybeHttpUrl); $this->request->stopSpan($recordedCall->timeExited()); } diff --git a/src/Extension/RecordedCall.php b/src/Extension/RecordedCall.php index c541e852..f669078b 100644 --- a/src/Extension/RecordedCall.php +++ b/src/Extension/RecordedCall.php @@ -8,7 +8,12 @@ use function array_key_exists; use function in_array; +use function is_array; +use function is_string; +use function json_decode; +use function preg_replace; use function stripos; +use function strtoupper; final class RecordedCall { @@ -91,13 +96,51 @@ public function timeExited(): float * avoid potentially spilling personally identifiable information. Another reason to only return specific arguments * is to avoid sending loads of data unnecessarily. * - * @return list|array{url: string} + * @return list|array{url: string, method: string} */ public function filteredArguments(): array { if ($this->function === 'file_get_contents' || $this->function === 'curl_exec') { + $method = 'GET'; + + // file_get_contents was used with a stream context + if ( + $this->function === 'file_get_contents' + && array_key_exists(2, $this->arguments) + && is_string($this->arguments[2]) + ) { + /** @var mixed $fileGetContentsStreamContext */ + $fileGetContentsStreamContext = json_decode($this->arguments[2], true); + if ( + is_array($fileGetContentsStreamContext) + && array_key_exists('http', $fileGetContentsStreamContext) + && is_array($fileGetContentsStreamContext['http']) + && array_key_exists('method', $fileGetContentsStreamContext['http']) + && is_string($fileGetContentsStreamContext['http']['method']) + && ! empty($fileGetContentsStreamContext['http']['method']) + ) { + $method = $fileGetContentsStreamContext['http']['method']; + } + } + + // curl_exec with CURLOPT_POST option was used with a truthy value + if ($this->function === 'curl_exec' && array_key_exists(1, $this->arguments) && $this->arguments[1]) { + $method = 'POST'; + } + + // curl_exec with CURLOPT_POST option was used with a truthy value + if ( + $this->function === 'curl_exec' + && array_key_exists(2, $this->arguments) + && is_string($this->arguments[2]) + && ! empty($this->arguments[2]) + ) { + $method = $this->arguments[2]; + } + return [ 'url' => (string) $this->arguments[0], + 'method' => preg_replace('/[^A-Z]/', '', strtoupper($method)), ]; } @@ -124,4 +167,19 @@ public function maybeHttpUrl(): ?string return $url; } + + public function maybeHttpMethod(): ?string + { + if (! in_array($this->function, ['file_get_contents', 'curl_exec'], true)) { + return null; + } + + $arguments = $this->filteredArguments(); + + if (! array_key_exists('method', $arguments)) { + return null; + } + + return $arguments['method']; + } } diff --git a/tests/Integration/AgentTest.php b/tests/Integration/AgentTest.php index 03119431..887e08bb 100644 --- a/tests/Integration/AgentTest.php +++ b/tests/Integration/AgentTest.php @@ -38,8 +38,11 @@ use function sleep; use function sprintf; use function str_repeat; +use function stream_context_create; use function uniqid; +use const CURLOPT_CUSTOMREQUEST; +use const CURLOPT_POST; use const CURLOPT_RETURNTRANSFER; use const CURLOPT_URL; @@ -492,17 +495,28 @@ public function testHttpSpansArePromoted(): void 'TestingHttpSpans', static function () use ($httpUrl, $httpsUrl): void { file_get_contents($httpUrl); - file_get_contents($httpsUrl); + + // 405 Method Not Allowed is expected, and emitted as a warning, so ignore for this test + @file_get_contents($httpsUrl, false, stream_context_create([ + 'http' => ['method' => 'POST'], + ])); $httpCurl = curl_init(); curl_setopt($httpCurl, CURLOPT_URL, $httpUrl); curl_setopt($httpCurl, CURLOPT_RETURNTRANSFER, true); curl_exec($httpCurl); - $httpsCurl = curl_init(); - curl_setopt($httpsCurl, CURLOPT_URL, $httpsUrl); - curl_setopt($httpsCurl, CURLOPT_RETURNTRANSFER, true); - curl_exec($httpsCurl); + $httpsPostCurl = curl_init(); + curl_setopt($httpsPostCurl, CURLOPT_URL, $httpsUrl); + curl_setopt($httpsPostCurl, CURLOPT_POST, 1); + curl_setopt($httpsPostCurl, CURLOPT_RETURNTRANSFER, true); + curl_exec($httpsPostCurl); + + $httpsPutCurl = curl_init(); + curl_setopt($httpsPutCurl, CURLOPT_URL, $httpsUrl); + curl_setopt($httpsPutCurl, CURLOPT_CUSTOMREQUEST, 'PUT'); + curl_setopt($httpsPutCurl, CURLOPT_RETURNTRANSFER, true); + curl_exec($httpsPutCurl); } ); @@ -516,14 +530,14 @@ static function () use ($httpUrl, $httpsUrl): void { /** * @psalm-param list>> $commands */ - static function (&$commands, string $outerOperation, string $url): void { + static function (&$commands, string $outerOperation, string $method, string $url): void { $fileGetContentsSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => $outerOperation], next($commands), 'span_id'); - $httpSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'HTTP', 'parent_id' => $fileGetContentsSpanId], next($commands), 'span_id'); + $httpSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'HTTP/' . $method, 'parent_id' => $fileGetContentsSpanId], next($commands), 'span_id'); TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $httpSpanId, 'tag' => 'uri', 'value' => $url], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $httpSpanId], TestHelper::skipBacktraceTagIfNext($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => $url]], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => $url, 'method' => $method]], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $fileGetContentsSpanId], TestHelper::skipBacktraceTagIfNext($commands), null); }; @@ -537,10 +551,11 @@ static function (array $commands) use ($assertSpanContainingHttpSpan, $httpUrl, TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'Controller/TestingHttpSpans'], next($commands), null); - $assertSpanContainingHttpSpan($commands, 'file_get_contents', $httpUrl); - $assertSpanContainingHttpSpan($commands, 'file_get_contents', $httpsUrl); - $assertSpanContainingHttpSpan($commands, 'curl_exec', $httpUrl); - $assertSpanContainingHttpSpan($commands, 'curl_exec', $httpsUrl); + $assertSpanContainingHttpSpan($commands, 'file_get_contents', 'GET', $httpUrl); + $assertSpanContainingHttpSpan($commands, 'file_get_contents', 'POST', $httpsUrl); + $assertSpanContainingHttpSpan($commands, 'curl_exec', 'GET', $httpUrl); + $assertSpanContainingHttpSpan($commands, 'curl_exec', 'POST', $httpsUrl); + $assertSpanContainingHttpSpan($commands, 'curl_exec', 'PUT', $httpsUrl); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); diff --git a/tests/Unit/AgentTest.php b/tests/Unit/AgentTest.php index 5553dc2a..1a3e2f7a 100644 --- a/tests/Unit/AgentTest.php +++ b/tests/Unit/AgentTest.php @@ -199,7 +199,7 @@ public function testFullAgentSequence(): void 'entered' => $microtime - 1, 'exited' => $microtime, 'time_taken' => 1, - 'argv' => ['http://some-url'], + 'argv' => ['http://some-url', 'method' => 'GET'], ]), ]); @@ -226,10 +226,10 @@ public function testFullAgentSequence(): void static function (array $commands): bool { TestHelper::assertUnserializedCommandContainsPayload('StartRequest', [], reset($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'file_get_contents'], next($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'HTTP'], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'HTTP/GET'], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'uri', 'value' => 'http://some-url'], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], TestHelper::skipBacktraceTagIfNext($commands), null); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'args', 'value' => ['url' => 'http://some-url']], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'args', 'value' => ['url' => 'http://some-url', 'method' => 'GET']], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['tag' => 'stack'], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', [], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'Controller/Test'], next($commands), null); diff --git a/tests/Unit/Extension/RecordedCallTest.php b/tests/Unit/Extension/RecordedCallTest.php index 6abfd2a2..ceba1d43 100644 --- a/tests/Unit/Extension/RecordedCallTest.php +++ b/tests/Unit/Extension/RecordedCallTest.php @@ -48,7 +48,7 @@ public function filteredArgumentsDataProvider(): array return [ 'file_get_contents' => [ 'recordedFunctionName' => 'file_get_contents', - 'expectedFilteredArguments' => ['url' => 'a'], + 'expectedFilteredArguments' => ['url' => 'a', 'method' => 'GET'], ], 'password_hash' => [ 'recordedFunctionName' => 'password_hashj', From d5ae2b2b176c29fbbd858747c87e17d54e751169 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Mon, 27 Dec 2021 11:08:48 +0000 Subject: [PATCH 07/11] Fix to Composer 2.1 as 2.2 broke phpunit --- .github/workflows/laravel.yml | 4 ++-- .github/workflows/lumen.yml | 4 ++-- .github/workflows/quality.yml | 3 +++ .github/workflows/symfony.yml | 2 +- .github/workflows/tests.yml | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/laravel.yml b/.github/workflows/laravel.yml index 232b85e2..07d605fa 100644 --- a/.github/workflows/laravel.yml +++ b/.github/workflows/laravel.yml @@ -38,7 +38,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl + tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} # --no-update then a full `composer update` is needed to overcome locked dependencies # See: https://github.com/composer/composer/issues/9561 @@ -85,7 +85,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl + tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} - name: "Install Laravel quickstart project" run: "composer create-project laravel/laravel:${{ matrix.laravel-version}} test-app --prefer-dist" diff --git a/.github/workflows/lumen.yml b/.github/workflows/lumen.yml index 5aa9dbf1..0bf87507 100644 --- a/.github/workflows/lumen.yml +++ b/.github/workflows/lumen.yml @@ -42,7 +42,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl + tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} # --no-update then a full `composer update` is needed to overcome locked dependencies # See: https://github.com/composer/composer/issues/9561 @@ -91,7 +91,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl + tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} - name: "Install Lumen quickstart project" run: "composer create-project laravel/lumen:${{ matrix.lumen-version}} test-app --prefer-dist" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index a818f5f6..4cad7585 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -19,6 +19,7 @@ jobs: with: coverage: "none" php-version: "8.0" + tools: composer:v2.1 - name: "Install dependencies" run: "composer install" - name: "Run PHP_CodeSniffer" @@ -34,6 +35,7 @@ jobs: with: coverage: "none" php-version: "8.0" + tools: composer:v2.1 - name: "Install dependencies" run: "composer install" - name: "Run Psalm" @@ -51,6 +53,7 @@ jobs: with: coverage: "none" php-version: "8.0" + tools: composer:v2.1 - name: "Require Roave/BackwardCompatibilityCheck" run: "composer require --no-update --no-interaction --prefer-dist --prefer-stable --dev roave/backward-compatibility-check:^6.0.1" - name: "Composer update with new requirements" diff --git a/.github/workflows/symfony.yml b/.github/workflows/symfony.yml index 599ebad8..e3f76fcd 100644 --- a/.github/workflows/symfony.yml +++ b/.github/workflows/symfony.yml @@ -38,7 +38,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl + tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} # --no-update then a full `composer update` is needed to overcome locked dependencies # See: https://github.com/composer/composer/issues/9561 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 65cf4d2a..1713263b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,7 +41,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl + tools: pecl, composer:v2.1 extensions: "curl, ${{ matrix.extensions }}" # Normally, we'd just add "scoutapm" to the above extensions in shivammathur/setup-php, but libcurl appears to # be missing wherever the extension is built (not immediately obvious), so install it first From 60aa6ee4d08bcc585177bbfa8b88a91ea5a4cf1e Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Tue, 28 Dec 2021 10:53:26 +0000 Subject: [PATCH 08/11] Use fail-fast:true for shivammathur/setup-php See: https://github.com/shivammathur/setup-php/issues/548#issuecomment-1001646667 --- .github/workflows/laravel.yml | 4 ++++ .github/workflows/lumen.yml | 4 ++++ .github/workflows/quality.yml | 6 ++++++ .github/workflows/symfony.yml | 2 ++ .github/workflows/tests.yml | 2 ++ 5 files changed, 18 insertions(+) diff --git a/.github/workflows/laravel.yml b/.github/workflows/laravel.yml index 07d605fa..49455dfb 100644 --- a/.github/workflows/laravel.yml +++ b/.github/workflows/laravel.yml @@ -40,6 +40,8 @@ jobs: php-version: "${{ matrix.php-version }}" tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} + env: + fail-fast: true # --no-update then a full `composer update` is needed to overcome locked dependencies # See: https://github.com/composer/composer/issues/9561 - name: "Remove existing requirements components (avoid conflicts)" @@ -87,6 +89,8 @@ jobs: php-version: "${{ matrix.php-version }}" tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} + env: + fail-fast: true - name: "Install Laravel quickstart project" run: "composer create-project laravel/laravel:${{ matrix.laravel-version}} test-app --prefer-dist" - name: "Add scout-apm-php as a repository" diff --git a/.github/workflows/lumen.yml b/.github/workflows/lumen.yml index 0bf87507..e0230d81 100644 --- a/.github/workflows/lumen.yml +++ b/.github/workflows/lumen.yml @@ -44,6 +44,8 @@ jobs: php-version: "${{ matrix.php-version }}" tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} + env: + fail-fast: true # --no-update then a full `composer update` is needed to overcome locked dependencies # See: https://github.com/composer/composer/issues/9561 - name: "Remove existing requirements components (avoid conflicts)" @@ -93,6 +95,8 @@ jobs: php-version: "${{ matrix.php-version }}" tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} + env: + fail-fast: true - name: "Install Lumen quickstart project" run: "composer create-project laravel/lumen:${{ matrix.lumen-version}} test-app --prefer-dist" - name: "Add scout-apm-php as a repository" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 4cad7585..ef88229a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -20,6 +20,8 @@ jobs: coverage: "none" php-version: "8.0" tools: composer:v2.1 + env: + fail-fast: true - name: "Install dependencies" run: "composer install" - name: "Run PHP_CodeSniffer" @@ -36,6 +38,8 @@ jobs: coverage: "none" php-version: "8.0" tools: composer:v2.1 + env: + fail-fast: true - name: "Install dependencies" run: "composer install" - name: "Run Psalm" @@ -54,6 +58,8 @@ jobs: coverage: "none" php-version: "8.0" tools: composer:v2.1 + env: + fail-fast: true - name: "Require Roave/BackwardCompatibilityCheck" run: "composer require --no-update --no-interaction --prefer-dist --prefer-stable --dev roave/backward-compatibility-check:^6.0.1" - name: "Composer update with new requirements" diff --git a/.github/workflows/symfony.yml b/.github/workflows/symfony.yml index e3f76fcd..6d31a5b6 100644 --- a/.github/workflows/symfony.yml +++ b/.github/workflows/symfony.yml @@ -40,6 +40,8 @@ jobs: php-version: "${{ matrix.php-version }}" tools: pecl, composer:v2.1 extensions: ${{ matrix.extensions }} + env: + fail-fast: true # --no-update then a full `composer update` is needed to overcome locked dependencies # See: https://github.com/composer/composer/issues/9561 - name: "Remove existing requirements components (avoid conflicts)" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1713263b..fbfd2808 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -43,6 +43,8 @@ jobs: php-version: "${{ matrix.php-version }}" tools: pecl, composer:v2.1 extensions: "curl, ${{ matrix.extensions }}" + env: + fail-fast: true # Normally, we'd just add "scoutapm" to the above extensions in shivammathur/setup-php, but libcurl appears to # be missing wherever the extension is built (not immediately obvious), so install it first - name: "Install scoutapm extension" From d8d97161c652183903c79eb4cb709b3376dc4435 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Tue, 28 Dec 2021 11:07:38 +0000 Subject: [PATCH 09/11] Fixed missing HTTP method added to file_get_contents in integration tests --- tests/Integration/AgentTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Integration/AgentTest.php b/tests/Integration/AgentTest.php index 887e08bb..3fea3319 100644 --- a/tests/Integration/AgentTest.php +++ b/tests/Integration/AgentTest.php @@ -287,7 +287,7 @@ static function (array $commands): bool { if (TestHelper::scoutApmExtensionAvailable()) { $fileGetContentsSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'file_get_contents', 'parent_id' => $controllerSpanId], next($commands), 'span_id'); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__]], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__, 'method' => 'GET']], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $fileGetContentsSpanId], next($commands), null); } @@ -295,7 +295,7 @@ static function (array $commands): bool { if (TestHelper::scoutApmExtensionAvailable()) { $fileGetContentsSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'file_get_contents', 'parent_id' => $fooSpanId], next($commands), 'span_id'); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__]], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__, 'method' => 'GET']], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $fileGetContentsSpanId], next($commands), null); } @@ -303,7 +303,7 @@ static function (array $commands): bool { if (TestHelper::scoutApmExtensionAvailable()) { $fileGetContentsSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'file_get_contents', 'parent_id' => $barSpanId], next($commands), 'span_id'); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__]], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__, 'method' => 'GET']], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $fileGetContentsSpanId], next($commands), null); } @@ -315,7 +315,7 @@ static function (array $commands): bool { if (TestHelper::scoutApmExtensionAvailable()) { $fileGetContentsSpanId = TestHelper::assertUnserializedCommandContainsPayload('StartSpan', ['operation' => 'file_get_contents', 'parent_id' => $controllerSpanId], next($commands), 'span_id'); - TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__]], next($commands), null); + TestHelper::assertUnserializedCommandContainsPayload('TagSpan', ['span_id' => $fileGetContentsSpanId, 'tag' => 'args', 'value' => ['url' => __FILE__, 'method' => 'GET']], next($commands), null); TestHelper::assertUnserializedCommandContainsPayload('StopSpan', ['span_id' => $fileGetContentsSpanId], next($commands), null); } From 7f4ec5fda47badcbe636fc9024d95f8b59ce78ff Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Mon, 10 Jan 2022 08:54:23 +0000 Subject: [PATCH 10/11] Updated Changelog for 6.7.0 release --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25d8e54d..9fb03a92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 6.7.0 - 2022-01-10 + +### Added + +- [#249](https://github.com/scoutapp/scout-apm-php/pull/249) Added HTTP spans for file_get_contents and curl_exec + +### Changed + +- Nothing. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 6.6.1 - 2022-01-03 ### Added From 609b6bca3cbf250fe59c547ce50789103fce966f Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Mon, 10 Jan 2022 08:56:40 +0000 Subject: [PATCH 11/11] Update to use Composer 2.2 now phpunit should be fixed --- .github/workflows/laravel.yml | 4 ++-- .github/workflows/lumen.yml | 4 ++-- .github/workflows/quality.yml | 6 +++--- .github/workflows/symfony.yml | 2 +- .github/workflows/tests.yml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/laravel.yml b/.github/workflows/laravel.yml index 49455dfb..e3676182 100644 --- a/.github/workflows/laravel.yml +++ b/.github/workflows/laravel.yml @@ -38,7 +38,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl, composer:v2.1 + tools: pecl, composer:v2.2 extensions: ${{ matrix.extensions }} env: fail-fast: true @@ -87,7 +87,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl, composer:v2.1 + tools: pecl, composer:v2.2 extensions: ${{ matrix.extensions }} env: fail-fast: true diff --git a/.github/workflows/lumen.yml b/.github/workflows/lumen.yml index e0230d81..11b0b995 100644 --- a/.github/workflows/lumen.yml +++ b/.github/workflows/lumen.yml @@ -42,7 +42,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl, composer:v2.1 + tools: pecl, composer:v2.2 extensions: ${{ matrix.extensions }} env: fail-fast: true @@ -93,7 +93,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl, composer:v2.1 + tools: pecl, composer:v2.2 extensions: ${{ matrix.extensions }} env: fail-fast: true diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index ef88229a..6a65d3a7 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -19,7 +19,7 @@ jobs: with: coverage: "none" php-version: "8.0" - tools: composer:v2.1 + tools: composer:v2.2 env: fail-fast: true - name: "Install dependencies" @@ -37,7 +37,7 @@ jobs: with: coverage: "none" php-version: "8.0" - tools: composer:v2.1 + tools: composer:v2.2 env: fail-fast: true - name: "Install dependencies" @@ -57,7 +57,7 @@ jobs: with: coverage: "none" php-version: "8.0" - tools: composer:v2.1 + tools: composer:v2.2 env: fail-fast: true - name: "Require Roave/BackwardCompatibilityCheck" diff --git a/.github/workflows/symfony.yml b/.github/workflows/symfony.yml index 6d31a5b6..21f0b3aa 100644 --- a/.github/workflows/symfony.yml +++ b/.github/workflows/symfony.yml @@ -38,7 +38,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl, composer:v2.1 + tools: pecl, composer:v2.2 extensions: ${{ matrix.extensions }} env: fail-fast: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fbfd2808..36daa9f0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,7 +41,7 @@ jobs: with: coverage: "none" php-version: "${{ matrix.php-version }}" - tools: pecl, composer:v2.1 + tools: pecl, composer:v2.2 extensions: "curl, ${{ matrix.extensions }}" env: fail-fast: true