diff --git a/lib/Client.php b/lib/Client.php index ec56ff5..4dc1599 100644 --- a/lib/Client.php +++ b/lib/Client.php @@ -149,27 +149,30 @@ public function capture(array $message) $message["properties"]['$groups'] = $message['$groups']; } - $extraProperties = []; - $flags = []; if (array_key_exists("send_feature_flags", $message) && $message["send_feature_flags"]) { - $flags = $this->fetchFeatureVariants($message["distinct_id"], $message["groups"]); - } elseif (count($this->featureFlags) != 0) { - # Local evaluation is enabled, flags are loaded, so try and get all flags we can without going to the server - $flags = $this->getAllFlags($message["distinct_id"], $message["groups"], [], [], true); - } + $extraProperties = []; + $flags = []; + + if (count($this->featureFlags) != 0) { + # Local evaluation is enabled, flags are loaded, so try and get all flags we can without going to the server + $flags = $this->getAllFlags($message["distinct_id"], $message["groups"], [], [], true); + } else { + $flags = $this->fetchFeatureVariants($message["distinct_id"], $message["groups"]); + } - // Add all feature variants to event - foreach ($flags as $flagKey => $flagValue) { - $extraProperties[sprintf('$feature/%s', $flagKey)] = $flagValue; - } + // Add all feature variants to event + foreach ($flags as $flagKey => $flagValue) { + $extraProperties[sprintf('$feature/%s', $flagKey)] = $flagValue; + } + // Add all feature flag keys that aren't false to $active_feature_flags + // decide v2 does this automatically, but we need it for when we upgrade to v3 + $extraProperties['$active_feature_flags'] = array_keys(array_filter($flags, function ($flagValue) { + return $flagValue !== false; + })); - // Add all feature flag keys that aren't false to $active_feature_flags - // decide v2 does this automatically, but we need it for when we upgrade to v3 - $extraProperties['$active_feature_flags'] = array_keys(array_filter($flags, function ($flagValue) { - return $flagValue !== false; - })); + $message["properties"] = array_merge($extraProperties, $message["properties"]); + } - $message["properties"] = array_merge($extraProperties, $message["properties"]); return $this->consumer->capture($message); } diff --git a/test/FeatureFlagLocalEvaluationTest.php b/test/FeatureFlagLocalEvaluationTest.php index fb5c294..b0cc66c 100644 --- a/test/FeatureFlagLocalEvaluationTest.php +++ b/test/FeatureFlagLocalEvaluationTest.php @@ -1367,7 +1367,7 @@ public function testSimpleFlag() ), 1 => array( "path" => "/batch/", - 'payload' => '{"batch":[{"properties":{"$feature\/simple-flag":true,"$active_feature_flags":["simple-flag"],"$feature_flag":"simple-flag","$feature_flag_response":true,"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl","$groups":[]},"distinct_id":"some-distinct-id","event":"$feature_flag_called","$groups":[],"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', + 'payload' => '{"batch":[{"properties":{"$feature_flag":"simple-flag","$feature_flag_response":true,"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl","$groups":[]},"distinct_id":"some-distinct-id","event":"$feature_flag_called","$groups":[],"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), "requestOptions" => array('shouldVerify' => true), ), diff --git a/test/FeatureFlagTest.php b/test/FeatureFlagTest.php index 1d355dc..cff58aa 100644 --- a/test/FeatureFlagTest.php +++ b/test/FeatureFlagTest.php @@ -96,7 +96,7 @@ public function testIsFeatureEnabledCapturesFeatureFlagCalledEventWithAdditional ), 1 => array( "path" => "/batch/", - "payload" => '{"batch":[{"properties":{"$active_feature_flags":[],"$feature_flag":"simple-test","$feature_flag_response":true,"$feature_flag_request_id":"98487c8a-287a-4451-a085-299cd76228dd","$feature_flag_id":6,"$feature_flag_version":1,"$feature_flag_reason":"Matched condition set 1","$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl","$groups":[]},"distinct_id":"user-id","event":"$feature_flag_called","$groups":[],"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', + "payload" => '{"batch":[{"properties":{"$feature_flag":"simple-test","$feature_flag_response":true,"$feature_flag_request_id":"98487c8a-287a-4451-a085-299cd76228dd","$feature_flag_id":6,"$feature_flag_version":1,"$feature_flag_reason":"Matched condition set 1","$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl","$groups":[]},"distinct_id":"user-id","event":"$feature_flag_called","$groups":[],"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), "requestOptions" => array('shouldVerify' => true), ), @@ -178,7 +178,7 @@ public function testGetFeatureFlagCapturesFeatureFlagCalledEventWithAdditionalMe ), 1 => array( "path" => "/batch/", - "payload" => '{"batch":[{"properties":{"$active_feature_flags":[],"$feature_flag":"multivariate-test","$feature_flag_response":"variant-value","$feature_flag_request_id":"98487c8a-287a-4451-a085-299cd76228dd","$feature_flag_id":7,"$feature_flag_version":3,"$feature_flag_reason":"Matched condition set 2","$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl","$groups":[]},"distinct_id":"user-id","event":"$feature_flag_called","$groups":[],"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', + "payload" => '{"batch":[{"properties":{"$feature_flag":"multivariate-test","$feature_flag_response":"variant-value","$feature_flag_request_id":"98487c8a-287a-4451-a085-299cd76228dd","$feature_flag_id":7,"$feature_flag_version":3,"$feature_flag_reason":"Matched condition set 2","$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl","$groups":[]},"distinct_id":"user-id","event":"$feature_flag_called","$groups":[],"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), "requestOptions" => array('shouldVerify' => true), ), diff --git a/test/PostHogTest.php b/test/PostHogTest.php index 3669b29..af41d7d 100644 --- a/test/PostHogTest.php +++ b/test/PostHogTest.php @@ -117,28 +117,22 @@ public function testCaptureWithSendFeatureFlagsOption(): void "requestOptions" => array("includeEtag" => true), ), 1 => array ( - "path" => "/flags/?v=2", - "payload" => sprintf('{"api_key":"%s","distinct_id":"john"}', self::FAKE_API_KEY), - "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), - "requestOptions" => array("timeout" => 1234, "shouldRetry" => false), - ), - 2 => array ( "path" => "/batch/", - "payload" => '{"batch":[{"event":"Module PHP Event","send_feature_flags":true,"properties":{"$feature\/simpleFlag":true,"$feature\/having_fun":false,"$feature\/enabled-flag":true,"$feature\/disabled-flag":false,"$feature\/multivariate-simple-test":"variant-simple-value","$feature\/simple-test":true,"$feature\/multivariate-test":"variant-value","$feature\/group-flag":"decide-fallback-value","$feature\/complex-flag":"decide-fallback-value","$feature\/beta-feature":"decide-fallback-value","$feature\/beta-feature2":"alakazam","$feature\/feature-1":"decide-fallback-value","$feature\/feature-2":"decide-fallback-value","$feature\/variant-1":"variant-1","$feature\/variant-3":"variant-3","$active_feature_flags":["simpleFlag","enabled-flag","multivariate-simple-test","simple-test","multivariate-test","group-flag","complex-flag","beta-feature","beta-feature2","feature-1","feature-2","variant-1","variant-3"],"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl"},"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","distinct_id":"john","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', + "payload" => '{"batch":[{"event":"Module PHP Event","send_feature_flags":true,"properties":{"$feature\/true-flag":true,"$active_feature_flags":["true-flag"],"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl"},"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","distinct_id":"john","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), "requestOptions" => array('shouldVerify' => true), ), ) ); - // check true-flag is not in captured event + // Verify only locally evaluated feature flags are included $this->assertEquals( - strpos($this->http_client->calls[2]["payload"], 'simpleFlag'), - true + strpos($this->http_client->calls[1]["payload"], 'simpleFlag'), + false ); $this->assertEquals( - strpos($this->http_client->calls[2]["payload"], 'true-flag'), - false + strpos($this->http_client->calls[1]["payload"], 'true-flag'), + true ); }); } @@ -162,7 +156,8 @@ public function testCaptureWithLocalSendFlags(): void array ( "distinctId" => "john", "event" => "Module PHP Event", - ) + "send_feature_flags" => true + ), ) ); @@ -179,7 +174,7 @@ public function testCaptureWithLocalSendFlags(): void ), 1 => array ( "path" => "/batch/", - "payload" => '{"batch":[{"event":"Module PHP Event","properties":{"$feature\/true-flag":true,"$active_feature_flags":["true-flag"],"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl"},"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","distinct_id":"john","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', + "payload" => '{"batch":[{"event":"Module PHP Event","send_feature_flags":true,"properties":{"$feature\/true-flag":true,"$active_feature_flags":["true-flag"],"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl"},"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","distinct_id":"john","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), "requestOptions" => array('shouldVerify' => true), ), @@ -209,7 +204,8 @@ public function testCaptureWithLocalSendFlagsNoOverrides(): void "event" => "Module PHP Event", "properties" => array ( "\$feature/true-flag" => "random-override" - ) + ), + "send_feature_flags" => true ) ) ); @@ -228,7 +224,7 @@ public function testCaptureWithLocalSendFlagsNoOverrides(): void ), 1 => array ( "path" => "/batch/", - "payload" => '{"batch":[{"event":"Module PHP Event","properties":{"$feature\/true-flag":"random-override","$active_feature_flags":["true-flag"],"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl"},"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","distinct_id":"john","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', + "payload" => '{"batch":[{"event":"Module PHP Event","properties":{"$feature\/true-flag":"random-override","$active_feature_flags":["true-flag"],"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl"},"send_feature_flags":true,"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","distinct_id":"john","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), "requestOptions" => array('shouldVerify' => true), ), @@ -467,4 +463,56 @@ public function testDefaultPropertiesGetAddedProperly(): void ) ); } + + public function testCaptureWithSendFeatureFlagsFalse(): void + { + $this->http_client = new MockedHttpClient(host: "app.posthog.com", flagEndpointResponse: MockedResponses::LOCAL_EVALUATION_MULTIPLE_REQUEST); + $this->client = new Client( + self::FAKE_API_KEY, + [ + "debug" => true, + ], + $this->http_client, + "test" + ); + PostHog::init(null, null, $this->client); + + ClockMock::executeAtFrozenDateTime(new \DateTime('2022-05-01'), function () { + $this->assertTrue( + PostHog::capture( + array ( + "distinctId" => "john", + "event" => "Module PHP Event", + "send_feature_flags" => false + ) + ) + ); + + PostHog::flush(); + + // When send_feature_flags is explicitly false, NO feature flags should be added + $this->assertEquals( + $this->http_client->calls, + array ( + 0 => array ( + "path" => "/api/feature_flag/local_evaluation?send_cohorts&token=random_key", + "payload" => null, + "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION, 1 => 'Authorization: Bearer test'), + "requestOptions" => array("includeEtag" => true), + ), + 1 => array ( + "path" => "/batch/", + "payload" => '{"batch":[{"event":"Module PHP Event","send_feature_flags":false,"properties":{"$lib":"posthog-php","$lib_version":"' . PostHog::VERSION . '","$lib_consumer":"LibCurl"},"library":"posthog-php","library_version":"' . PostHog::VERSION . '","library_consumer":"LibCurl","distinct_id":"john","groups":[],"timestamp":"2022-05-01T00:00:00+00:00","type":"capture"}],"api_key":"random_key"}', + "extraHeaders" => array(0 => 'User-Agent: posthog-php/' . PostHog::VERSION), + "requestOptions" => array('shouldVerify' => true), + ), + ) + ); + + // Verify NO feature flag properties were added + $payload = $this->http_client->calls[1]["payload"]; + $this->assertStringNotContainsString('$feature/', $payload); + $this->assertStringNotContainsString('$active_feature_flags', $payload); + }); + } }