diff --git a/.gitattributes b/.gitattributes index 4e30f2f..4b607e7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,5 +7,6 @@ /.sonarcloud.properties export-ignore /CONTRIBUTING.md export-ignore /DEPLOY.md export-ignore -/phpcs.xml export-ignore /phpunit.xml export-ignore +/.php-cs-fixer.php export-ignore +/phpstan.neon export-ignore diff --git a/.github/workflows/php-ci.yml b/.github/workflows/php-ci.yml index 01f5704..390c632 100644 --- a/.github/workflows/php-ci.yml +++ b/.github/workflows/php-ci.yml @@ -4,7 +4,7 @@ on: schedule: - cron: '0 0 * * *' push: - branches: [ master ] + branches: [ '*' ] pull_request: branches: [ master ] @@ -43,12 +43,14 @@ jobs: - name: Execute tests run: vendor/bin/phpunit - + coverage: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -76,5 +78,68 @@ jobs: - name: Execute coverage run: vendor/bin/phpunit --coverage-clover clover.xml - - name: Upload coverage report - run: bash <(curl -s https://codecov.io/bash) + - name: Upload coverage + uses: codecov/codecov-action@v3 + with: + fail_ci_if_error: true + files: ./clover.xml + + php-cs-fixer: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + ini-values: 'memory_limit=-1' + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: | + composer self-update + composer install --prefer-dist --no-progress --no-suggest --no-plugins + + - run: vendor/bin/php-cs-fixer fix --ansi --diff --dry-run + + phpstan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + ini-values: 'memory_limit=-1' + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: | + composer self-update + composer install --prefer-dist --no-progress --no-suggest --no-plugins + + - run: vendor/bin/phpstan analyse --ansi diff --git a/.gitignore b/.gitignore index f9582fe..af20d34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,7 @@ /vendor composer.lock .idea/ - .DS_Store - .phpunit.result.cache - +.php-cs-fixer.cache /samples/consolesample/vendor \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..abd507c --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,24 @@ +in(__DIR__.'/src') + ->in(__DIR__.'/tests') +; + +$config = (new PhpCsFixer\Config()) + ->setRules([ + '@PhpCsFixer' => true, + '@PSR2' => true, + 'php_unit_internal_class' => false, + 'php_unit_test_class_requires_covers' => false, + 'global_namespace_import' => [ + 'import_classes' => true, + 'import_constants' => true, + 'import_functions' => false, + ], + ]) + ->setUsingCache(true) + ->setFinder($finder) +; + +return $config; diff --git a/DEPLOY.md b/DEPLOY.md index c4f3104..6b2ec56 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -6,15 +6,19 @@ ``` 2. Make sure the code is properly formatted. ```bash - ./vendor/bin/phpcs + vendor/bin/php-cs-fixer fix --ansi --diff --dry-run ``` - > If it shows formatting errors, then you can fix them with the `./vendor/bin/phpcbf` command -3. Run tests + > If it shows formatting errors, then you can fix them with the `vendor/bin/php-cs-fixer fix --ansi` command +3. Make sure the static analyzer doesn't show errors. + ```bash + vendor/bin/phpstan analyse --ansi + ``` +4. Run tests ```bash vendor/bin/phpunit tests ``` -4. Set `SDK_VERSION` constant in `ConfigCatClient.php` -5. Commit & Push +5. Set `SDK_VERSION` constant in `ConfigCatClient.php` +6. Commit & Push ## Publish - Via git tag 1. Create a new version tag. diff --git a/composer.json b/composer.json index d01139f..76451c4 100644 --- a/composer.json +++ b/composer.json @@ -17,15 +17,16 @@ "guzzlehttp/guzzle": "^6.3|^7.0", "psr/log": "^2.0|^3.0", "ext-json": "*", - "monolog/monolog": "^2.0|^3.0", - "z4kn4fein/php-semver": "^2.0" + "z4kn4fein/php-semver": "^2.0", + "psr/http-client": "^1.0" }, "require-dev": { - "phpunit/phpunit": "~7.0|^8", + "phpunit/phpunit": "^9", "illuminate/cache": "^9.0", "psr/simple-cache": "^3.0", "psr/cache": "^1.0", - "squizlabs/php_codesniffer": "^3.7" + "phpstan/phpstan": "^1.0", + "friendsofphp/php-cs-fixer": "^3.0" }, "autoload": { "psr-4": { diff --git a/phpcs.xml b/phpcs.xml deleted file mode 100644 index 9b158ae..0000000 --- a/phpcs.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - The coding standard for ConfigCat. - - src - tests - - */samples/* - */vendor/* - */tests/* - */src/Hash/* - - - - - - - - - \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..099fec6 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +parameters: + level: 8 + paths: + - src + ignoreErrors: + - + message: '#PHPDoc tag \@throws with type Psr\\Cache\\InvalidArgumentException is not subtype of Throwable#' + path: src/Cache/Psr6Cache.php + count: 2 \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index ed5ec68..59b4b59 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,23 +1,13 @@ - - - - tests - - - - - src - - - \ No newline at end of file + + + + + src + + + + + tests + + + diff --git a/src/Attributes/Config.php b/src/Attributes/Config.php index 752cf9b..fc6c4c1 100644 --- a/src/Attributes/Config.php +++ b/src/Attributes/Config.php @@ -1,13 +1,14 @@ + */ private static array $arrayCache = []; /** * Reads the value identified by the given $key from the underlying cache. * - * @param string $key Identifier for the cached value. - * @return ?string Cached value for the given key, or null if it's missing. + * @param string $key identifier for the cached value + * + * @return ?string cached value for the given key, or null if it's missing */ protected function get(string $key): ?string { @@ -24,8 +29,8 @@ protected function get(string $key): ?string /** * Writes the value identified by the given $key into the underlying cache. * - * @param string $key Identifier for the cached value. - * @param string $value The value to cache. + * @param string $key identifier for the cached value + * @param string $value the value to cache */ protected function set(string $key, string $value): void { diff --git a/src/Cache/ConfigCache.php b/src/Cache/ConfigCache.php index be56612..91a79e3 100644 --- a/src/Cache/ConfigCache.php +++ b/src/Cache/ConfigCache.php @@ -1,5 +1,7 @@ set($key, $value->serialize()); } catch (Exception $exception) { - $this->logger->error("Error occurred while writing the cache.", [ - 'event_id' => 2201, 'exception' => $exception + $this->logger->error('Error occurred while writing the cache.', [ + 'event_id' => 2201, 'exception' => $exception, ]); } } @@ -58,16 +43,17 @@ public function store(string $key, ConfigEntry $value): void /** * Reads the value identified by the given $key from the underlying cache. * - * @param string $key Identifier for the cached value. - * @return ConfigEntry Cached value for the given key, or null if it's missing. + * @param string $key identifier for the cached value + * + * @return ConfigEntry cached value for the given key, or null if it's missing * * @throws InvalidArgumentException - * If the $key is not a legal value. + * If the $key is not a legal value */ public function load(string $key): ConfigEntry { if (empty($key)) { - throw new InvalidArgumentException("key cannot be empty."); + throw new InvalidArgumentException('key cannot be empty.'); } try { @@ -75,11 +61,11 @@ public function load(string $key): ConfigEntry if (empty($cached)) { return ConfigEntry::empty(); } - + return ConfigEntry::fromCached($cached); } catch (Exception $exception) { - $this->logger->error("Error occurred while reading the cache.", [ - 'event_id' => 2200, 'exception' => $exception + $this->logger->error('Error occurred while reading the cache.', [ + 'event_id' => 2200, 'exception' => $exception, ]); } @@ -88,11 +74,26 @@ public function load(string $key): ConfigEntry /** * Sets a logger instance on the object. - * - * @param LoggerInterface $logger */ public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; } + + /** + * Reads the value identified by the given $key from the underlying cache. + * + * @param string $key identifier for the cached value + * + * @return null|string cached value for the given key, or null if it's missing + */ + abstract protected function get(string $key): ?string; + + /** + * Writes the value identified by the given $key into the underlying cache. + * + * @param string $key identifier for the cached value + * @param string $value the value to cache + */ + abstract protected function set(string $key, string $value): void; } diff --git a/src/Cache/ConfigEntry.php b/src/Cache/ConfigEntry.php index d35b019..539274a 100644 --- a/src/Cache/ConfigEntry.php +++ b/src/Cache/ConfigEntry.php @@ -1,40 +1,58 @@ configJson; } + /** + * @return mixed[] the deserialized config + */ public function getConfig(): array { return $this->config; } + /** + * @return string the ETag related to the current config + */ public function getEtag(): string { return $this->etag; } + /** + * @return float the time when the current config was fetched + */ public function getFetchTime(): float { return $this->fetchTime; @@ -42,7 +60,7 @@ public function getFetchTime(): float public function serialize(): string { - return $this->fetchTime . "\n" . $this->etag . "\n" . $this->configJson; + return $this->fetchTime."\n".$this->etag."\n".$this->configJson; } public function withTime(float $time): ConfigEntry @@ -52,8 +70,8 @@ public function withTime(float $time): ConfigEntry public static function empty(): ConfigEntry { - if (self::$empty == null) { - self::$empty = new ConfigEntry("", [], "", 0); + if (null == self::$empty) { + self::$empty = new ConfigEntry('', [], '', 0); } return self::$empty; @@ -62,7 +80,7 @@ public static function empty(): ConfigEntry public static function fromConfigJson(string $configJson, string $etag, float $fetchTime): ConfigEntry { $deserialized = json_decode($configJson, true); - if ($deserialized == null) { + if (null == $deserialized) { return self::empty(); } @@ -74,15 +92,15 @@ public static function fromCached(string $cached): ConfigEntry $timePos = strpos($cached, "\n"); $etagPos = strpos($cached, "\n", $timePos + 1); - if ($timePos === false || $etagPos === false) { - throw new UnexpectedValueException("Number of values is fewer than expected."); + if (false === $timePos || false === $etagPos) { + throw new UnexpectedValueException('Number of values is fewer than expected.'); } $fetchTimeString = substr($cached, 0, $timePos); $fetchTime = floatval($fetchTimeString); - if ($fetchTime == 0) { - throw new UnexpectedValueException("Invalid fetch time: " . $fetchTimeString); + if (0 == $fetchTime) { + throw new UnexpectedValueException('Invalid fetch time: '.$fetchTimeString); } $etag = substr($cached, $timePos + 1, $etagPos - $timePos - 1); diff --git a/src/Cache/LaravelCache.php b/src/Cache/LaravelCache.php index e9223ea..829e06d 100644 --- a/src/Cache/LaravelCache.php +++ b/src/Cache/LaravelCache.php @@ -1,5 +1,7 @@ cachePool->getItem($key); + return $item->get(); } /** * Writes the value identified by the given $key into the underlying cache. * - * @param string $key Identifier for the cached value. - * @param string $value The value to cache. + * @param string $key identifier for the cached value + * @param string $value the value to cache * - * @throws InvalidArgumentException If the $key is not a legal value. + * @throws InvalidArgumentException if the $key is not a legal value */ protected function set(string $key, string $value): void { diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 7d3ec97..97fe576 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -1,61 +1,67 @@ new \ConfigCat\Cache\LaravelCache(Cache::store()), + * \ConfigCat\ClientOptions::CACHE_REFRESH_INTERVAL => 5 + * ]); + * + * The configuration options include the following: + * + * - base-url: The base ConfigCat CDN url. + * - logger: A \Psr\Log\LoggerInterface implementation used for logging. + * - cache: A \ConfigCat\ConfigCache implementation used for caching the latest feature flag and setting values. + * - cache-refresh-interval: Sets how frequent the cached configuration should be refreshed in seconds. + * - request-options: Additional options for Guzzle http requests. + * https://docs.guzzlephp.org/en/stable/request-options.html + * - custom-handler: A custom callable Guzzle http handler. + * - fetch-client: A \ConfigCat\Http\FetchClientInterface implementation that wraps an actual HTTP client used + * to make HTTP requests towards ConfigCat. + * When it's not set, \ConfigCat\Http\FetchClient::Guzzle() is used by default. + * - data-governance: Default: Global. Set this parameter to be in sync with the Data Governance + * preference on the Dashboard: https://app.configcat.com/organization/data-governance + * (Only Organization Admins can access) + * - exceptions-to-ignore: Array of exception classes that should be ignored from logs. + * - flag-overrides: A \ConfigCat\Override\FlagOverrides instance used to override + * feature flags & settings. + * - log-level: Default: Warning. Sets the internal log level. + * - default-user: A \ConfigCat\User as default user. + * - offline: Default: false. Indicates whether the SDK should be initialized in offline mode or not. + * + * @param string $sdkKey the SDK Key used to communicate with the ConfigCat services + * @param mixed[] $options the configuration options * - * @throws InvalidArgumentException - * When the $sdkKey is not valid. + * @throws InvalidArgumentException if the $sdkKey is not valid + * + * @see ClientOptions for the list of all available options. */ public function __construct(string $sdkKey, array $options = []) { @@ -73,32 +85,32 @@ public function __construct(string $sdkKey, array $options = []) } $this->hooks = new Hooks(); - $this->cacheKey = sha1(sprintf("%s_" . ConfigFetcher::CONFIG_JSON_NAME . "_" . self::CONFIG_JSON_CACHE_VERSION, $sdkKey)); + $this->cacheKey = sha1(sprintf('%s_'.ConfigFetcher::CONFIG_JSON_NAME.'_'.self::CONFIG_JSON_CACHE_VERSION, $sdkKey)); - $externalLogger = (isset($options[ClientOptions::LOGGER]) && - $options[ClientOptions::LOGGER] instanceof LoggerInterface) + $externalLogger = (isset($options[ClientOptions::LOGGER]) + && $options[ClientOptions::LOGGER] instanceof LoggerInterface) ? $options[ClientOptions::LOGGER] - : $this->getMonolog(); + : new DefaultLogger(); - $logLevel = (isset($options[ClientOptions::LOG_LEVEL]) && - LogLevel::isValid($options[ClientOptions::LOG_LEVEL])) + $logLevel = (isset($options[ClientOptions::LOG_LEVEL]) + && LogLevel::isValid($options[ClientOptions::LOG_LEVEL])) ? $options[ClientOptions::LOG_LEVEL] : LogLevel::WARNING; - $exceptionsToIgnore = (isset($options[ClientOptions::EXCEPTIONS_TO_IGNORE]) && - is_array($options[ClientOptions::EXCEPTIONS_TO_IGNORE])) + $exceptionsToIgnore = (isset($options[ClientOptions::EXCEPTIONS_TO_IGNORE]) + && is_array($options[ClientOptions::EXCEPTIONS_TO_IGNORE])) ? $options[ClientOptions::EXCEPTIONS_TO_IGNORE] : []; $this->logger = new InternalLogger($externalLogger, $logLevel, $exceptionsToIgnore, $this->hooks); - $this->overrides = (isset($options[ClientOptions::FLAG_OVERRIDES]) && - $options[ClientOptions::FLAG_OVERRIDES] instanceof FlagOverrides) + $this->overrides = (isset($options[ClientOptions::FLAG_OVERRIDES]) + && $options[ClientOptions::FLAG_OVERRIDES] instanceof FlagOverrides) ? $options[ClientOptions::FLAG_OVERRIDES] : null; - $this->defaultUser = (isset($options[ClientOptions::DEFAULT_USER]) && - $options[ClientOptions::DEFAULT_USER] instanceof User) + $this->defaultUser = (isset($options[ClientOptions::DEFAULT_USER]) + && $options[ClientOptions::DEFAULT_USER] instanceof User) ? $options[ClientOptions::DEFAULT_USER] : null; @@ -106,15 +118,14 @@ public function __construct(string $sdkKey, array $options = []) ? $options[ClientOptions::CACHE] : new ArrayCache(); - - if (isset($options[ClientOptions::CACHE_REFRESH_INTERVAL]) && - is_int($options[ClientOptions::CACHE_REFRESH_INTERVAL])) { + if (isset($options[ClientOptions::CACHE_REFRESH_INTERVAL]) + && is_int($options[ClientOptions::CACHE_REFRESH_INTERVAL])) { $this->cacheRefreshInterval = $options[ClientOptions::CACHE_REFRESH_INTERVAL]; } $this->overrides?->setLogger($this->logger); - if (isset($options[ClientOptions::OFFLINE]) && $options[ClientOptions::OFFLINE] === true) { + if (isset($options[ClientOptions::OFFLINE]) && true === $options[ClientOptions::OFFLINE]) { $this->offline = true; } @@ -126,23 +137,25 @@ public function __construct(string $sdkKey, array $options = []) /** * Gets a value of a feature flag or setting identified by the given key. * - * @param string $key The identifier of the configuration value. - * @param mixed $defaultValue In case of any failure, this value will be returned. - * @param ?User $user The user object to identify the caller. - * @return mixed The configuration value identified by the given key. + * @param string $key the identifier of the configuration value + * @param mixed $defaultValue in case of any failure, this value will be returned + * @param ?User $user the user object to identify the caller + * + * @return mixed the configuration value identified by the given key */ public function getValue(string $key, mixed $defaultValue, ?User $user = null): mixed { try { $settingsResult = $this->getSettingsResult(); - $errorMessage = $this->checkSettingAvailable($settingsResult, $key, '$defaultValue', $defaultValue); - if ($errorMessage !== null) { + $errorMessage = $this->checkSettingAvailable($settingsResult, $key, $defaultValue); + if (null !== $errorMessage) { $this->hooks->fireOnFlagEvaluated(EvaluationDetails::fromError( $key, $defaultValue, $user, $errorMessage )); + return $defaultValue; } @@ -153,12 +166,11 @@ public function getValue(string $key, mixed $defaultValue, ?User $user = null): $settingsResult->fetchTime )->getValue(); } catch (Exception $exception) { - $message = "Error occurred in the `{METHOD_NAME}` method while evaluating setting '{KEY}'. " . - "Returning the `{DEFAULT_PARAM_NAME}` parameter that you specified in your application: '{DEFAULT_PARAM_VALUE}'."; + $message = "Error occurred in the `getValue` method while evaluating setting '".$key."'. ". + 'Returning the `defaultValue` parameter that you specified '. + "in your application: '".Utils::getStringRepresentation($defaultValue)."'."; $messageCtx = [ 'event_id' => 1002, 'exception' => $exception, - 'METHOD_NAME' => 'getValue', 'KEY' => $key, - 'DEFAULT_PARAM_NAME' => '$defaultValue', 'DEFAULT_PARAM_VALUE' => Utils::getStringRepresentation($defaultValue) ]; $this->logger->error($message, $messageCtx); $this->hooks->fireOnFlagEvaluated(EvaluationDetails::fromError( @@ -167,6 +179,7 @@ public function getValue(string $key, mixed $defaultValue, ?User $user = null): $user, InternalLogger::format($message, $messageCtx) )); + return $defaultValue; } } @@ -174,17 +187,18 @@ public function getValue(string $key, mixed $defaultValue, ?User $user = null): /** * Gets the value and evaluation details of a feature flag or setting identified by the given key. * - * @param string $key The identifier of the configuration value. - * @param mixed $defaultValue In case of any failure, this value will be returned. - * @param ?User $user The user object to identify the caller. - * @return mixed The configuration value identified by the given key. + * @param string $key the identifier of the configuration value + * @param mixed $defaultValue in case of any failure, this value will be returned + * @param ?User $user the user object to identify the caller + * + * @return EvaluationDetails the configuration value identified by the given key */ public function getValueDetails(string $key, mixed $defaultValue, ?User $user = null): EvaluationDetails { try { $settingsResult = $this->getSettingsResult(); - $errorMessage = $this->checkSettingAvailable($settingsResult, $key, '$defaultValue', $defaultValue); - if ($errorMessage !== null) { + $errorMessage = $this->checkSettingAvailable($settingsResult, $key, $defaultValue); + if (null !== $errorMessage) { $details = EvaluationDetails::fromError( $key, $defaultValue, @@ -192,21 +206,22 @@ public function getValueDetails(string $key, mixed $defaultValue, ?User $user = $errorMessage ); $this->hooks->fireOnFlagEvaluated($details); + return $details; } return $this->evaluate($key, $settingsResult->settings[$key], $user, $settingsResult->fetchTime); } catch (Exception $exception) { - $message = "Error occurred in the `{METHOD_NAME}` method while evaluating setting '{KEY}'. " . - "Returning the `{DEFAULT_PARAM_NAME}` parameter that you specified in your application: '{DEFAULT_PARAM_VALUE}'."; + $message = "Error occurred in the `getValueDetails` method while evaluating setting '".$key."'. ". + 'Returning the `defaultValue` parameter that you specified in '. + "your application: '".Utils::getStringRepresentation($defaultValue)."'."; $messageCtx = [ 'event_id' => 1002, 'exception' => $exception, - 'METHOD_NAME' => 'getValueDetails', 'KEY' => $key, - 'DEFAULT_PARAM_NAME' => '$defaultValue', 'DEFAULT_PARAM_VALUE' => Utils::getStringRepresentation($defaultValue) ]; $this->logger->error($message, $messageCtx); $details = EvaluationDetails::fromError($key, $defaultValue, $user, InternalLogger::format($message, $messageCtx)); $this->hooks->fireOnFlagEvaluated($details); + return $details; } } @@ -214,26 +229,26 @@ public function getValueDetails(string $key, mixed $defaultValue, ?User $user = /** * Gets the key of a setting and its value identified by the given Variation ID (analytics). * - * @param string $variationId The Variation ID. - * @return ?Pair of the key and value of a setting. + * @param string $variationId the Variation ID + * + * @return ?Pair of the key and value of a setting */ public function getKeyAndValue(string $variationId): ?Pair { try { $settingsResult = $this->getSettingsResult(); - if (!$this->checkSettingsAvailable($settingsResult, "null")) { + if (!$this->checkSettingsAvailable($settingsResult, 'null')) { return null; } - return $settingsResult->settings === null + return empty($settingsResult->settings) ? null : $this->parseKeyAndValue($settingsResult->settings, $variationId); } catch (Exception $exception) { - $this->logger->error("Error occurred in the `{METHOD_NAME}` method. Returning {DEFAULT_RETURN_VALUE}.", [ + $this->logger->error('Error occurred in the `getKeyAndValue` method. Returning null.', [ 'event_id' => 1002, 'exception' => $exception, - 'METHOD_NAME' => 'getKeyAndValue', - 'DEFAULT_RETURN_VALUE' => "null" ]); + return null; } } @@ -241,23 +256,22 @@ public function getKeyAndValue(string $variationId): ?Pair /** * Gets a collection of all setting keys. * - * @return array of keys. + * @return string[] of keys */ public function getAllKeys(): array { try { $settingsResult = $this->getSettingsResult(); - if (!$this->checkSettingsAvailable($settingsResult, "empty array")) { + if (!$this->checkSettingsAvailable($settingsResult, 'empty array')) { return []; } - return $settingsResult->settings === null ? [] : array_keys($settingsResult->settings); + return empty($settingsResult->settings) ? [] : array_keys($settingsResult->settings); } catch (Exception $exception) { - $this->logger->error("Error occurred in the `{METHOD_NAME}` method. Returning {DEFAULT_RETURN_VALUE}.", [ + $this->logger->error('Error occurred in the `getAllKeys` method. Returning empty array.', [ 'event_id' => 1002, 'exception' => $exception, - 'METHOD_NAME' => 'getAllKeys', - 'DEFAULT_RETURN_VALUE' => "empty array" ]); + return []; } } @@ -265,24 +279,24 @@ public function getAllKeys(): array /** * Gets the values of all feature flags or settings. * - * @param ?User $user The user object to identify the caller. - * @return array of values. + * @param ?User $user the user object to identify the caller + * + * @return mixed[] of values */ public function getAllValues(?User $user = null): array { try { $settingsResult = $this->getSettingsResult(); - if (!$this->checkSettingsAvailable($settingsResult, "empty array")) { + if (!$this->checkSettingsAvailable($settingsResult, 'empty array')) { return []; } - - return $settingsResult->settings === null ? [] : $this->parseValues($settingsResult, $user); + + return empty($settingsResult->settings) ? [] : $this->parseValues($settingsResult, $user); } catch (Exception $exception) { - $this->logger->error("Error occurred in the `{METHOD_NAME}` method. Returning {DEFAULT_RETURN_VALUE}.", [ + $this->logger->error('Error occurred in the `getAllValues` method. Returning empty array.', [ 'event_id' => 1002, 'exception' => $exception, - 'METHOD_NAME' => 'getAllValues', - 'DEFAULT_RETURN_VALUE' => "empty array" ]); + return []; } } @@ -290,14 +304,15 @@ public function getAllValues(?User $user = null): array /** * Gets the values along with evaluation details of all feature flags and settings. * - * @param ?User $user The user object to identify the caller. - * @return EvaluationDetails[] of evaluation details of all feature flags and settings. + * @param ?User $user the user object to identify the caller + * + * @return EvaluationDetails[] of evaluation details of all feature flags and settings */ public function getAllValueDetails(?User $user = null): array { try { $settingsResult = $this->getSettingsResult(); - if (!$this->checkSettingsAvailable($settingsResult, "empty array")) { + if (!$this->checkSettingsAvailable($settingsResult, 'empty array')) { return []; } @@ -311,13 +326,13 @@ public function getAllValueDetails(?User $user = null): array $settingsResult->fetchTime ); } + return $result; } catch (Exception $exception) { - $this->logger->error("Error occurred in the `{METHOD_NAME}` method. Returning {DEFAULT_RETURN_VALUE}.", [ + $this->logger->error('Error occurred in the `getAllValueDetails` method. Returning empty array.', [ 'event_id' => 1002, 'exception' => $exception, - 'METHOD_NAME' => 'getAllValueDetails', - 'DEFAULT_RETURN_VALUE' => "empty array" ]); + return []; } } @@ -327,22 +342,22 @@ public function getAllValueDetails(?User $user = null): array */ public function forceRefresh(): RefreshResult { - if ($this->overrides !== null && OverrideBehaviour::LOCAL_ONLY == $this->overrides->getBehaviour()) { - $message = "Client is configured to use the `{OVERRIDE_BEHAVIOR}` override behavior, thus `{METHOD_NAME}()` has no effect."; + if (null !== $this->overrides && OverrideBehaviour::LOCAL_ONLY == $this->overrides->getBehaviour()) { + $message = 'Client is configured to use the `LOCAL_ONLY` override behavior, thus `forceRefresh()` has no effect.'; $messageCtx = [ 'event_id' => 3202, - 'OVERRIDE_BEHAVIOR' => 'LOCAL_ONLY', - 'METHOD_NAME' => 'forceRefresh' ]; $this->logger->warning($message, $messageCtx); + return new RefreshResult(false, InternalLogger::format($message, $messageCtx)); } if ($this->offline) { - $message = "Client is in offline mode, it cannot initiate HTTP calls."; + $message = 'Client is in offline mode, it cannot initiate HTTP calls.'; $this->logger->warning($message, [ - 'event_id' => 3200 + 'event_id' => 3200, ]); + return new RefreshResult(false, $message); } @@ -372,7 +387,7 @@ public function clearDefaultUser(): void /** * Gets the Hooks object for subscribing to SDK events. * - * @return Hooks for subscribing to SDK events. + * @return Hooks for subscribing to SDK events */ public function hooks(): Hooks { @@ -405,50 +420,56 @@ public function isOffline(): bool private function checkSettingsAvailable(SettingsResult $settingsResult, string $defaultReturnValue): bool { - if ($settingsResult->settings === null) { - $this->logger->error("Config JSON is not present. Returning {DEFAULT_RETURN_VALUE}.", [ + if (empty($settingsResult->settings)) { + $this->logger->error('Config JSON is not present. Returning '.$defaultReturnValue.'.', [ 'event_id' => 1000, - 'DEFAULT_RETURN_VALUE' => $defaultReturnValue ]); + return false; } return true; } - private function checkSettingAvailable(SettingsResult $settingsResult, string $key, string $defaultValueParam, mixed $defaultValue): ?string + private function checkSettingAvailable(SettingsResult $settingsResult, string $key, mixed $defaultValue): ?string { - if ($settingsResult->settings === null) { - $message = "Config JSON is not present when evaluating setting '{KEY}'. " . - "Returning the `{DEFAULT_PARAM_NAME}` parameter that you specified in your application: '{DEFAULT_PARAM_VALUE}'."; + if (!$settingsResult->hasConfigJson) { + $message = "Config JSON is not present when evaluating setting '".$key."'. ". + 'Returning the `defaultValue` parameter that you specified in '. + "your application: '".Utils::getStringRepresentation($defaultValue)."'."; $messageCtx = [ 'event_id' => 1000, - 'KEY' => $key, - 'DEFAULT_PARAM_NAME' => $defaultValueParam, 'DEFAULT_PARAM_VALUE' => Utils::getStringRepresentation($defaultValue) ]; $this->logger->error($message, $messageCtx); + return InternalLogger::format($message, $messageCtx); } if (!array_key_exists($key, $settingsResult->settings)) { - $message = "Failed to evaluate setting '{KEY}' (the key was not found in config JSON). " . - "Returning the `{DEFAULT_PARAM_NAME}` parameter that you specified in your application: '{DEFAULT_PARAM_VALUE}'. " . - "Available keys: [{AVAILABLE_KEYS}]."; + $message = "Failed to evaluate setting '".$key."' (the key was not found in config JSON). ". + 'Returning the `defaultValue` parameter that you specified in your '. + "application: '".Utils::getStringRepresentation($defaultValue)."'. ". + 'Available keys: ['.(!empty($settingsResult->settings) ? "'".implode("', '", array_keys($settingsResult->settings))."'" : '').'].'; $messageCtx = [ 'event_id' => 1001, - 'KEY' => $key, - 'DEFAULT_PARAM_NAME' => $defaultValueParam, 'DEFAULT_PARAM_VALUE' => Utils::getStringRepresentation($defaultValue), - 'AVAILABLE_KEYS' => !empty($settingsResult->settings) ? "'".implode("', '", array_keys($settingsResult->settings))."'" : "" ]; $this->logger->error($message, $messageCtx); + return InternalLogger::format($message, $messageCtx); } return null; } + /** + * @return mixed[] + */ private function parseValues(SettingsResult $settingsResult, User $user = null): array { + if (empty($settingsResult->settings)) { + return []; + } + $keys = array_keys($settingsResult->settings); $result = []; foreach ($keys as $key) { @@ -463,15 +484,17 @@ private function parseValues(SettingsResult $settingsResult, User $user = null): return $result; } + /** + * @param mixed[] $setting + */ private function evaluate(string $key, array $setting, ?User $user, float $fetchTime): EvaluationDetails { - $actualUser = $user === null ? $this->defaultUser : $user; + $actualUser = null === $user ? $this->defaultUser : $user; $collector = new EvaluationLogCollector(); - $collector->add("Evaluating " . $key . "."); + $collector->add('Evaluating '.$key.'.'); $result = $this->evaluator->evaluate($key, $setting, $collector, $actualUser); - $this->logger->info("{EVALUATE_LOG}", [ + $this->logger->info((string) $collector, [ 'event_id' => 5000, - 'EVALUATE_LOG' => $collector ]); $details = new EvaluationDetails( $key, @@ -485,10 +508,14 @@ private function evaluate(string $key, array $setting, ?User $user, float $fetch $result->percentageRule ); $this->hooks->fireOnFlagEvaluated($details); + return $details; } - private function parseKeyAndValue(array $json, $variationId): ?Pair + /** + * @param array $json + */ + private function parseKeyAndValue(array $json, string $variationId): ?Pair { foreach ($json as $key => $value) { if ($variationId == $value[SettingAttributes::VARIATION_ID]) { @@ -511,27 +538,31 @@ private function parseKeyAndValue(array $json, $variationId): ?Pair } } - $this->logger->error("Could not find the setting for the specified variation ID: '{VARIATION_ID}'.", [ + $this->logger->error("Could not find the setting for the specified variation ID: '".$variationId."'.", [ 'event_id' => 2011, - 'VARIATION_ID' => $variationId ]); + return null; } private function getSettingsResult(): SettingsResult { - if ($this->overrides !== null) { + if (null !== $this->overrides) { switch ($this->overrides->getBehaviour()) { case OverrideBehaviour::LOCAL_ONLY: - return new SettingsResult($this->overrides->getDataSource()->getOverrides(), 0); + return new SettingsResult($this->overrides->getDataSource()->getOverrides(), 0, true); + case OverrideBehaviour::LOCAL_OVER_REMOTE: $local = $this->overrides->getDataSource()->getOverrides(); $remote = $this->getRemoteSettingsResult(); - return new SettingsResult(array_merge($remote->settings ?? [], $local), $remote->fetchTime); + + return new SettingsResult(array_merge($remote->settings, $local), $remote->fetchTime, $remote->hasConfigJson); + default: // remote over local $local = $this->overrides->getDataSource()->getOverrides(); $remote = $this->getRemoteSettingsResult(); - return new SettingsResult(array_merge($local, $remote->settings ?? []), $remote->fetchTime); + + return new SettingsResult(array_merge($local, $remote->settings), $remote->fetchTime, $remote->hasConfigJson); } } @@ -547,10 +578,10 @@ private function getRemoteSettingsResult(): SettingsResult } if (empty($cacheEntry->getConfig())) { - return new SettingsResult(null, 0); + return new SettingsResult([], 0, false); } - return new SettingsResult($cacheEntry->getConfig()[Config::ENTRIES], $cacheEntry->getFetchTime()); + return new SettingsResult($cacheEntry->getConfig()[Config::ENTRIES], $cacheEntry->getFetchTime(), true); } private function handleResponse(FetchResponse $response, ConfigEntry $cacheEntry): ConfigEntry @@ -558,32 +589,16 @@ private function handleResponse(FetchResponse $response, ConfigEntry $cacheEntry if ($response->isFetched()) { $this->hooks->fireOnConfigChanged($response->getConfigEntry()->getConfig()[Config::ENTRIES]); $this->cache->store($this->cacheKey, $response->getConfigEntry()); + return $response->getConfigEntry(); - } elseif ($response->isNotModified()) { + } + if ($response->isNotModified()) { $newEntry = $cacheEntry->withTime(Utils::getUnixMilliseconds()); $this->cache->store($this->cacheKey, $newEntry); + return $newEntry; } - return $cacheEntry; - } - private function getMonolog(): Logger - { - $handler = new ErrorLogHandler(); - // This is a slightly modified version of the default log message format used by LineFormatter - // (see https://github.com/Seldaek/monolog/blob/3.3.1/src/Monolog/Formatter/LineFormatter.php#L28), - // we just add the event ID. - $formatter = new LineFormatter( - "[%datetime%] %channel%.%level_name%: [%context.event_id%] %message% %context% %extra%\n", - null, - true, - true - ); - $handler->setFormatter($formatter); - // We use placeholders for message arguments as defined by the PSR-3 standard - // (see https://www.php-fig.org/psr/psr-3/#12-message). Since `true` is passed - // as the 2nd argument, placeholder values will be removed from the context array. - $psrProcessor = new PsrLogMessageProcessor(null, true); - return new Logger("ConfigCat", [$handler], [$psrProcessor]); + return $cacheEntry; } } diff --git a/src/ConfigCatClientException.php b/src/ConfigCatClientException.php index 8ff0ab1..d81555d 100644 --- a/src/ConfigCatClientException.php +++ b/src/ConfigCatClientException.php @@ -1,7 +1,9 @@ urlPath = sprintf("configuration-files/%s/" . self::CONFIG_JSON_NAME, $sdkKey); + $this->urlPath = sprintf('configuration-files/%s/'.self::CONFIG_JSON_NAME, $sdkKey); if (isset($options[ClientOptions::BASE_URL]) && !empty($options[ClientOptions::BASE_URL])) { $this->baseUrl = $options[ClientOptions::BASE_URL]; $this->urlIsCustom = true; - } elseif (isset($options[ClientOptions::DATA_GOVERNANCE]) && - DataGovernance::isValid($options[ClientOptions::DATA_GOVERNANCE])) { + } elseif (isset($options[ClientOptions::DATA_GOVERNANCE]) + && DataGovernance::isValid($options[ClientOptions::DATA_GOVERNANCE])) { $this->baseUrl = DataGovernance::isEuOnly($options[ClientOptions::DATA_GOVERNANCE]) ? self::EU_ONLY_URL : self::GLOBAL_URL; @@ -73,51 +66,28 @@ public function __construct(string $sdkKey, InternalLogger $logger, array $optio $this->baseUrl = self::GLOBAL_URL; } - $additionalOptions = isset($options[ClientOptions::REQUEST_OPTIONS]) - && is_array($options[ClientOptions::REQUEST_OPTIONS]) - && !empty($options[ClientOptions::REQUEST_OPTIONS]) - ? $options[ClientOptions::REQUEST_OPTIONS] - : []; - - if (!isset($additionalOptions[RequestOptions::CONNECT_TIMEOUT])) { - $additionalOptions[RequestOptions::CONNECT_TIMEOUT] = 10; - } - - if (!isset($additionalOptions[RequestOptions::TIMEOUT])) { - $additionalOptions[RequestOptions::TIMEOUT] = 30; - } + $this->userAgentHeader = 'ConfigCat-PHP/'.ConfigCatClient::SDK_VERSION; $this->logger = $logger; - $this->requestOptions = array_merge([ - 'headers' => [ - 'X-ConfigCat-UserAgent' => "ConfigCat-PHP/" . ConfigCatClient::SDK_VERSION - ], - ], $additionalOptions); - - $this->clientOptions = isset($options[ClientOptions::CUSTOM_HANDLER]) - ? ['handler' => $options[ClientOptions::CUSTOM_HANDLER]] - : []; - $this->client = new Client($this->clientOptions); + $this->client = (isset($options[ClientOptions::FETCH_CLIENT]) + && $options[ClientOptions::FETCH_CLIENT] instanceof FetchClientInterface) + ? $options[ClientOptions::FETCH_CLIENT] + : GuzzleFetchClient::create($options); } /** * Gets the latest configuration from the network. * - * @param ?string $etag The ETag. + * @param ?string $etag the ETag * - * @return FetchResponse An object describing the result of the fetch. + * @return FetchResponse an object describing the result of the fetch */ public function fetch(?string $etag): FetchResponse { return $this->executeFetch($etag, $this->baseUrl, 2); } - public function getRequestOptions(): array - { - return $this->requestOptions; - } - private function executeFetch(?string $etag, string $url, int $executionCount): FetchResponse { $response = $this->sendConfigFetchRequest($etag, $url); @@ -126,7 +96,7 @@ private function executeFetch(?string $etag, string $url, int $executionCount): return $response; } - $newUrl = ""; + $newUrl = ''; if (isset($response->getConfigEntry()->getConfig()[Config::PREFERENCES][Preferences::BASE_URL])) { $newUrl = $response->getConfigEntry()->getConfig()[Config::PREFERENCES][Preferences::BASE_URL]; } @@ -136,21 +106,21 @@ private function executeFetch(?string $etag, string $url, int $executionCount): $preferences = $response->getConfigEntry()->getConfig()[Config::PREFERENCES]; $redirect = $preferences[Preferences::REDIRECT]; - if ($this->urlIsCustom && $redirect != self::FORCE_REDIRECT) { + if ($this->urlIsCustom && self::FORCE_REDIRECT != $redirect) { return $response; } - if ($redirect == self::NO_REDIRECT) { + if (self::NO_REDIRECT == $redirect) { return $response; } - if ($redirect == self::SHOULD_REDIRECT) { + if (self::SHOULD_REDIRECT == $redirect) { $this->logger->warning( - "The `dataGovernance` parameter specified at the client initialization is ". - "not in sync with the preferences on the ConfigCat Dashboard. " . - "Read more: https://configcat.com/docs/advanced/data-governance/", + 'The `dataGovernance` parameter specified at the client initialization is '. + 'not in sync with the preferences on the ConfigCat Dashboard. '. + 'Read more: https://configcat.com/docs/advanced/data-governance/', [ - 'event_id' => 3002 + 'event_id' => 3002, ] ); } @@ -159,21 +129,27 @@ private function executeFetch(?string $etag, string $url, int $executionCount): return $this->executeFetch($etag, $newUrl, $executionCount - 1); } - $this->logger->error("Redirection loop encountered while trying to fetch config JSON. Please contact us at https://configcat.com/support/", [ - 'event_id' => 1104 + $this->logger->error('Redirection loop encountered while trying to fetch config JSON. Please contact us at https://configcat.com/support/', [ + 'event_id' => 1104, ]); + return $response; } private function sendConfigFetchRequest(?string $etag, string $url): FetchResponse { + $configJsonUrl = sprintf('%s/%s', $url, $this->urlPath); + $request = $this->client->createRequest('GET', $configJsonUrl) + ->withHeader('X-ConfigCat-UserAgent', $this->userAgentHeader) + ; + if (!empty($etag)) { - $this->requestOptions['headers']['If-None-Match'] = $etag; + $request = $request->withHeader('If-None-Match', $etag); } try { - $configJsonUrl = sprintf("%s/%s", $url, $this->urlPath); - $response = $this->client->get($configJsonUrl, $this->requestOptions); + $fetchClient = $this->client->getClient(); + $response = $fetchClient->sendRequest($request); $statusCode = $response->getStatusCode(); if ($response->hasHeader(self::ETAG_HEADER)) { @@ -181,52 +157,40 @@ private function sendConfigFetchRequest(?string $etag, string $url): FetchRespon } if ($statusCode >= 200 && $statusCode < 300) { - $this->logger->debug("Fetch was successful: new config fetched."); + $this->logger->debug('Fetch was successful: new config fetched.'); - $entry = ConfigEntry::fromConfigJson($response->getBody(), $etag, Utils::getUnixMilliseconds()); - if (json_last_error() !== JSON_ERROR_NONE) { - $message = "Fetching config JSON was successful but the HTTP response content was invalid. JSON error: {JSON_ERROR}"; + $entry = ConfigEntry::fromConfigJson((string) $response->getBody(), $etag ?? '', Utils::getUnixMilliseconds()); + if (JSON_ERROR_NONE !== json_last_error()) { + $message = 'Fetching config JSON was successful but the HTTP response content was invalid. JSON error: '.json_last_error_msg(); $messageCtx = [ 'event_id' => 1105, - 'JSON_ERROR' => json_last_error_msg() ]; $this->logger->error($message, $messageCtx); + return FetchResponse::failure(InternalLogger::format($message, $messageCtx)); } return FetchResponse::success($entry); - } elseif ($statusCode === 304) { - $this->logger->debug("Fetch was successful: config not modified."); + } + if (304 === $statusCode) { + $this->logger->debug('Fetch was successful: config not modified.'); + return FetchResponse::notModified(); } - $message = "Your SDK Key seems to be wrong. You can find the valid SDK Key at https://app.configcat.com/sdkkey. " . - "Received unexpected response: {STATUS_CODE}"; + $message = 'Your SDK Key seems to be wrong. You can find the valid SDK Key at https://app.configcat.com/sdkkey. '. + "Received unexpected response: {$statusCode}"; $messageCtx = [ 'event_id' => 1100, - 'STATUS_CODE' => $statusCode - ]; - $this->logger->error($message, $messageCtx); - return FetchResponse::failure(InternalLogger::format($message, $messageCtx)); - } catch (ConnectException $exception) { - $connTimeout = $this->requestOptions[RequestOptions::CONNECT_TIMEOUT]; - $timeout = $this->requestOptions[RequestOptions::TIMEOUT]; - $message = "Request timed out while trying to fetch config JSON. Timeout values: [connect: {CONN_TIMEOUT}s, timeout: {TIMEOUT}s]"; - $messageCtx = [ - 'event_id' => 1102, 'exception' => $exception, - 'CONN_TIMEOUT' => $connTimeout, 'TIMEOUT' => $timeout, ]; $this->logger->error($message, $messageCtx); + return FetchResponse::failure(InternalLogger::format($message, $messageCtx)); - } catch (GuzzleException $exception) { - $message = "Unexpected HTTP response was received while trying to fetch config JSON."; - $messageCtx = ['event_id' => 1101, 'exception' => $exception]; - $this->logger->error($message, $messageCtx); - return FetchResponse::failure($message); - } catch (Exception $exception) { - $message = "Unexpected error occurred while trying to fetch config JSON."; + } catch (ClientExceptionInterface $exception) { + $message = 'Unexpected error occurred while trying to fetch config JSON.'; $messageCtx = ['event_id' => 1103, 'exception' => $exception]; $this->logger->error($message, $messageCtx); + return FetchResponse::failure($message); } } diff --git a/src/DataGovernance.php b/src/DataGovernance.php index 0a8e832..add5452 100644 --- a/src/DataGovernance.php +++ b/src/DataGovernance.php @@ -1,30 +1,32 @@ entries[] = $entry; + return implode(PHP_EOL, $this->entries); } - public function __toString(): string + public function add(string $entry): void { - return implode(PHP_EOL, $this->entries); + $this->entries[] = $entry; } } diff --git a/src/EvaluationResult.php b/src/EvaluationResult.php index b7994c2..08c3cb4 100644 --- a/src/EvaluationResult.php +++ b/src/EvaluationResult.php @@ -1,5 +1,7 @@ status === self::FETCHED; + return self::FETCHED === $this->status; } /** * Returns true when the response is in not modified state. * - * @return bool True if the fetched configurations was not modified, otherwise false. + * @return bool true if the fetched configurations was not modified, otherwise false */ public function isNotModified(): bool { - return $this->status === self::NOT_MODIFIED; + return self::NOT_MODIFIED === $this->status; } /** * Returns true when the response is in failed state. * - * @return bool True if the fetch failed, otherwise false. + * @return bool true if the fetch failed, otherwise false */ public function isFailed(): bool { - return $this->status === self::FAILED; + return self::FAILED === $this->status; } /** * Returns the produced config entry. * - * @return ConfigEntry The produced config entry. + * @return ConfigEntry the produced config entry */ public function getConfigEntry(): ConfigEntry { @@ -100,7 +105,7 @@ public function getConfigEntry(): ConfigEntry /** * Returns the error if the fetch failed. * - * @return ?string The error. + * @return ?string the error */ public function getError(): ?string { diff --git a/src/Hooks.php b/src/Hooks.php index 0f46524..ded72af 100644 --- a/src/Hooks.php +++ b/src/Hooks.php @@ -1,15 +1,27 @@ $settings + * * @internal */ public function fireOnConfigChanged(array $settings): void diff --git a/src/Http/FetchClientInterface.php b/src/Http/FetchClientInterface.php new file mode 100644 index 0000000..c319aa5 --- /dev/null +++ b/src/Http/FetchClientInterface.php @@ -0,0 +1,28 @@ + $options + */ + private function __construct(array $options = []) + { + if (!isset($options[RequestOptions::CONNECT_TIMEOUT])) { + $options[RequestOptions::CONNECT_TIMEOUT] = 10; + } + + if (!isset($options[RequestOptions::TIMEOUT])) { + $options[RequestOptions::TIMEOUT] = 30; + } + + if (isset($options[ClientOptions::CUSTOM_HANDLER])) { + $options['handler'] = $options[ClientOptions::CUSTOM_HANDLER]; + } + + $requestOptions = isset($options[ClientOptions::REQUEST_OPTIONS]) + && is_array($options[ClientOptions::REQUEST_OPTIONS]) + && !empty($options[ClientOptions::REQUEST_OPTIONS]) + ? $options[ClientOptions::REQUEST_OPTIONS] + : []; + + $this->client = new Client(array_merge($options, $requestOptions)); + } + + public function getClient(): ClientInterface + { + return $this->client; + } + + public function createRequest(string $method, string $uri): RequestInterface + { + return new Request($method, $uri); + } + + /** + * Constructs a \GuzzleHttp\Client to handle the HTTP requests initiated by the SDK. + * + * @param array $options options for the underlying \GuzzleHttp\Client + * + * @return FetchClientInterface the constructed fetch client that works with \GuzzleHttp\Client + */ + public static function create(array $options = []): FetchClientInterface + { + return new GuzzleFetchClient($options); + } +} diff --git a/src/Log/DefaultLogger.php b/src/Log/DefaultLogger.php new file mode 100644 index 0000000..facc05c --- /dev/null +++ b/src/Log/DefaultLogger.php @@ -0,0 +1,86 @@ +format('Y-m-d\\TH:i:sP'); + $context['level'] = LogLevel::asString($level); + + $final = '[{timestamp}] ConfigCat.{level}: '.$message; + + error_log(self::interpolate($final, $context)); + } + + /** + * @param mixed[] $context + */ + private static function interpolate(string|Stringable $message, array $context = []): string + { + $replace = []; + foreach ($context as $key => $val) { + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace['{'.$key.'}'] = $val; + } + } + + return strtr((string) $message, $replace); + } +} diff --git a/src/Log/InternalLogger.php b/src/Log/InternalLogger.php index 5d7e772..c391ca1 100644 --- a/src/Log/InternalLogger.php +++ b/src/Log/InternalLogger.php @@ -1,36 +1,37 @@ hooks->fireOnError(self::format($message, $context)); if ($this->shouldLog(LogLevel::EMERGENCY, $context)) { - $this->ensureEventId($context); - $this->logger->emergency($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->emergency($enriched, $context); } } @@ -38,8 +39,8 @@ public function alert($message, array $context = []): void { $this->hooks->fireOnError(self::format($message, $context)); if ($this->shouldLog(LogLevel::ALERT, $context)) { - $this->ensureEventId($context); - $this->logger->alert($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->alert($enriched, $context); } } @@ -47,8 +48,8 @@ public function critical($message, array $context = []): void { $this->hooks->fireOnError(self::format($message, $context)); if ($this->shouldLog(LogLevel::CRITICAL, $context)) { - $this->ensureEventId($context); - $this->logger->critical($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->critical($enriched, $context); } } @@ -56,40 +57,40 @@ public function error($message, array $context = []): void { $this->hooks->fireOnError(self::format($message, $context)); if ($this->shouldLog(LogLevel::ERROR, $context)) { - $this->ensureEventId($context); - $this->logger->error($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->error($enriched, $context); } } public function warning($message, array $context = []): void { if ($this->shouldLog(LogLevel::WARNING, $context)) { - $this->ensureEventId($context); - $this->logger->warning($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->warning($enriched, $context); } } public function notice($message, array $context = []): void { if ($this->shouldLog(LogLevel::NOTICE, $context)) { - $this->ensureEventId($context); - $this->logger->notice($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->notice($enriched, $context); } } public function info($message, array $context = []): void { if ($this->shouldLog(LogLevel::INFO, $context)) { - $this->ensureEventId($context); - $this->logger->info($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->info($enriched, $context); } } public function debug($message, array $context = []): void { if ($this->shouldLog(LogLevel::DEBUG, $context)) { - $this->ensureEventId($context); - $this->logger->debug($message, $context); + $enriched = $this->enrichMessage($message, $context); + $this->logger->debug($enriched, $context); } } @@ -98,51 +99,49 @@ public function log($level, $message, array $context = []): void // Do nothing, only the leveled methods should be used. } - private function shouldLog($currentLevel, array $context): bool + /** + * @param mixed[] $context + */ + public static function format(string|Stringable $message, array $context = []): string + { + if (array_key_exists('exception', $context)) { + $message = $message.PHP_EOL.$context['exception']->getMessage(); + } + + return (string) $message; + } + + /** + * @param mixed[] $context + */ + private function shouldLog(int $currentLevel, array $context): bool { return $currentLevel >= $this->globalLevel && !$this->hasAnythingToIgnore($context); } + /** + * @param mixed[] $context + */ private function hasAnythingToIgnore(array $context): bool { - if (empty($this->exceptionsToIgnore) || - empty($context) || - !isset($context['exception'])) { + if (empty($this->exceptionsToIgnore) + || empty($context) + || !isset($context['exception'])) { return false; } return in_array(get_class($context['exception']), $this->exceptionsToIgnore); } - private function ensureEventId(array &$context): void + /** + * @param mixed[] $context + */ + private function enrichMessage(string|Stringable $message, array &$context): string { if (!array_key_exists('event_id', $context)) { $context['event_id'] = 0; } - } - - public static function format(string $message, array $context): string - { - // Format PSR-3 log message by reusing PsrLogMessageProcessor's logic - // (see https://www.php-fig.org/psr/psr-3/#12-message). - static $psrProcessor = null; - if (is_null($psrProcessor)) { - $psrProcessor = new PsrLogMessageProcessor(); - } - - // Before v3.0, Monolog didn't have the LogRecord class but used a simple array. - if (class_exists('\Monolog\LogRecord')) { - $rec = new LogRecord(new DateTimeImmutable('@0'), "", Level::Notice, $message, $context); - $message = $psrProcessor->__invoke($rec)->message; - } else { - $rec = ['message' => $message, 'context' => $context]; - $message = $psrProcessor->__invoke($rec)['message']; - } - - if (array_key_exists('exception', $context)) { - $message = $message . PHP_EOL . $context['exception']->getMessage(); - } - return $message; + return self::format('[{event_id}] '.$message); } } diff --git a/src/Log/LogLevel.php b/src/Log/LogLevel.php index c4a1b38..bb93275 100644 --- a/src/Log/LogLevel.php +++ b/src/Log/LogLevel.php @@ -1,10 +1,11 @@ 'DEBUG', + self::INFO => 'INFO', + self::NOTICE => 'NOTICE', + self::WARNING => 'WARNING', + self::ERROR => 'ERROR', + self::CRITICAL => 'CRITICAL', + self::ALERT => 'ALERT', + self::EMERGENCY => 'EMERGENCY', + self::NO_LOG => 'NO_LOG', + default => '', + }; } } diff --git a/src/Override/ArrayDataSource.php b/src/Override/ArrayDataSource.php index f542219..edcb831 100644 --- a/src/Override/ArrayDataSource.php +++ b/src/Override/ArrayDataSource.php @@ -1,36 +1,37 @@ $overrides the array that contains the overrides */ - public function __construct(private readonly array $overrides) - { - } + public function __construct(private readonly array $overrides) {} /** * Gets the overrides. - * @return array The overrides. + * + * @return mixed[] the overrides */ public function getOverrides(): array { $result = []; foreach ($this->overrides as $key => $value) { $result[$key] = [ - SettingAttributes::VALUE => $value + SettingAttributes::VALUE => $value, ]; } + return $result; } } diff --git a/src/Override/FlagOverrides.php b/src/Override/FlagOverrides.php index 9c8a7ab..7bbf5f5 100644 --- a/src/Override/FlagOverrides.php +++ b/src/Override/FlagOverrides.php @@ -1,5 +1,7 @@ filePath); - if ($content === false) { - $this->logger->error("Cannot find the local config file '{FILE_PATH}'. ' . - 'This is a path that your application provided to the ConfigCat SDK by passing it to the `FlagOverrides.LocalFile()` method. ' . + if (false === $content) { + $this->logger->error("Cannot find the local config file '".$this->filePath."'. ' . + 'This is a path that your application provided to the ConfigCat SDK by passing it to the `FlagOverrides.LocalFile()` method. ' . 'Read more: https://configcat.com/docs/sdk-reference/php/#json-file", [ - 'event_id' => 1300, - 'FILE_PATH' => $this->filePath - ]); - return null; + 'event_id' => 1300, + ]); + + return []; } $json = json_decode($content, true); - if ($json == null) { - $this->logger->error("Failed to decode JSON from the local config file '{FILE_PATH}'. JSON error: {JSON_ERROR}", [ + if (null == $json) { + $this->logger->error("Failed to decode JSON from the local config file '".$this->filePath."'. JSON error: ".json_last_error_msg(), [ 'event_id' => 2302, - 'FILE_PATH' => $this->filePath, 'JSON_ERROR' => json_last_error_msg() ]); - return null; + + return []; } if (isset($json['flags'])) { $result = []; foreach ($json['flags'] as $key => $value) { $result[$key] = [ - SettingAttributes::VALUE => $value + SettingAttributes::VALUE => $value, ]; } + return $result; } + return $json[Config::ENTRIES]; } } diff --git a/src/Override/OverrideBehaviour.php b/src/Override/OverrideBehaviour.php index a3d6ec7..a94e094 100644 --- a/src/Override/OverrideBehaviour.php +++ b/src/Override/OverrideBehaviour.php @@ -1,10 +1,11 @@ $overrides the array that contains the overrides + * + * @return OverrideDataSource the constructed data source */ public static function localArray(array $overrides): OverrideDataSource { diff --git a/src/Pair.php b/src/Pair.php index 2da39ec..7adb225 100644 --- a/src/Pair.php +++ b/src/Pair.php @@ -1,27 +1,24 @@ (SemVer)", - ">= (SemVer)", - "= (Number)", - "<> (Number)", - "< (Number)", - "<= (Number)", - "> (Number)", - ">= (Number)", - "IS ONE OF (Sensitive)", - "IS NOT ONE OF (Sensitive)" + 'IS ONE OF', + 'IS NOT ONE OF', + 'CONTAINS', + 'DOES NOT CONTAIN', + 'IS ONE OF (SemVer)', + 'IS NOT ONE OF (SemVer)', + '< (SemVer)', + '<= (SemVer)', + '> (SemVer)', + '>= (SemVer)', + '= (Number)', + '<> (Number)', + '< (Number)', + '<= (Number)', + '> (Number)', + '>= (Number)', + 'IS ONE OF (Sensitive)', + 'IS NOT ONE OF (Sensitive)', ]; /** * RolloutEvaluator constructor. * - * @param InternalLogger $logger The logger instance. + * @param InternalLogger $logger the logger instance */ - public function __construct(private readonly InternalLogger $logger) - { - } + public function __construct(private readonly InternalLogger $logger) {} /** * Evaluates a requested value from the configuration by the specified roll-out rules. * - * @param string $key The key of the desired value. - * @param array $json The decoded JSON configuration. - * @param EvaluationLogCollector $logCollector The evaluation log collector. - * @param ?User $user Optional. The user to identify the caller. - * @return EvaluationResult The evaluation result. + * @param string $key the key of the desired value + * @param array $json the decoded JSON configuration + * @param EvaluationLogCollector $logCollector the evaluation log collector + * @param ?User $user Optional. The user to identify the caller. + * + * @return EvaluationResult the evaluation result */ public function evaluate( string $key, @@ -62,33 +66,33 @@ public function evaluate( EvaluationLogCollector $logCollector, ?User $user = null ): EvaluationResult { - if ($user === null) { - if (isset($json[SettingAttributes::ROLLOUT_RULES]) && - !empty($json[SettingAttributes::ROLLOUT_RULES]) || - isset($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS]) && - !empty($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS])) { - $this->logger->warning("Cannot evaluate targeting rules and % options for setting '{KEY}' (User Object is missing). " . - "You should pass a User Object to the evaluation methods like `getValue()` in order to make targeting work properly. " . - "Read more: https://configcat.com/docs/advanced/user-object/", [ + if (null === $user) { + if (isset($json[SettingAttributes::ROLLOUT_RULES]) + && !empty($json[SettingAttributes::ROLLOUT_RULES]) + || isset($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS]) + && !empty($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS])) { + $this->logger->warning("Cannot evaluate targeting rules and % options for setting '".$key."' (User Object is missing). ". + 'You should pass a User Object to the evaluation methods like `getValue()` in order to make targeting work properly. '. + 'Read more: https://configcat.com/docs/advanced/user-object/', [ 'event_id' => 3001, - 'KEY' => $key ]); } $result = $json[SettingAttributes::VALUE]; - $variationId = $json[SettingAttributes::VARIATION_ID] ?? ""; - $logCollector->add("Returning " . Utils::getStringRepresentation($result) . "."); + $variationId = $json[SettingAttributes::VARIATION_ID] ?? ''; + $logCollector->add('Returning '.Utils::getStringRepresentation($result).'.'); + return new EvaluationResult($result, $variationId, null, null); } - $logCollector->add("User object: " . $user); + $logCollector->add('User object: '.$user); if (isset($json[SettingAttributes::ROLLOUT_RULES]) && !empty($json[SettingAttributes::ROLLOUT_RULES])) { foreach ($json[SettingAttributes::ROLLOUT_RULES] as $rule) { $comparisonAttribute = $rule[RolloutAttributes::COMPARISON_ATTRIBUTE]; $comparisonValue = $rule[RolloutAttributes::COMPARISON_VALUE]; $comparator = $rule[RolloutAttributes::COMPARATOR]; $value = $rule[RolloutAttributes::VALUE]; - $variationId = $rule[RolloutAttributes::VARIATION_ID] ?? ""; + $variationId = $rule[RolloutAttributes::VARIATION_ID] ?? ''; $userValue = $user->getAttribute($comparisonAttribute); if (empty($comparisonValue) || (!is_numeric($userValue) && empty($userValue))) { @@ -98,11 +102,12 @@ public function evaluate( $comparator, $comparisonValue )); + continue; } switch ($comparator) { - //IS ONE OF + // IS ONE OF case 0: $split = array_filter(Utils::splitTrim($comparisonValue)); if (in_array($userValue, $split, true)) { @@ -113,10 +118,13 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } + break; - //IS NOT ONE OF + + // IS NOT ONE OF case 1: $split = array_filter(Utils::splitTrim($comparisonValue)); if (!in_array($userValue, $split, true)) { @@ -127,10 +135,13 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } + break; - //CONTAINS + + // CONTAINS case 2: if (Utils::strContains($userValue, $comparisonValue)) { $logCollector->add($this->logMatch( @@ -140,10 +151,13 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } + break; - //DOES NOT CONTAIN + + // DOES NOT CONTAIN case 3: if (!Utils::strContains($userValue, $comparisonValue)) { $logCollector->add($this->logMatch( @@ -153,20 +167,24 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } + break; - //IS ONE OF, IS NOT ONE OF (SemVer) + + // IS ONE OF, IS NOT ONE OF (SemVer) case 4: case 5: $split = array_filter(Utils::splitTrim($comparisonValue)); + try { $matched = false; foreach ($split as $semVer) { $matched = Version::equal($userValue, $semVer) || $matched; } - if (($matched && $comparator == 4) || (!$matched && $comparator == 5)) { + if (($matched && 4 == $comparator) || (!$matched && 5 == $comparator)) { $logCollector->add($this->logMatch( $comparisonAttribute, $userValue, @@ -174,6 +192,7 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } } catch (SemverException) { @@ -184,24 +203,26 @@ public function evaluate( $comparisonValue, $value )); + break; } break; - //LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (SemVer) + + // LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (SemVer) case 6: case 7: case 8: case 9: try { - if (($comparator == 6 && - Version::lessThan($userValue, $comparisonValue)) || - ($comparator == 7 && - Version::lessThanOrEqual($userValue, $comparisonValue)) || - ($comparator == 8 && - Version::greaterThan($userValue, $comparisonValue)) || - ($comparator == 9 && - Version::greaterThanOrEqual($userValue, $comparisonValue))) { + if ((6 == $comparator + && Version::lessThan($userValue, $comparisonValue)) + || (7 == $comparator + && Version::lessThanOrEqual($userValue, $comparisonValue)) + || (8 == $comparator + && Version::greaterThan($userValue, $comparisonValue)) + || (9 == $comparator + && Version::greaterThanOrEqual($userValue, $comparisonValue))) { $logCollector->add($this->logMatch( $comparisonAttribute, $userValue, @@ -209,6 +230,7 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } } catch (SemverException $exception) { @@ -219,26 +241,30 @@ public function evaluate( $comparisonValue, $exception )); + break; } + break; - //LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (Number) + + // LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (Number) case 10: case 11: case 12: case 13: case 14: case 15: - $userDouble = str_replace(",", ".", $userValue); - $comparisonDouble = str_replace(",", ".", $comparisonValue); + $userDouble = str_replace(',', '.', $userValue); + $comparisonDouble = str_replace(',', '.', $comparisonValue); if (!is_numeric($userDouble)) { $logCollector->add($this->logFormatErrorWithMessage( $comparisonAttribute, $userValue, $comparator, $comparisonValue, - $userDouble . "is not a valid number." + $userDouble.'is not a valid number.' )); + break; } @@ -248,20 +274,21 @@ public function evaluate( $userValue, $comparator, $comparisonValue, - $comparisonDouble . "is not a valid number." + $comparisonDouble.'is not a valid number.' )); + break; } $userDoubleValue = floatval($userDouble); $comparisonDoubleValue = floatval($comparisonDouble); - if (($comparator == 10 && $userDoubleValue == $comparisonDoubleValue) || - ($comparator == 11 && $userDoubleValue != $comparisonDoubleValue) || - ($comparator == 12 && $userDoubleValue < $comparisonDoubleValue) || - ($comparator == 13 && $userDoubleValue <= $comparisonDoubleValue) || - ($comparator == 14 && $userDoubleValue > $comparisonDoubleValue) || - ($comparator == 15 && $userDoubleValue >= $comparisonDoubleValue)) { + if ((10 == $comparator && $userDoubleValue == $comparisonDoubleValue) + || (11 == $comparator && $userDoubleValue != $comparisonDoubleValue) + || (12 == $comparator && $userDoubleValue < $comparisonDoubleValue) + || (13 == $comparator && $userDoubleValue <= $comparisonDoubleValue) + || (14 == $comparator && $userDoubleValue > $comparisonDoubleValue) + || (15 == $comparator && $userDoubleValue >= $comparisonDoubleValue)) { $logCollector->add($this->logMatch( $comparisonAttribute, $userValue, @@ -269,10 +296,13 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } + break; - //IS ONE OF (Sensitive) + + // IS ONE OF (Sensitive) case 16: $split = array_filter(Utils::splitTrim($comparisonValue)); if (in_array(sha1($userValue), $split, true)) { @@ -283,10 +313,13 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } + break; - //IS NOT ONE OF (Sensitive) + + // IS NOT ONE OF (Sensitive) case 17: $split = array_filter(Utils::splitTrim($comparisonValue)); if (!in_array(sha1($userValue), $split, true)) { @@ -297,17 +330,19 @@ public function evaluate( $comparisonValue, $value )); + return new EvaluationResult($value, $variationId, $rule, null); } + break; } $logCollector->add($this->logNoMatch($comparisonAttribute, $userValue, $comparator, $comparisonValue)); } } - if (isset($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS]) && - !empty($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS])) { - $hashCandidate = $key . $user->getIdentifier(); + if (isset($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS]) + && !empty($json[SettingAttributes::ROLLOUT_PERCENTAGE_ITEMS])) { + $hashCandidate = $key.$user->getIdentifier(); $stringHash = substr(sha1($hashCandidate), 0, 7); $intHash = intval($stringHash, 16); $scale = $intHash % 100; @@ -319,58 +354,71 @@ public function evaluate( $result = $rule[PercentageAttributes::VALUE]; $variationId = $rule[PercentageAttributes::VARIATION_ID]; $logCollector->add( - "Evaluating % options. Returning " . Utils::getStringRepresentation($result) . "." + 'Evaluating % options. Returning '.Utils::getStringRepresentation($result).'.' ); + return new EvaluationResult($result, $variationId, null, $rule); } } } $result = $json[SettingAttributes::VALUE]; - $variationId = $json[SettingAttributes::VARIATION_ID] ?? ""; - $logCollector->add("Returning " . Utils::getStringRepresentation($result) . "."); + $variationId = $json[SettingAttributes::VARIATION_ID] ?? ''; + $logCollector->add('Returning '.Utils::getStringRepresentation($result).'.'); + return new EvaluationResult($result, $variationId, null, null); } - private function logMatch($comparisonAttribute, $userValue, $comparator, $comparisonValue, $value): string - { - return "Evaluating rule: [" . $comparisonAttribute . ":" . $userValue . "] " . - "[" . $this->comparatorTexts[$comparator] . "] " . - "[" . $comparisonValue . "] => match, returning: " . Utils::getStringRepresentation($value) . "."; + private function logMatch( + string $comparisonAttribute, + string $userValue, + int $comparator, + string $comparisonValue, + mixed $value + ): string { + return 'Evaluating rule: ['.$comparisonAttribute.':'.$userValue.'] '. + '['.$this->comparatorTexts[$comparator].'] '. + '['.$comparisonValue.'] => match, returning: '.Utils::getStringRepresentation($value).'.'; } - private function logNoMatch($comparisonAttribute, $userValue, $comparator, $comparisonValue): string - { - return "Evaluating rule: [" . $comparisonAttribute . ":" . $userValue . "] " . - "[" . $this->comparatorTexts[$comparator] . "] " . - "[" . $comparisonValue . "] => no match."; + private function logNoMatch( + string $comparisonAttribute, + ?string $userValue, + int $comparator, + string $comparisonValue + ): string { + return 'Evaluating rule: ['.$comparisonAttribute.':'.$userValue.'] '. + '['.$this->comparatorTexts[$comparator].'] '. + '['.$comparisonValue.'] => no match.'; } private function logFormatError( - $comparisonAttribute, - $userValue, - $comparator, - $comparisonValue, + string $comparisonAttribute, + string $userValue, + int $comparator, + string $comparisonValue, Exception $exception ): string { - $message = "Evaluating rule: [" . $comparisonAttribute . ":" . $userValue . "] " . - "[" . $this->comparatorTexts[$comparator] . "] " . - "[" . $comparisonValue . "] => SKIP rule. Validation error: " . $exception->getMessage() . "."; + $message = 'Evaluating rule: ['.$comparisonAttribute.':'.$userValue.'] '. + '['.$this->comparatorTexts[$comparator].'] '. + '['.$comparisonValue.'] => SKIP rule. Validation error: '.$exception->getMessage().'.'; $this->logger->warning($message, ['exception' => $exception]); + return $message; } private function logFormatErrorWithMessage( - $comparisonAttribute, - $userValue, - $comparator, - $comparisonValue, - $message + string $comparisonAttribute, + string $userValue, + int $comparator, + string $comparisonValue, + string $message ): string { - $message = "Evaluating rule: [" . $comparisonAttribute . ":" . $userValue . "] " . - "[" . $this->comparatorTexts[$comparator] . "] " . - "[" . $comparisonValue . "] => SKIP rule. Validation error: " . $message . "."; + $message = 'Evaluating rule: ['.$comparisonAttribute.':'.$userValue.'] '. + '['.$this->comparatorTexts[$comparator].'] '. + '['.$comparisonValue.'] => SKIP rule. Validation error: '.$message.'.'; $this->logger->warning($message); + return $message; } } diff --git a/src/SettingsResult.php b/src/SettingsResult.php index daece65..f3cc273 100644 --- a/src/SettingsResult.php +++ b/src/SettingsResult.php @@ -1,5 +1,7 @@ + */ private array $attributes = []; /** * User constructor. * - * @param string $identifier The identifier of the user. - * @param string $email Optional. The email of the user. - * @param string $country Optional. The country attribute of the user. - * @param array $custom Custom user attributes. + * @param string $identifier the identifier of the user + * @param string $email Optional. The email of the user. + * @param string $country Optional. The country attribute of the user. + * @param array $custom custom user attributes */ public function __construct( string $identifier, - string $email = "", - string $country = "", + string $email = '', + string $country = '', array $custom = [] ) { $this->identifier = $this->attributes['Identifier'] = $identifier; @@ -40,10 +45,23 @@ public function __construct( } } + /** + * @return string the string representation of the user + */ + public function __toString(): string + { + $result = json_encode($this->attributes); + if (!$result) { + return ''; + } + + return $result; + } + /** * Gets the identifier of the user. * - * @return string The identifier of the user. + * @return string the identifier of the user */ public function getIdentifier(): string { @@ -53,19 +71,12 @@ public function getIdentifier(): string /** * Gets a user attribute identified by the given key. * - * @param string $key The key of the user attribute. - * @return string|null The user attribute, or null if it doesn't exist. + * @param string $key the key of the user attribute + * + * @return null|string the user attribute, or null if it doesn't exist */ public function getAttribute(string $key): ?string { return array_key_exists($key, $this->attributes) ? $this->attributes[$key] : null; } - - /** - * @return string The string representation of the user. - */ - public function __toString(): string - { - return json_encode($this->attributes); - } } diff --git a/src/Utils.php b/src/Utils.php index 4be70ea..c508854 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -1,10 +1,12 @@ format('Uv'); - $etag = "test-etag"; + $time = (float) $dateTime->format('Uv'); + $etag = 'test-etag'; - $expectedPayload = "1686756435844\ntest-etag\n" . $testJson; + $expectedPayload = "1686756435844\ntest-etag\n".$testJson; $entry = ConfigEntry::fromConfigJson($testJson, $etag, $time); @@ -41,26 +42,33 @@ public function testCachePayload() /** * @dataProvider cacheKeyTestData + * + * @param mixed $sdkKey + * @param mixed $cacheKey */ public function testCacheKeyGeneration($sdkKey, $cacheKey) { $cache = $this->getMockBuilder(ConfigCache::class)->getMock(); $client = new ConfigCatClient($sdkKey, [ - ClientOptions::CACHE => $cache, - ClientOptions::CUSTOM_HANDLER => new MockHandler( + ClientOptions::CACHE => $cache, + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler( [new Response(200, [], self::TEST_JSON)] ), - ]); + ]), + ]); $cache ->expects($this->once()) ->method('store') - ->with($this->equalTo($cacheKey)); + ->with($this->equalTo($cacheKey)) + ; $cache ->expects($this->once()) ->method('load') - ->with($this->equalTo($cacheKey)); + ->with($this->equalTo($cacheKey)) + ; $client->forceRefresh(); } @@ -68,8 +76,8 @@ public function testCacheKeyGeneration($sdkKey, $cacheKey) public function cacheKeyTestData(): array { return [ - ["test1", "147c5b4c2b2d7c77e1605b1a4309f0ea6684a0c6"], - ["test2", "c09513b1756de9e4bc48815ec7a142b2441ed4d5"], + ['test1', '147c5b4c2b2d7c77e1605b1a4309f0ea6684a0c6'], + ['test2', 'c09513b1756de9e4bc48815ec7a142b2441ed4d5'], ]; } } diff --git a/tests/ConfigCatClientTest.php b/tests/ConfigCatClientTest.php index d79626b..a1207d8 100644 --- a/tests/ConfigCatClientTest.php +++ b/tests/ConfigCatClientTest.php @@ -8,6 +8,7 @@ use ConfigCat\ClientOptions; use ConfigCat\ConfigCatClient; use ConfigCat\EvaluationDetails; +use ConfigCat\Http\GuzzleFetchClient; use ConfigCat\Log\InternalLogger; use ConfigCat\Log\LogLevel; use ConfigCat\User; @@ -19,16 +20,17 @@ use GuzzleHttp\Psr7\Response; use InvalidArgumentException; use PHPUnit\Framework\TestCase; +use ReflectionClass; use ReflectionException; class ConfigCatClientTest extends TestCase { - private const TEST_JSON = "{ \"f\" : { \"first\": { \"v\": false, \"p\": [], \"r\": [], \"i\":\"fakeIdFirst\" }, \"second\": { \"v\": true, \"p\": [], \"r\": [], \"i\":\"fakeIdSecond\" }}}"; + private const TEST_JSON = '{ "f" : { "first": { "v": false, "p": [], "r": [], "i":"fakeIdFirst" }, "second": { "v": true, "p": [], "r": [], "i":"fakeIdSecond" }}}'; public function testConstructEmptySdkKey() { $this->expectException(InvalidArgumentException::class); - new ConfigCatClient(""); + new ConfigCatClient(''); } /** @@ -36,15 +38,15 @@ public function testConstructEmptySdkKey() */ public function testConstructDefaults() { - $client = new ConfigCatClient("testConstructDefaults"); + $client = new ConfigCatClient('testConstructDefaults'); - $logger = $this->getReflectedValue($client, "logger"); + $logger = $this->getReflectedValue($client, 'logger'); $this->assertInstanceOf(InternalLogger::class, $logger); - $cache = $this->getReflectedValue($client, "cache"); + $cache = $this->getReflectedValue($client, 'cache'); $this->assertInstanceOf(ArrayCache::class, $cache); - $cacheRefreshInterval = $this->getReflectedValue($client, "cacheRefreshInterval"); + $cacheRefreshInterval = $this->getReflectedValue($client, 'cacheRefreshInterval'); $this->assertEquals(60, $cacheRefreshInterval); } @@ -54,49 +56,61 @@ public function testConstructDefaults() public function testConstructLoggerOption() { $logger = Utils::getNullLogger(); - $client = new ConfigCatClient("testConstructLoggerOption", [ + $client = new ConfigCatClient('testConstructLoggerOption', [ ClientOptions::LOGGER => $logger, ClientOptions::LOG_LEVEL => LogLevel::ERROR, - ClientOptions::EXCEPTIONS_TO_IGNORE => [InvalidArgumentException::class] + ClientOptions::EXCEPTIONS_TO_IGNORE => [InvalidArgumentException::class], ]); - $internalLogger = $this->getReflectedValue($client, "logger"); + $internalLogger = $this->getReflectedValue($client, 'logger'); - $externalLogger = $this->getReflectedValue($internalLogger, "logger"); - $globalLevel = $this->getReflectedValue($internalLogger, "globalLevel"); - $exceptions = $this->getReflectedValue($internalLogger, "exceptionsToIgnore"); + $externalLogger = $this->getReflectedValue($internalLogger, 'logger'); + $globalLevel = $this->getReflectedValue($internalLogger, 'globalLevel'); + $exceptions = $this->getReflectedValue($internalLogger, 'exceptionsToIgnore'); $this->assertSame($logger, $externalLogger); $this->assertSame(LogLevel::ERROR, $globalLevel); - $this->assertArraySubset([InvalidArgumentException::class], $exceptions); + $this->assertTrue(in_array(InvalidArgumentException::class, $exceptions)); } + /** + * @throws ReflectionException + */ public function testConstructCacheOption() { $cache = new ArrayCache(); - $client = new ConfigCatClient("testConstructCacheOption", [ClientOptions::CACHE => $cache]); - $this->assertAttributeSame($cache, "cache", $client); + $client = new ConfigCatClient('testConstructCacheOption', [ClientOptions::CACHE => $cache]); + $propCache = $this->getReflectedValue($client, 'cache'); + $this->assertSame($cache, $propCache); } + /** + * @throws ReflectionException + */ public function testConstructCacheRefreshIntervalOption() { - $client = new ConfigCatClient("testConstructCacheRefreshIntervalOption", [ClientOptions::CACHE_REFRESH_INTERVAL => 20]); - $this->assertAttributeSame(20, "cacheRefreshInterval", $client); + $client = new ConfigCatClient('testConstructCacheRefreshIntervalOption', [ClientOptions::CACHE_REFRESH_INTERVAL => 20]); + $propInterval = $this->getReflectedValue($client, 'cacheRefreshInterval'); + $this->assertSame(20, $propInterval); } public function testGetValueFailedFetch() { - $client = new ConfigCatClient("testGetValueFailedFetch", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(400) + $client = new ConfigCatClient('testGetValueFailedFetch', [ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler( + [new Response(400)] + ), ])]); - $value = $client->getValue("key", false); + $value = $client->getValue('key', false); $this->assertFalse($value); } public function testGetAllKeysFailedFetch() { - $client = new ConfigCatClient("testGetAllKeysFailedFetch", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(400) + $client = new ConfigCatClient('testGetAllKeysFailedFetch', [ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler( + [new Response(400)] + ), ])]); $keys = $client->getAllKeys(); @@ -106,20 +120,23 @@ public function testGetAllKeysFailedFetch() public function testForceRefresh() { $cache = $this->getMockBuilder(ConfigCache::class)->getMock(); - $client = new ConfigCatClient("PKDVCLf-Hq-h-kCzMp-L7Q/PaDVCFk9EpmD6sLpGLltTA", - [ClientOptions::CACHE => $cache]); + $client = new ConfigCatClient( + 'PKDVCLf-Hq-h-kCzMp-L7Q/PaDVCFk9EpmD6sLpGLltTA', + [ClientOptions::CACHE => $cache] + ); $cache ->expects(self::once()) - ->method("store"); + ->method('store') + ; $client->forceRefresh(); } public function testKeyNotExist() { - $client = new ConfigCatClient("PKDVCLf-Hq-h-kCzMp-L7Q/PaDVCFk9EpmD6sLpGLltTA"); - $value = $client->getValue("nonExistingKey", false); + $client = new ConfigCatClient('PKDVCLf-Hq-h-kCzMp-L7Q/PaDVCFk9EpmD6sLpGLltTA'); + $value = $client->getValue('nonExistingKey', false); $this->assertFalse($value); } @@ -130,24 +147,27 @@ public function testCacheExpiration() $mockHandler = new MockHandler( [new Response(200, [], self::TEST_JSON)] ); - $client = new ConfigCatClient("testCacheExpiration", [ + $client = new ConfigCatClient('testCacheExpiration', [ ClientOptions::CACHE => $cache, - ClientOptions::CUSTOM_HANDLER => $mockHandler, - ClientOptions::CACHE_REFRESH_INTERVAL => 1 + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => $mockHandler, + ]), + ClientOptions::CACHE_REFRESH_INTERVAL => 1, ]); $cache ->method('load') - ->willReturn(ConfigEntry::fromConfigJson(self::TEST_JSON, "", \ConfigCat\Utils::getUnixMilliseconds() - 500)); + ->willReturn(ConfigEntry::fromConfigJson(self::TEST_JSON, '', \ConfigCat\Utils::getUnixMilliseconds() - 500)) + ; - $value = $client->getValue("second", false); + $value = $client->getValue('second', false); $this->assertTrue($value); $this->assertNull($mockHandler->getLastRequest()); sleep(1); - $value = $client->getValue("second", false); + $value = $client->getValue('second', false); $this->assertTrue($value); $this->assertNotNull($mockHandler->getLastRequest()); @@ -155,32 +175,32 @@ public function testCacheExpiration() public function testGetVariationId() { - $client = new ConfigCatClient("testGetVariationId", [ - ClientOptions::CUSTOM_HANDLER => new MockHandler( - [new Response(200, [], self::TEST_JSON)] - ), + $client = new ConfigCatClient('testGetVariationId', [ + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler([new Response(200, [], self::TEST_JSON)]), + ]), ]); - $details = $client->getValueDetails("second", false); + $details = $client->getValueDetails('second', false); - $this->assertEquals("fakeIdSecond", $details->getVariationId()); + $this->assertEquals('fakeIdSecond', $details->getVariationId()); } public function testGetAllVariationIds() { - $client = new ConfigCatClient("testGetAllVariationIds", [ - ClientOptions::CUSTOM_HANDLER => new MockHandler( - [new Response(200, [], self::TEST_JSON)] - ), + $client = new ConfigCatClient('testGetAllVariationIds', [ + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler([new Response(200, [], self::TEST_JSON)]), + ]), ]); $value = $client->getAllValueDetails(); - $this->assertEquals(2, count($value)); + $this->assertCount(2, $value); } public function testGetAllVariationIdsEmpty() { - $client = new ConfigCatClient("testGetAllVariationIdsEmpty", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(400) + $client = new ConfigCatClient('testGetAllVariationIdsEmpty', [ClientOptions::CUSTOM_HANDLER => new MockHandler([ + new Response(400), ])]); $value = $client->getAllValueDetails(); @@ -189,121 +209,122 @@ public function testGetAllVariationIdsEmpty() public function testGetKeyAndValue() { - $client = new ConfigCatClient("testGetKeyAndValue", [ - ClientOptions::CUSTOM_HANDLER => new MockHandler( - [new Response(200, [], self::TEST_JSON)] - ), + $client = new ConfigCatClient('testGetKeyAndValue', [ + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler([new Response(200, [], self::TEST_JSON)]), + ]), ]); - $value = $client->getKeyAndValue("fakeIdSecond"); + $value = $client->getKeyAndValue('fakeIdSecond'); - $this->assertEquals("second", $value->getKey()); + $this->assertEquals('second', $value->getKey()); $this->assertTrue($value->getValue()); } public function testGetKeyAndValueNull() { - $client = new ConfigCatClient("testGetKeyAndValueNull", [ - ClientOptions::CUSTOM_HANDLER => new MockHandler( - [new Response(200, [], self::TEST_JSON)] - ), + $client = new ConfigCatClient('testGetKeyAndValueNull', [ + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler([new Response(200, [], self::TEST_JSON)]), + ]), ]); - $value = $client->getKeyAndValue("nonexisting"); + $value = $client->getKeyAndValue('nonexisting'); $this->assertNull($value); } public function testGetAllValues() { - $client = new ConfigCatClient("testGetAllValues", [ - ClientOptions::CUSTOM_HANDLER => new MockHandler( - [new Response(200, [], self::TEST_JSON)] - ), + $client = new ConfigCatClient('testGetAllValues', [ + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler([new Response(200, [], self::TEST_JSON)]), + ]), ]); $value = $client->getAllValues(); - $this->assertEquals(["first" => false, "second" => true], $value); + $this->assertEquals(['first' => false, 'second' => true], $value); } public function testGetAllValueDetails() { - $client = new ConfigCatClient("testGetAllValueDetails", [ - ClientOptions::CUSTOM_HANDLER => new MockHandler( - [new Response(200, [], self::TEST_JSON)] - ), + $client = new ConfigCatClient('testGetAllValueDetails', [ + ClientOptions::FETCH_CLIENT => GuzzleFetchClient::create([ + 'handler' => new MockHandler([new Response(200, [], self::TEST_JSON)]), + ]), ]); $value = $client->getAllValueDetails(); - $this->assertFalse($value["first"]->getValue()); - $this->assertTrue($value["second"]->getValue()); + $this->assertFalse($value['first']->getValue()); + $this->assertTrue($value['second']->getValue()); } public function testDefaultUser() { - $client = new ConfigCatClient("testDefaultUser", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(200, [], Utils::formatConfigWithRules()) + $client = new ConfigCatClient('testDefaultUser', [ClientOptions::CUSTOM_HANDLER => new MockHandler([ + new Response(200, [], Utils::formatConfigWithRules()), ])]); - $user1 = new User("test@test1.com"); - $user2 = new User("test@test2.com"); + $user1 = new User('test@test1.com'); + $user2 = new User('test@test2.com'); $client->setDefaultUser($user1); - $value = $client->getValue("key", ""); - $this->assertEquals("fake1", $value); + $value = $client->getValue('key', ''); + $this->assertEquals('fake1', $value); - $value = $client->getValue("key", "", $user2); - $this->assertEquals("fake2", $value); + $value = $client->getValue('key', '', $user2); + $this->assertEquals('fake2', $value); $client->clearDefaultUser(); - $value = $client->getValue("key", ""); - $this->assertEquals("def", $value); + $value = $client->getValue('key', ''); + $this->assertEquals('def', $value); } public function testInitDefaultUser() { - $client = new ConfigCatClient("testInitDefaultUser", + $client = new ConfigCatClient( + 'testInitDefaultUser', [ ClientOptions::CUSTOM_HANDLER => new MockHandler([new Response(200, [], Utils::formatConfigWithRules())]), - ClientOptions::DEFAULT_USER => new User("test@test1.com") + ClientOptions::DEFAULT_USER => new User('test@test1.com'), ] ); - $user2 = new User("test@test2.com"); + $user2 = new User('test@test2.com'); - $value = $client->getValue("key", ""); - $this->assertEquals("fake1", $value); + $value = $client->getValue('key', ''); + $this->assertEquals('fake1', $value); - $value = $client->getValue("key", "", $user2); - $this->assertEquals("fake2", $value); + $value = $client->getValue('key', '', $user2); + $this->assertEquals('fake2', $value); $client->clearDefaultUser(); - $value = $client->getValue("key", ""); - $this->assertEquals("def", $value); + $value = $client->getValue('key', ''); + $this->assertEquals('def', $value); } public function testDefaultUserVariationId() { - $client = new ConfigCatClient("testDefaultUserVariationId", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(200, [], Utils::formatConfigWithRules()) + $client = new ConfigCatClient('testDefaultUserVariationId', [ClientOptions::CUSTOM_HANDLER => new MockHandler([ + new Response(200, [], Utils::formatConfigWithRules()), ])]); - $user1 = new User("test@test1.com"); - $user2 = new User("test@test2.com"); + $user1 = new User('test@test1.com'); + $user2 = new User('test@test2.com'); $client->setDefaultUser($user1); - $value = $client->getValueDetails("key", ""); - $this->assertEquals("id1", $value->getVariationId()); + $value = $client->getValueDetails('key', ''); + $this->assertEquals('id1', $value->getVariationId()); - $value = $client->getValueDetails("key", "", $user2); - $this->assertEquals("id2", $value->getVariationId()); + $value = $client->getValueDetails('key', '', $user2); + $this->assertEquals('id2', $value->getVariationId()); $client->clearDefaultUser(); - $value = $client->getValueDetails("key", ""); - $this->assertEquals("defVar", $value->getVariationId()); + $value = $client->getValueDetails('key', ''); + $this->assertEquals('defVar', $value->getVariationId()); } public function testOfflineOnline() @@ -316,7 +337,7 @@ public function testOfflineOnline() ] ); - $client = new ConfigCatClient("testOfflineOnline", [ + $client = new ConfigCatClient('testOfflineOnline', [ ClientOptions::CUSTOM_HANDLER => $handler, ]); @@ -349,9 +370,9 @@ public function testInitOfflineOnline() ] ); - $client = new ConfigCatClient("testInitOfflineOnline", [ + $client = new ConfigCatClient('testInitOfflineOnline', [ ClientOptions::CUSTOM_HANDLER => $handler, - ClientOptions::OFFLINE => true + ClientOptions::OFFLINE => true, ]); $this->assertTrue($client->isOffline()); @@ -370,11 +391,11 @@ public function testInitOfflineOnline() public function testHooks() { - $client = new ConfigCatClient("getTestClientWithError", [ + $client = new ConfigCatClient('getTestClientWithError', [ ClientOptions::CUSTOM_HANDLER => new MockHandler( [ new Response(200, [], self::TEST_JSON), - new Response(400, [], "") + new Response(400, [], ''), ] ), ]); @@ -382,65 +403,64 @@ public function testHooks() $evaluated = false; $error = false; $changed = false; - $message = ""; - $client->hooks()->addOnFlagEvaluated(function($details) use (&$evaluated) { + $message = ''; + $client->hooks()->addOnFlagEvaluated(function ($details) use (&$evaluated) { $evaluated = true; }); - $client->hooks()->addOnConfigChanged(function($settings) use (&$changed) { + $client->hooks()->addOnConfigChanged(function ($settings) use (&$changed) { $changed = true; }); - $client->hooks()->addOnError(function($err) use (&$error, &$message) { + $client->hooks()->addOnError(function ($err) use (&$error, &$message) { $error = true; $message = $err; }); - $client->getValue("first", false); + $client->getValue('first', false); $result = $client->forceRefresh(); - $this->assertTrue($evaluated); $this->assertTrue($error); - $this->assertEquals("Your SDK Key seems to be wrong. You can find the valid SDK Key at https://app.configcat.com/sdkkey. Received unexpected response: 400", $message); + $this->assertEquals('Your SDK Key seems to be wrong. You can find the valid SDK Key at https://app.configcat.com/sdkkey. Received unexpected response: 400', $message); $this->assertTrue($changed); $this->assertFalse($result->isSuccess()); - $this->assertEquals("Your SDK Key seems to be wrong. You can find the valid SDK Key at https://app.configcat.com/sdkkey. Received unexpected response: 400", $result->getError()); + $this->assertEquals('Your SDK Key seems to be wrong. You can find the valid SDK Key at https://app.configcat.com/sdkkey. Received unexpected response: 400', $result->getError()); } public function testEvalDetails() { - $client = new ConfigCatClient("testEvalDetails", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(200, [], Utils::formatConfigWithRules()) + $client = new ConfigCatClient('testEvalDetails', [ClientOptions::CUSTOM_HANDLER => new MockHandler([ + new Response(200, [], Utils::formatConfigWithRules()), ])]); - $details = $client->getValueDetails("key", "", new User("test@test1.com")); + $details = $client->getValueDetails('key', '', new User('test@test1.com')); - $this->assertEquals("fake1", $details->getValue()); - $this->assertEquals("id1", $details->getVariationId()); + $this->assertEquals('fake1', $details->getValue()); + $this->assertEquals('id1', $details->getVariationId()); $this->assertNull($details->getError()); - $this->assertEquals("key", $details->getKey()); - $this->assertEquals("test@test1.com", $details->getUser()->getIdentifier()); - $this->assertEquals("Identifier", $details->getMatchedEvaluationRule()["a"]); - $this->assertEquals("@test1.com", $details->getMatchedEvaluationRule()["c"]); - $this->assertEquals(2, $details->getMatchedEvaluationRule()["t"]); + $this->assertEquals('key', $details->getKey()); + $this->assertEquals('test@test1.com', $details->getUser()->getIdentifier()); + $this->assertEquals('Identifier', $details->getMatchedEvaluationRule()['a']); + $this->assertEquals('@test1.com', $details->getMatchedEvaluationRule()['c']); + $this->assertEquals(2, $details->getMatchedEvaluationRule()['t']); $this->assertNull($details->getMatchedEvaluationPercentageRule()); $this->assertTrue($details->getFetchTimeUnixMilliseconds() > 0); $this->assertFalse($details->isDefaultValue()); } - public function testEvalDetails_Non_Existent_Flag() + public function testEvalDetailsNonExistentFlag() { - $client = new ConfigCatClient("testEvalDetails", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(200, [], Utils::formatConfigWithRules()) + $client = new ConfigCatClient('testEvalDetails', [ClientOptions::CUSTOM_HANDLER => new MockHandler([ + new Response(200, [], Utils::formatConfigWithRules()), ])]); - $details = $client->getValueDetails("non-existent", "", new User("test@test1.com")); + $details = $client->getValueDetails('non-existent', '', new User('test@test1.com')); - $this->assertEquals("", $details->getValue()); - $this->assertEquals("", $details->getVariationId()); + $this->assertEquals('', $details->getValue()); + $this->assertEquals('', $details->getVariationId()); $this->assertNotNull($details->getError()); - $this->assertEquals("non-existent", $details->getKey()); - $this->assertEquals("test@test1.com", $details->getUser()->getIdentifier()); + $this->assertEquals('non-existent', $details->getKey()); + $this->assertEquals('test@test1.com', $details->getUser()->getIdentifier()); $this->assertNull($details->getMatchedEvaluationRule()); $this->assertNull($details->getMatchedEvaluationPercentageRule()); $this->assertTrue($details->isDefaultValue()); @@ -448,75 +468,79 @@ public function testEvalDetails_Non_Existent_Flag() public function testEvalDetailsHook() { - $client = new ConfigCatClient("testEvalDetailsHook", [ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Response(200, [], Utils::formatConfigWithRules()) + $client = new ConfigCatClient('testEvalDetailsHook', [ClientOptions::CUSTOM_HANDLER => new MockHandler([ + new Response(200, [], Utils::formatConfigWithRules()), ])]); $called = false; $client->hooks()->addOnFlagEvaluated(function (EvaluationDetails $details) use (&$called) { - $this->assertEquals("fake1", $details->getValue()); - $this->assertEquals("id1", $details->getVariationId()); + $this->assertEquals('fake1', $details->getValue()); + $this->assertEquals('id1', $details->getVariationId()); $this->assertNull($details->getError()); - $this->assertEquals("key", $details->getKey()); - $this->assertEquals("test@test1.com", $details->getUser()->getIdentifier()); - $this->assertEquals("Identifier", $details->getMatchedEvaluationRule()["a"]); - $this->assertEquals("@test1.com", $details->getMatchedEvaluationRule()["c"]); - $this->assertEquals(2, $details->getMatchedEvaluationRule()["t"]); + $this->assertEquals('key', $details->getKey()); + $this->assertEquals('test@test1.com', $details->getUser()->getIdentifier()); + $this->assertEquals('Identifier', $details->getMatchedEvaluationRule()['a']); + $this->assertEquals('@test1.com', $details->getMatchedEvaluationRule()['c']); + $this->assertEquals(2, $details->getMatchedEvaluationRule()['t']); $this->assertNull($details->getMatchedEvaluationPercentageRule()); $this->assertFalse($details->isDefaultValue()); $this->assertTrue($details->getFetchTimeUnixMilliseconds() > 0); $called = true; }); - $client->getValue("key", "", new User("test@test1.com")); + $client->getValue('key', '', new User('test@test1.com')); $this->assertTrue($called); } public function testTimeout() { - $client = new ConfigCatClient("testTimout", [ + $client = new ConfigCatClient('testTimout', [ ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new ConnectException("timeout", new Request("GET", "test")) + new ConnectException('timeout', new Request('GET', 'test')), ]), ]); - $value = $client->getValue("test", "def"); + $value = $client->getValue('test', 'def'); - $this->assertEquals("def", $value); + $this->assertEquals('def', $value); } public function testHttpException() { - $client = new ConfigCatClient("testHttpException", [ + $client = new ConfigCatClient('testHttpException', [ ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new RequestException("failed", new Request("GET", "test")) + new RequestException('failed', new Request('GET', 'test')), ]), ]); - $value = $client->getValue("test", "def"); + $value = $client->getValue('test', 'def'); - $this->assertEquals("def", $value); + $this->assertEquals('def', $value); } public function testGeneralException() { - $client = new ConfigCatClient("testGeneralException", [ + $client = new ConfigCatClient('testGeneralException', [ ClientOptions::CUSTOM_HANDLER => new MockHandler([ - new Exception("failed") + new Exception('failed'), ]), ]); - $value = $client->getValue("test", "def"); + $value = $client->getValue('test', 'def'); - $this->assertEquals("def", $value); + $this->assertEquals('def', $value); } /** + * @param mixed $object + * @param mixed $propertyName + * * @throws ReflectionException */ private function getReflectedValue($object, $propertyName) { - $reflection = new \ReflectionClass($object); + $reflection = new ReflectionClass($object); $property = $reflection->getProperty($propertyName); $property->setAccessible(true); + return $property->getValue($object); } } diff --git a/tests/ConfigFetcherTest.php b/tests/ConfigFetcherTest.php index 9f5346e..835fd29 100644 --- a/tests/ConfigFetcherTest.php +++ b/tests/ConfigFetcherTest.php @@ -9,36 +9,37 @@ use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Response; -use GuzzleHttp\RequestOptions; use InvalidArgumentException; use PHPUnit\Framework\TestCase; class ConfigFetcherTest extends TestCase { - private string $mockSdkKey = "testSdkKey"; - private string $mockEtag = "testEtag"; - private string $mockBody = "{\"key\": \"value\"}"; + private string $mockSdkKey = 'testSdkKey'; + private string $mockEtag = 'testEtag'; + private string $mockBody = '{"key": "value"}'; public function testFetchOk() { - $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([ - new Response(200, [ConfigFetcher::ETAG_HEADER => $this->mockEtag], $this->mockBody) - ]))]); + $mockHandler = new MockHandler([ + new Response(200, [ConfigFetcher::ETAG_HEADER => $this->mockEtag], $this->mockBody), ]); + $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ + ClientOptions::CUSTOM_HANDLER => HandlerStack::create($mockHandler), ]); - $response = $fetcher->fetch("old_etag"); + $response = $fetcher->fetch('old_etag'); $this->assertTrue($response->isFetched()); $this->assertEquals($this->mockEtag, $response->getConfigEntry()->getEtag()); - $this->assertEquals("value", $response->getConfigEntry()->getConfig()['key']); + $this->assertEquals('value', $response->getConfigEntry()->getConfig()['key']); + $this->assertNotEmpty($mockHandler->getLastRequest()->getHeader('X-ConfigCat-UserAgent')); } public function testFetchNotModified() { - $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([ - new Response(304, [ConfigFetcher::ETAG_HEADER => $this->mockEtag]) - ]))]); + $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ + ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([ + new Response(304, [ConfigFetcher::ETAG_HEADER => $this->mockEtag]), ])), ]); - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); $this->assertTrue($response->isNotModified()); $this->assertEmpty($response->getConfigEntry()->getETag()); @@ -47,11 +48,10 @@ public function testFetchNotModified() public function testFetchFailed() { - $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([ - new Response(400) - ]))]); + $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ + ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([new Response(400)])), ]); - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); $this->assertTrue($response->isFailed()); $this->assertEmpty($response->getConfigEntry()->getETag()); @@ -60,64 +60,37 @@ public function testFetchFailed() public function testFetchInvalidJson() { - $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([ - new Response(200, [], "{\"key\": value}") - ]))]); + $mockHandler = new MockHandler([new Response(200, [], '{"key": value}')]); + $fetcher = new ConfigFetcher($this->mockSdkKey, Utils::getTestLogger(), [ + ClientOptions::CUSTOM_HANDLER => HandlerStack::create($mockHandler), ]); - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); $this->assertTrue($response->isFailed()); $this->assertEmpty($response->getConfigEntry()->getETag()); $this->assertEmpty($response->getConfigEntry()->getConfig()); + $this->assertNotEmpty($mockHandler->getLastRequest()->getHeader('X-ConfigCat-UserAgent')); } public function testConstructEmptySdkKey() { $this->expectException(InvalidArgumentException::class); - new ConfigFetcher("", Utils::getNullLogger()); - } - - public function testConstructDefaults() - { - $fetcher = new ConfigFetcher("api", Utils::getTestLogger()); - $options = $fetcher->getRequestOptions(); - - $this->assertEquals(10, $options[RequestOptions::CONNECT_TIMEOUT]); - $this->assertEquals(30, $options[RequestOptions::TIMEOUT]); - $this->assertArrayHasKey("headers", $options); - } - - public function testConstructConnectTimeoutOption() - { - $fetcher = new ConfigFetcher("api", Utils::getTestLogger(), [ClientOptions::REQUEST_OPTIONS => [ - RequestOptions::CONNECT_TIMEOUT => 5 - ]]); - $options = $fetcher->getRequestOptions(); - $this->assertEquals(5, $options[RequestOptions::CONNECT_TIMEOUT]); - } - - public function testConstructRequestTimeoutOption() - { - $fetcher = new ConfigFetcher("api", Utils::getTestLogger(), [ClientOptions::REQUEST_OPTIONS => [ - RequestOptions::TIMEOUT => 5 - ]]); - $options = $fetcher->getRequestOptions(); - $this->assertEquals(5, $options[RequestOptions::TIMEOUT]); + new ConfigFetcher('', Utils::getNullLogger()); } public function testTimeoutException() { - $fetcher = new ConfigFetcher("api", Utils::getTestLogger(), [ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([ - new ConnectException("timeout", new Request("GET", "test")) + $fetcher = new ConfigFetcher('api', Utils::getTestLogger(), [ClientOptions::CUSTOM_HANDLER => HandlerStack::create(new MockHandler([ + new ConnectException('timeout', new Request('GET', 'test')), ]))]); - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); $this->assertTrue($response->isFailed()); } public function testIntegration() { - $fetcher = new ConfigFetcher("PKDVCLf-Hq-h-kCzMp-L7Q/PaDVCFk9EpmD6sLpGLltTA", Utils::getTestLogger()); - $response = $fetcher->fetch(""); + $fetcher = new ConfigFetcher('PKDVCLf-Hq-h-kCzMp-L7Q/PaDVCFk9EpmD6sLpGLltTA', Utils::getTestLogger()); + $response = $fetcher->fetch(''); $this->assertTrue($response->isFetched()); diff --git a/tests/DataGovernanceTest.php b/tests/DataGovernanceTest.php index d94f38b..f6b3635 100644 --- a/tests/DataGovernanceTest.php +++ b/tests/DataGovernanceTest.php @@ -12,70 +12,74 @@ class DataGovernanceTest extends TestCase { - const JSON_TEMPLATE = "{ \"p\": { \"u\": \"%s\", \"r\": %d }, \"f\": {} }"; - const CUSTOM_CDN_URL = "https://custom-cdn.configcat.com"; + const JSON_TEMPLATE = '{ "p": { "u": "%s", "r": %d }, "f": {} }'; + const CUSTOM_CDN_URL = 'https://custom-cdn.configcat.com'; - public function testShouldStayOnServer() { + public function testShouldStayOnServer() + { // Arrange $requests = []; - $body = sprintf(self::JSON_TEMPLATE, "https://fakeUrl", 0); + $body = sprintf(self::JSON_TEMPLATE, 'https://fakeUrl', 0); $responses = [ - new Response(200, [], $body) + new Response(200, [], $body), ]; $handler = $this->getHandlerStack($responses, $requests); $fetcher = $this->getFetcher($handler); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(1, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); $this->assertEquals(json_decode($body, true), $response->getConfigEntry()->getConfig()); } - public function testShouldStayOnSameUrlWithRedirect() { + public function testShouldStayOnSameUrlWithRedirect() + { // Arrange $requests = []; $body = sprintf(self::JSON_TEMPLATE, ConfigFetcher::GLOBAL_URL, 1); $responses = [ - new Response(200, [], $body) + new Response(200, [], $body), ]; $handler = $this->getHandlerStack($responses, $requests); $fetcher = $this->getFetcher($handler); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(1, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); $this->assertEquals(json_decode($body, true), $response->getConfigEntry()->getConfig()); } - public function testShouldStayOnSameUrlEvenWhenForced() { + public function testShouldStayOnSameUrlEvenWhenForced() + { // Arrange $requests = []; $body = sprintf(self::JSON_TEMPLATE, ConfigFetcher::GLOBAL_URL, 2); $responses = [ - new Response(200, [], $body) + new Response(200, [], $body), ]; $handler = $this->getHandlerStack($responses, $requests); $fetcher = $this->getFetcher($handler); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(1, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); $this->assertEquals(json_decode($body, true), $response->getConfigEntry()->getConfig()); } - public function testShouldRedirectToAnotherServer() { + public function testShouldRedirectToAnotherServer() + { // Arrange $requests = []; $firstBody = sprintf(self::JSON_TEMPLATE, ConfigFetcher::EU_ONLY_URL, 1); @@ -89,16 +93,17 @@ public function testShouldRedirectToAnotherServer() { $fetcher = $this->getFetcher($handler); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(2, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); - $this->assertContains($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); $this->assertEquals(json_decode($secondBody, true), $response->getConfigEntry()->getConfig()); } - public function testShouldRedirectToAnotherServerWhenForced() { + public function testShouldRedirectToAnotherServerWhenForced() + { // Arrange $requests = []; $firstBody = sprintf(self::JSON_TEMPLATE, ConfigFetcher::EU_ONLY_URL, 2); @@ -112,16 +117,17 @@ public function testShouldRedirectToAnotherServerWhenForced() { $fetcher = $this->getFetcher($handler); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(2, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); - $this->assertContains($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); $this->assertEquals(json_decode($secondBody, true), $response->getConfigEntry()->getConfig()); } - public function testShouldBreakRedirectLoop() { + public function testShouldBreakRedirectLoop() + { // Arrange $requests = []; $firstBody = sprintf(self::JSON_TEMPLATE, ConfigFetcher::EU_ONLY_URL, 1); @@ -136,17 +142,18 @@ public function testShouldBreakRedirectLoop() { $fetcher = $this->getFetcher($handler); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(3, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); - $this->assertContains($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); - $this->assertContains($requests[2]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); + $this->assertStringContainsString($requests[2]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); $this->assertEquals(json_decode($firstBody, true), $response->getConfigEntry()->getConfig()); } - public function testShouldBreakRedirectLoopWhenForced() { + public function testShouldBreakRedirectLoopWhenForced() + { // Arrange $requests = []; $firstBody = sprintf(self::JSON_TEMPLATE, ConfigFetcher::EU_ONLY_URL, 2); @@ -161,69 +168,72 @@ public function testShouldBreakRedirectLoopWhenForced() { $fetcher = $this->getFetcher($handler); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(3, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); - $this->assertContains($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); - $this->assertContains($requests[2]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[1]['request']->getUri()->getHost(), ConfigFetcher::EU_ONLY_URL); + $this->assertStringContainsString($requests[2]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); $this->assertEquals(json_decode($firstBody, true), $response->getConfigEntry()->getConfig()); } - public function testShouldRespectCustomUrlWhenNotForced() { + public function testShouldRespectCustomUrlWhenNotForced() + { // Arrange $requests = []; $firstBody = sprintf(self::JSON_TEMPLATE, ConfigFetcher::GLOBAL_URL, 1); $responses = [ new Response(200, [], $firstBody), - new Response(200, [], $firstBody) + new Response(200, [], $firstBody), ]; $handler = $this->getHandlerStack($responses, $requests); $fetcher = $this->getFetcher($handler, self::CUSTOM_CDN_URL); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(1, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), self::CUSTOM_CDN_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), self::CUSTOM_CDN_URL); $this->assertEquals(json_decode($firstBody, true), $response->getConfigEntry()->getConfig()); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(2, count($requests)); - $this->assertContains($requests[1]['request']->getUri()->getHost(), self::CUSTOM_CDN_URL); + $this->assertStringContainsString($requests[1]['request']->getUri()->getHost(), self::CUSTOM_CDN_URL); $this->assertEquals(json_decode($firstBody, true), $response->getConfigEntry()->getConfig()); } - public function testShouldNotRespectCustomUrlWhenForced() { + public function testShouldNotRespectCustomUrlWhenForced() + { // Arrange $requests = []; $firstBody = sprintf(self::JSON_TEMPLATE, ConfigFetcher::GLOBAL_URL, 2); $secondBody = sprintf(self::JSON_TEMPLATE, ConfigFetcher::GLOBAL_URL, 0); $responses = [ new Response(200, [], $firstBody), - new Response(200, [], $secondBody) + new Response(200, [], $secondBody), ]; $handler = $this->getHandlerStack($responses, $requests); $fetcher = $this->getFetcher($handler, self::CUSTOM_CDN_URL); // Act - $response = $fetcher->fetch(""); + $response = $fetcher->fetch(''); // Assert $this->assertEquals(2, count($requests)); - $this->assertContains($requests[0]['request']->getUri()->getHost(), self::CUSTOM_CDN_URL); - $this->assertContains($requests[1]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); + $this->assertStringContainsString($requests[0]['request']->getUri()->getHost(), self::CUSTOM_CDN_URL); + $this->assertStringContainsString($requests[1]['request']->getUri()->getHost(), ConfigFetcher::GLOBAL_URL); $this->assertEquals(json_decode($secondBody, true), $response->getConfigEntry()->getConfig()); } - private function getHandlerStack(array $responses, array &$container = []) { + private function getHandlerStack(array $responses, array &$container = []) + { $history = Middleware::history($container); $stack = HandlerStack::create(new MockHandler($responses)); $stack->push($history); @@ -231,10 +241,11 @@ private function getHandlerStack(array $responses, array &$container = []) { return $stack; } - private function getFetcher($handler, $customUrl = "") { - return new ConfigFetcher("fakeKey", Utils::getTestLogger(), [ + private function getFetcher($handler, $customUrl = '') + { + return new ConfigFetcher('fakeKey', Utils::getTestLogger(), [ ClientOptions::CUSTOM_HANDLER => $handler, - ClientOptions::BASE_URL => $customUrl + ClientOptions::BASE_URL => $customUrl, ]); } -} \ No newline at end of file +} diff --git a/tests/LocalSourceTest.php b/tests/LocalSourceTest.php index 6efdf5b..d1efd03 100644 --- a/tests/LocalSourceTest.php +++ b/tests/LocalSourceTest.php @@ -10,86 +10,93 @@ use ConfigCat\User; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Psr7\Response; -use PHPUnit\Framework\TestCase; use InvalidArgumentException; +use PHPUnit\Framework\TestCase; class LocalSourceTest extends TestCase { - const TEST_JSON_BODY = "{ \"f\" : { \"disabled\": { \"v\": false, \"p\": [], \"r\": [], \"i\":\"fakeIdFirst\" }, \"enabled\": { \"v\": true, \"p\": [], \"r\": [], \"i\":\"fakeIdSecond\" }}}"; + const TEST_JSON_BODY = '{ "f" : { "disabled": { "v": false, "p": [], "r": [], "i":"fakeIdFirst" }, "enabled": { "v": true, "p": [], "r": [], "i":"fakeIdSecond" }}}'; - public function testWithNonExistingFile() { + public function testWithNonExistingFile() + { $this->expectException(InvalidArgumentException::class); - new ConfigCatClient("testWithNonExistingFile", [ - ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile("non-existing"), OverrideBehaviour::LOCAL_ONLY), + new ConfigCatClient('testWithNonExistingFile', [ + ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile('non-existing'), OverrideBehaviour::LOCAL_ONLY), ]); } - public function testWithInvalidBehavior() { + public function testWithInvalidBehavior() + { $this->expectException(InvalidArgumentException::class); - new ConfigCatClient("testWithInvalidBehavior", [ + new ConfigCatClient('testWithInvalidBehavior', [ ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localArray([]), 50), ]); } - public function testWithFile() { - $client = new ConfigCatClient("testWithFile", [ - ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile("tests/test.json"), OverrideBehaviour::LOCAL_ONLY), + public function testWithFile() + { + $client = new ConfigCatClient('testWithFile', [ + ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile('tests/test.json'), OverrideBehaviour::LOCAL_ONLY), ]); - $this->assertTrue($client->getValue("enabledFeature", false)); - $this->assertFalse($client->getValue("disabledFeature", true)); - $this->assertEquals(5, $client->getValue("intSetting", 0)); - $this->assertEquals(3.14, $client->getValue("doubleSetting", 0.0)); - $this->assertEquals("test", $client->getValue("stringSetting", 0)); + $this->assertTrue($client->getValue('enabledFeature', false)); + $this->assertFalse($client->getValue('disabledFeature', true)); + $this->assertEquals(5, $client->getValue('intSetting', 0)); + $this->assertEquals(3.14, $client->getValue('doubleSetting', 0.0)); + $this->assertEquals('test', $client->getValue('stringSetting', 0)); } - public function testWithFile_Rules() { - $client = new ConfigCatClient("testWithFile_Rules", [ - ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile("tests/test-rules.json"), OverrideBehaviour::LOCAL_ONLY), + public function testWithFileRules() + { + $client = new ConfigCatClient('testWithFile_Rules', [ + ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile('tests/test-rules.json'), OverrideBehaviour::LOCAL_ONLY), ]); // without user - $this->assertFalse($client->getValue("rolloutFeature", true)); + $this->assertFalse($client->getValue('rolloutFeature', true)); // not in rule - $this->assertFalse($client->getValue("rolloutFeature", true, new User("test@test.com"))); + $this->assertFalse($client->getValue('rolloutFeature', true, new User('test@test.com'))); // in rule - $this->assertTrue($client->getValue("rolloutFeature", false, new User("test@example.com"))); + $this->assertTrue($client->getValue('rolloutFeature', false, new User('test@example.com'))); } - public function testWithSimpleFile() { - $client = new ConfigCatClient("testWithSimpleFile", [ - ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile("tests/test-simple.json"), OverrideBehaviour::LOCAL_ONLY), + public function testWithSimpleFile() + { + $client = new ConfigCatClient('testWithSimpleFile', [ + ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localFile('tests/test-simple.json'), OverrideBehaviour::LOCAL_ONLY), ]); - $this->assertTrue($client->getValue("enabledFeature", false)); - $this->assertFalse($client->getValue("disabledFeature", true)); - $this->assertEquals(5, $client->getValue("intSetting", 0)); - $this->assertEquals(3.14, $client->getValue("doubleSetting", 0.0)); - $this->assertEquals("test", $client->getValue("stringSetting", 0)); + $this->assertTrue($client->getValue('enabledFeature', false)); + $this->assertFalse($client->getValue('disabledFeature', true)); + $this->assertEquals(5, $client->getValue('intSetting', 0)); + $this->assertEquals(3.14, $client->getValue('doubleSetting', 0.0)); + $this->assertEquals('test', $client->getValue('stringSetting', 0)); } - public function testWithArraySource() { - $client = new ConfigCatClient("testWithArraySource", [ + public function testWithArraySource() + { + $client = new ConfigCatClient('testWithArraySource', [ ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localArray([ 'enabledFeature' => true, 'disabledFeature' => false, 'intSetting' => 5, 'doubleSetting' => 3.14, - 'stringSetting' => "test", + 'stringSetting' => 'test', ]), OverrideBehaviour::LOCAL_ONLY), ]); - $this->assertTrue($client->getValue("enabledFeature", false)); - $this->assertFalse($client->getValue("disabledFeature", true)); - $this->assertEquals(5, $client->getValue("intSetting", 0)); - $this->assertEquals(3.14, $client->getValue("doubleSetting", 0.0)); - $this->assertEquals("test", $client->getValue("stringSetting", 0)); + $this->assertTrue($client->getValue('enabledFeature', false)); + $this->assertFalse($client->getValue('disabledFeature', true)); + $this->assertEquals(5, $client->getValue('intSetting', 0)); + $this->assertEquals(3.14, $client->getValue('doubleSetting', 0.0)); + $this->assertEquals('test', $client->getValue('stringSetting', 0)); } - public function testLocalOverRemote() { - $client = new ConfigCatClient("testLocalOverRemote", [ + public function testLocalOverRemote() + { + $client = new ConfigCatClient('testLocalOverRemote', [ ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localArray([ 'enabled' => false, 'nonexisting' => true, @@ -99,12 +106,13 @@ public function testLocalOverRemote() { ), ]); - $this->assertTrue($client->getValue("nonexisting", false)); - $this->assertFalse($client->getValue("enabled", true)); + $this->assertTrue($client->getValue('nonexisting', false)); + $this->assertFalse($client->getValue('enabled', true)); } - public function testRemoteOverLocal() { - $client = new ConfigCatClient("testRemoteOverLocal", [ + public function testRemoteOverLocal() + { + $client = new ConfigCatClient('testRemoteOverLocal', [ ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localArray([ 'enabled' => false, 'nonexisting' => true, @@ -114,22 +122,23 @@ public function testRemoteOverLocal() { ), ]); - $this->assertTrue($client->getValue("nonexisting", false)); - $this->assertTrue($client->getValue("enabled", false)); + $this->assertTrue($client->getValue('nonexisting', false)); + $this->assertTrue($client->getValue('enabled', false)); } - public function testLocalOnlyIgnoresFetched() { + public function testLocalOnlyIgnoresFetched() + { $handler = new MockHandler( [new Response(200, [], self::TEST_JSON_BODY)] ); - $client = new ConfigCatClient("testLocalOnlyIgnoresFetched", [ + $client = new ConfigCatClient('testLocalOnlyIgnoresFetched', [ ClientOptions::FLAG_OVERRIDES => new FlagOverrides(OverrideDataSource::localArray([ 'nonexisting' => true, ]), OverrideBehaviour::LOCAL_ONLY), ClientOptions::CUSTOM_HANDLER => $handler, ]); - $this->assertFalse($client->getValue("enabled", false)); + $this->assertFalse($client->getValue('enabled', false)); $this->assertEquals(1, $handler->count()); } -} \ No newline at end of file +} diff --git a/tests/LoggerTest.php b/tests/LoggerTest.php index 6d7d7b4..693cbe1 100644 --- a/tests/LoggerTest.php +++ b/tests/LoggerTest.php @@ -21,44 +21,52 @@ public function testLoggerBypassesInternalLogicWhenGlobalLevelIsZero() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::once()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::once()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::once()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::once()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::once()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::once()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::once()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerLogOnlyHigherLevelThanDebug() @@ -69,44 +77,52 @@ public function testLoggerLogOnlyHigherLevelThanDebug() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::once()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::once()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::once()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::once()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::once()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::once()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerLogOnlyHigherLevelThanInfo() @@ -117,44 +133,52 @@ public function testLoggerLogOnlyHigherLevelThanInfo() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::once()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::once()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::once()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::once()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::once()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerLogOnlyHigherLevelThanNotice() @@ -165,44 +189,52 @@ public function testLoggerLogOnlyHigherLevelThanNotice() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::once()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::once()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::once()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::once()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerLogOnlyHigherLevelThanWarning() @@ -213,44 +245,52 @@ public function testLoggerLogOnlyHigherLevelThanWarning() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::once()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::once()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::once()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::never()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerLogOnlyHigherLevelThanError() @@ -261,44 +301,52 @@ public function testLoggerLogOnlyHigherLevelThanError() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::once()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::once()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::never()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::never()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerLogOnlyHigherLevelThanCritical() @@ -309,44 +357,52 @@ public function testLoggerLogOnlyHigherLevelThanCritical() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::once()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::never()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::never()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::never()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerLogOnlyHigherLevelThanAlert() @@ -357,44 +413,52 @@ public function testLoggerLogOnlyHigherLevelThanAlert() $mockLogger ->expects(self::once()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::never()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::never()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::never()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::never()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testLoggerNoLog() @@ -405,88 +469,104 @@ public function testLoggerNoLog() $mockLogger ->expects(self::never()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::never()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::never()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::never()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::never()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency(""); - $logger->alert(""); - $logger->critical(""); - $logger->error(""); - $logger->notice(""); - $logger->info(""); - $logger->debug(""); - $logger->warning(""); + $logger->emergency(''); + $logger->alert(''); + $logger->critical(''); + $logger->error(''); + $logger->notice(''); + $logger->info(''); + $logger->debug(''); + $logger->warning(''); } public function testClientNoLog() { $mockLogger = $this->getMockBuilder(LoggerInterface::class)->getMock(); - $client = new ConfigCatClient("not-existing", [ - ClientOptions::LOGGER => $mockLogger, - ClientOptions::LOG_LEVEL => LogLevel::NO_LOG + $client = new ConfigCatClient('not-existing', [ + ClientOptions::LOGGER => $mockLogger, + ClientOptions::LOG_LEVEL => LogLevel::NO_LOG, ]); $mockLogger ->expects(self::never()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::never()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::never()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::never()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::never()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $client->getValue("fake", false); + $client->getValue('fake', false); } public function testLoggerBypassesLogWhenExceptionIsIgnored() @@ -497,43 +577,51 @@ public function testLoggerBypassesLogWhenExceptionIsIgnored() $mockLogger ->expects(self::never()) - ->method("emergency"); + ->method('emergency') + ; $mockLogger ->expects(self::never()) - ->method("alert"); + ->method('alert') + ; $mockLogger ->expects(self::never()) - ->method("critical"); + ->method('critical') + ; $mockLogger ->expects(self::never()) - ->method("error"); + ->method('error') + ; $mockLogger ->expects(self::never()) - ->method("warning"); + ->method('warning') + ; $mockLogger ->expects(self::never()) - ->method("notice"); + ->method('notice') + ; $mockLogger ->expects(self::never()) - ->method("info"); + ->method('info') + ; $mockLogger ->expects(self::never()) - ->method("debug"); + ->method('debug') + ; - $logger->emergency("", ['exception' => new InvalidArgumentException()]); - $logger->alert("", ['exception' => new InvalidArgumentException()]); - $logger->critical("", ['exception' => new InvalidArgumentException()]); - $logger->error("", ['exception' => new InvalidArgumentException()]); - $logger->notice("", ['exception' => new InvalidArgumentException()]); - $logger->info("", ['exception' => new InvalidArgumentException()]); - $logger->debug("", ['exception' => new InvalidArgumentException()]); - $logger->warning("", ['exception' => new InvalidArgumentException()]); + $logger->emergency('', ['exception' => new InvalidArgumentException()]); + $logger->alert('', ['exception' => new InvalidArgumentException()]); + $logger->critical('', ['exception' => new InvalidArgumentException()]); + $logger->error('', ['exception' => new InvalidArgumentException()]); + $logger->notice('', ['exception' => new InvalidArgumentException()]); + $logger->info('', ['exception' => new InvalidArgumentException()]); + $logger->debug('', ['exception' => new InvalidArgumentException()]); + $logger->warning('', ['exception' => new InvalidArgumentException()]); } -} \ No newline at end of file +} diff --git a/tests/RolloutIntegrationsTest.php b/tests/RolloutIntegrationsTest.php index 6d1c522..56ed21d 100644 --- a/tests/RolloutIntegrationsTest.php +++ b/tests/RolloutIntegrationsTest.php @@ -14,19 +14,19 @@ class RolloutIntegrationsTest extends TestCase const variationKind = 1; /** - * @param $file - * @param $sdkKey - * @param $kind - * * @dataProvider rolloutTestData + * + * @param mixed $file + * @param mixed $sdkKey + * @param mixed $kind */ public function testRolloutIntegration($file, $sdkKey, $kind) { - $rows = self::readCsv("tests/" . $file); + $rows = self::readCsv('tests/'.$file); $settingKeys = array_slice($rows[0], 4); $customKey = $rows[0][3]; $client = new ConfigCatClient($sdkKey, [ - ClientOptions::LOG_LEVEL => LogLevel::WARNING + ClientOptions::LOG_LEVEL => LogLevel::WARNING, ]); $errors = []; @@ -34,32 +34,34 @@ public function testRolloutIntegration($file, $sdkKey, $kind) $keys = $client->getAllKeys(); $diff = array_diff($settingKeys, $keys); if (!empty($diff)) { - $errors[] = sprintf("Not all keys are found, Expected: %s, Result: %s, Diff: %s", + $errors[] = sprintf( + 'Not all keys are found, Expected: %s, Result: %s, Diff: %s', print_r($settingKeys, true), print_r($keys, true), - print_r($diff, true)); + print_r($diff, true) + ); } foreach (range(1, count($rows) - 1) as $i) { $testObjects = $rows[$i]; $user = null; - if ($testObjects[0] !== "##null##") { + if ('##null##' !== $testObjects[0]) { $identifier = $testObjects[0]; - $email = ""; - $country = ""; + $email = ''; + $country = ''; - if (!empty($testObjects[1]) && $testObjects[1] !== "##null##") { + if (!empty($testObjects[1]) && '##null##' !== $testObjects[1]) { $email = $testObjects[1]; } - if (!empty($testObjects[2]) && $testObjects[2] !== "##null##") { + if (!empty($testObjects[2]) && '##null##' !== $testObjects[2]) { $country = $testObjects[2]; } $custom = []; - if (!empty($testObjects[3]) && $testObjects[3] !== "##null##") { + if (!empty($testObjects[3]) && '##null##' !== $testObjects[3]) { $custom[$customKey] = $testObjects[3]; } elseif (is_numeric($testObjects[3])) { $custom[$customKey] = $testObjects[3]; @@ -71,12 +73,12 @@ public function testRolloutIntegration($file, $sdkKey, $kind) $count = 0; foreach ($settingKeys as $key) { $expected = $testObjects[$count + 4]; - $actual = $kind == self::valueKind + $actual = self::valueKind == $kind ? $client->getValue($key, null, $user) : $client->getValueDetails($key, null, $user)->getVariationId(); if (is_bool($actual)) { - $actual = $actual ? "True" : "False"; + $actual = $actual ? 'True' : 'False'; } if (is_int($actual)) { @@ -88,19 +90,31 @@ public function testRolloutIntegration($file, $sdkKey, $kind) } if ($expected !== $actual) { - $errors[] = sprintf("Identifier: %s, SettingKey: %s, UV: %s, Expected: %s, Result: %s", $testObjects[0], $key, $testObjects[3], $expected, $actual); + $errors[] = sprintf('Identifier: %s, SettingKey: %s, UV: %s, Expected: %s, Result: %s', $testObjects[0], $key, $testObjects[3], $expected, $actual); } - $count++; + ++$count; } } $this->assertEquals(0, count($errors)); } + public function rolloutTestData(): array + { + return [ + ['testmatrix.csv', 'PKDVCLf-Hq-h-kCzMp-L7Q/psuH7BGHoUmdONrzzUOY7A', self::valueKind], + ['testmatrix_semantic.csv', 'PKDVCLf-Hq-h-kCzMp-L7Q/BAr3KgLTP0ObzKnBTo5nhA', self::valueKind], + ['testmatrix_number.csv', 'PKDVCLf-Hq-h-kCzMp-L7Q/uGyK3q9_ckmdxRyI7vjwCw', self::valueKind], + ['testmatrix_semantic_2.csv', 'PKDVCLf-Hq-h-kCzMp-L7Q/q6jMCFIp-EmuAfnmZhPY7w', self::valueKind], + ['testmatrix_sensitive.csv', 'PKDVCLf-Hq-h-kCzMp-L7Q/qX3TP2dTj06ZpCCT1h_SPA', self::valueKind], + ['testmatrix_variationId.csv', 'PKDVCLf-Hq-h-kCzMp-L7Q/nQ5qkhRAUEa6beEyyrVLBA', self::variationKind], + ]; + } + private static function readCsv($file): array { $rows = []; - if (($handle = fopen($file, "r")) !== false) { + if (($handle = fopen($file, 'r')) !== false) { while (($data = fgetcsv($handle, 1200, ';')) !== false) { $rows[] = $data; } @@ -109,16 +123,4 @@ private static function readCsv($file): array return $rows; } - - public function rolloutTestData(): array - { - return [ - ["testmatrix.csv", "PKDVCLf-Hq-h-kCzMp-L7Q/psuH7BGHoUmdONrzzUOY7A", self::valueKind], - ["testmatrix_semantic.csv", "PKDVCLf-Hq-h-kCzMp-L7Q/BAr3KgLTP0ObzKnBTo5nhA", self::valueKind], - ["testmatrix_number.csv", "PKDVCLf-Hq-h-kCzMp-L7Q/uGyK3q9_ckmdxRyI7vjwCw", self::valueKind], - ["testmatrix_semantic_2.csv", "PKDVCLf-Hq-h-kCzMp-L7Q/q6jMCFIp-EmuAfnmZhPY7w", self::valueKind], - ["testmatrix_sensitive.csv", "PKDVCLf-Hq-h-kCzMp-L7Q/qX3TP2dTj06ZpCCT1h_SPA", self::valueKind], - ["testmatrix_variationId.csv", "PKDVCLf-Hq-h-kCzMp-L7Q/nQ5qkhRAUEa6beEyyrVLBA", self::variationKind], - ]; - } } diff --git a/tests/UserTest.php b/tests/UserTest.php index ac76263..5fe8d6a 100644 --- a/tests/UserTest.php +++ b/tests/UserTest.php @@ -3,20 +3,19 @@ namespace ConfigCat\Tests; use ConfigCat\User; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; class UserTest extends TestCase { public function testConstructEmptyIdentifier() { - $user = new User(""); - $this->assertEquals("", $user->getIdentifier()); + $user = new User(''); + $this->assertEquals('', $user->getIdentifier()); } public function testGetAttributeEmptyKey() { - $user = new User("id"); - $this->assertEquals("", $user->getAttribute("")); + $user = new User('id'); + $this->assertEquals('', $user->getAttribute('')); } } diff --git a/tests/Utils.php b/tests/Utils.php index eea5a0b..a35a464 100644 --- a/tests/Utils.php +++ b/tests/Utils.php @@ -3,48 +3,40 @@ namespace ConfigCat\Tests; use ConfigCat\Hooks; +use ConfigCat\Log\DefaultLogger; use ConfigCat\Log\InternalLogger; use ConfigCat\Log\LogLevel; -use Monolog\Formatter\LineFormatter; -use Monolog\Handler\ErrorLogHandler; -use Monolog\Logger; -use Monolog\Processor\PsrLogMessageProcessor; use Psr\Log\NullLogger; class Utils { public static function getTestLogger(): InternalLogger { - $handler = new ErrorLogHandler(); - $formatter = new LineFormatter( - "[%datetime%] %channel%.%level_name%: [%context.event_id%] %message% %context% %extra%\n", - null, true, true); - $handler->setFormatter($formatter); - $psrProcessor = new PsrLogMessageProcessor(null, true); - return new InternalLogger(new Logger("ConfigCat", [$handler], [$psrProcessor]), LogLevel::WARNING, [], new Hooks()); + return new InternalLogger(new DefaultLogger(), LogLevel::WARNING, [], new Hooks()); } - public static function getNullLogger(): InternalLogger { + public static function getNullLogger(): InternalLogger + { return new InternalLogger(new NullLogger(), LogLevel::DEBUG, [], new Hooks()); } public static function formatConfigWithRules(): string { - return "{ \"f\": { \"key\": { \"v\": \"def\", \"i\": \"defVar\", \"p\": [], \"r\": [ - { - \"v\": \"fake1\", - \"i\": \"id1\", - \"t\": 2, - \"a\": \"Identifier\", - \"c\": \"@test1.com\" - }, - { - \"v\": \"fake2\", - \"i\": \"id2\", - \"t\": 2, - \"a\": \"Identifier\", - \"c\": \"@test2.com\" - } - ] }}}"; + return '{ "f": { "key": { "v": "def", "i": "defVar", "p": [], "r": [ + { + "v": "fake1", + "i": "id1", + "t": 2, + "a": "Identifier", + "c": "@test1.com" + }, + { + "v": "fake2", + "i": "id2", + "t": 2, + "a": "Identifier", + "c": "@test2.com" + } + ] }}}'; } -} \ No newline at end of file +}