Skip to content

Commit 529fa36

Browse files
LaunchDarklyReleaseBoteli-darklyhroederldLaunchDarklyCIbwoskow-ld
authored
prepare 5.0.0 release (#168)
* fix apc/apcu calls * fix comment * fix deprecated caching store classes * better caching abstraction * typo * linter * fix namespaces * fix tests * doc fixes * move a bunch more stuff * misc cleanup * add test for not having any flags * restore documentation for deprecated properties * version 3.5.0 * revert accidental commit * add experimentation event overrides for rules and fallthrough * linter * misc fixes * misc test fixes * Hr/ch34492/waitonconsul (#39) * add step to wait on Consul * coerce user attributes to strings when necessary, don't send events without valid users * explanatory comments * add release script (version update only) * use newer readme footer format * support metric value in track() * linter * update method description * add param to skip db tests, update docs for new repo name * wrong method name * add tests for rollout calculations, + misc test cleanup * misc doc fixes * misc doc fixes * update package name (#45) * add test for cached get all * typo * revert bugfix to test the test * reinstate bugfix * mix fixes, rename file * fix filename * misc cleanup * linter * misc fixes * test state cleanup * linter + fix filename in instructions * misc CI fixes, don't try to install phpredis in 5.6 * don't let user fall outside of last bucket in rollout * PHP 5.5 requires even null properties to be defined * minor cleanup * more accurate changelog text regarding phpredis * Add circle jobs for newer PHP versions. * Revert "Add circle jobs for newer PHP versions." This reverts commit 8939cb2. * implement doc generation with phpDocumentor 3 (prerelease), clean up doc tags (#50) * Add CI jobs for PHP 7.3 + 7.4 (#51) * ensure events aren't sent if send_events is false * clarify test with comment * make prefix concatenation in DynamoDB consistent with other SDKs * fix test * fix PHP 5.5 CI build by pinning Composer version (#54) * Updating warning log in Identify to not say Track was called (#56) * Removed the guides link * add alias functionality and some related tests * Revert "add alias functionality and some related tests" This reverts commit 2bf1cba. * add alias events support (#57) * add alias function to LDClient * add `contextKind` to events that require the new field * add tests for alias and contextKind * merge exp-alloc * drop support for EOL php versions and update deps (#60) * fix test class * remove database integrations from SDK (#63) * add type hints to FeatureRequesterBase methods * fix PHPRedis logic for prefix & custom client, add unit tests (#64) * add psalm lints and php hints (#62) * remove deprecated members (#65) * change default base URL to sdk.launchdarkly.com * move non-public classes into Impl namespace (#66) * add CI job for PHP 8.0 (#69) * use phpDocumentor 3 + misc doc comment cleanup (#68) * Updates docs URLs * pin Psalm to 4.9.2 to prevent a spurious linting error (#71) * use Releaser v2 config + add badge links in readme (#72) * use Releaser v2 config + add badge links in readme * fix badge URL * use Releaser PHP project template (#73) * use Releaser PHP project template * exclude implementation classes from docs * remove obsolete line that's no longer used because $seed is computed elsewhere * remove obsolete VERSION file * Fix test confirming send_events = false is honored (#78) * Minor cleanup and consistency changes (#79) * Add integration test for curl event publisher (#77) * Curl honor connect_timeout (#81) * Set required connect_timeout option in test (#82) * Add Windows support for "Curl" publisher (#80) * Add cs-check to build process (#83) * Don't treat numeric strings as numbers (#84) * Decrease psalm error level (#85) * added TestData, FlagBuilder; added TestDataTest; started implementing FlagBuilder methods * made props protected, made basic build method, made variations method with tests * continued implementing test data and tests for test data * implemented varationForAllUsers, valueForAllUsers, and variationForUsers along with corresponding tests * reorganized functions, added needed classes and functions, left some bodys blank to complete later, implemeted others, organized tests, added missing assertions * broken - transitioning build to return a FeatureFlag, finished implementing some methods * reverted build to return array, implemented getFeature and getAllFeatures methods in FeatureRequester interface for TestData * fixed build, implemented FeatureRequester, added simple test case * Event attribute filtering is overly aggressively (#86) If you try to create a custom attribute with a value of 0, our event serialization code would filter that out because `0 != null` is false. However, it is reasonable to expect that a user might want to provide the value of 0 as a custom value. The code has been updated to only exclude explicitly null values. * Account for traffic allocation on all flags (#87) * finished FlagRuleBuilder implementation; fixed psalm errors; improved comment blocks to adhere better to phpdoc; fixed formatting errors in both TestData and TestDataTest * fixed php-cs-fixer warnings in TestDataTest.php * Apply suggestions from code review Co-authored-by: Matthew M. Keeler <keelerm84@gmail.com> * replace use of array_push with append operator; standardized capitalization of booleans including in code blogs; other formatting adjustments * remove array_splice() implementation of existing user key removal due to breakage in unit tests * converted variationForUser to use array_splice but fixed issue caused by pass by reference instead of pass by value * fixed missing indexes required to decode FlagBuilder into a FeatureFlag using the decode() method * split off TestData\FlagBuilder and TestData\FlagRuleBuilder from TestData; add test coverage for TestData class * additional cleanup; added missing type hint; minor refactoring * remove special handling of singleton arrays in FlagBuilder::variations() * run php-cs-fixer on TestData-related files * started writing repetitive tests using the phpunit @dataProvider feature * swapped positions of expected and actual in dataProvider-driven test * fix typos in code in comment blocks * Apply whitespace fixes from code review Co-authored-by: Matthew M. Keeler <keelerm84@gmail.com> * split unwieldy tests in TestDataTest into separate tests; address TODO item * added annotations to dataProvider-driven tests * changed _isBooleanFlag() implementation to use strict equality * Add unit test to verify `in` operator in TestData (#89) * Add support for psr/log 2 and 3 (#91) * Adds link to Relay Proxy docs * master -> main * Add support for Guzzle 6.3 (#93) * Use setVersion on update of a changed flag (#161) - in TestData::update when flag is being copied from previous version - use setVersion rather than ['version'] to avoid generated error - associated unit test to update an initial flag, change the flag and update it again - unit-tested using docker for php 7.3, 7.4, 8.0, 8.1 Co-authored-by: Colin Henwood <colin.henwood@xero.com> * Add support for monolog 3.0 (#94) * fix base URI concatenation so path isn't lost * fix JSON output for empty allFlagsState result * lint * also fix base URIs for events * fix JSON output for empty allFlagsState result (#97) * fix base URI concatenation so path isn't lost (#96) * fix base URI concatenation so path isn't lost * also fix base URIs for events * disallow non-strings in semver comparisons (#98) * implement contract test service (#95) * (5.0) fix date parsing to disallow invalid types and formats (#99) * fix date parsing to disallow invalid types and formats * lint * change test service to not require Docker, enable tests in CI (#100) * remove alias events (#101) * (U2C #1) implement context type (without attribute references) (#102) * (U2C #2) basic changes to use contexts in evaluations instead of users (#103) * (U2C #3) update CI, release configuration, and dev dependencies for min PHP version of 8.0 (#104) * update CI, release configuration, and dev dependencies for min PHP version of 8.0- * require more recent php-cs-fixer * (U2C #4) use PHP 8 type declarations and strict mode (#105) * (U2C #5) misc syntax cleanup to take advantage of modern language features (#106) * (U2C 6) factor evaluation logic out of model classes (#107) * (U2C #7) support contextKind in clauses (#108) * (U2C #8) support contextTargets (#109) * (U2C #9) support contextKind in rollouts/experiments (#110) * (U2C #10) support includedContexts/excludedContexts in segment (#111) * support attribute reference lookups in evaluations * misc fixes * move AttributeReference class and create instances of it * lint * improve error handling/logging in evaluations * misc fixes * fix exception string conversion * (U2C #11) support attribute reference lookups in evaluations (#112) * support attribute reference lookups in evaluations * misc fixes * move AttributeReference class and create instances of it * lint * comment * revert unnecessary change * update all event logic for U2C * (U2C #12) improve error handling/logging in evaluations (#113) * coalesce operator makes Elvis operator redundant * more coalesce * (U2C #13) update all event logic for U2C (#114) * remove LDUser and LDUserBuilder (#115) * (U2C #15) implement prerequisite cycle detection (#120) * (U2C #16) implement segment recursion and segment cycle detection (#117) * remove LDUser and LDUserBuilder * implement prerequisite cycle detection * lint * rm unused * rm debugging * implement segment recursion and segment cycle detection * (U2C #17) make AttributeReference public in new Types namespace (#118) * (U2C #18) move EventPublisher & FeatureRequester out of main namespace (#119) * (U2C #19) remove deprecated things, clean up tests (#121) * re-add LDUser, allow SDK to accept it interchangeably with LDContext * doc comment improvements * fix custom attribute validation for old users * fix user conversion * update TestData API to use context kinds (#123) Co-authored-by: Eli Bishop <eli@launchdarkly.com> Co-authored-by: hroederld <46500128+hroederld@users.noreply.github.com> Co-authored-by: LaunchDarklyCI <dev@launchdarkly.com> Co-authored-by: Ben Woskow <48036130+bwoskow-ld@users.noreply.github.com> Co-authored-by: Ben Woskow <bwoskow@launchdarkly.com> Co-authored-by: Gavin Whelan <gwhelan@launchdarkly.com> Co-authored-by: elliot <elliot@debian.elliot> Co-authored-by: Elliot Haisley <35050275+Apache-HB@users.noreply.github.com> Co-authored-by: Harpo Roeder <hroeder@launchdarkly.com> Co-authored-by: LaunchDarklyReleaseBot <launchdarklyreleasebot@launchdarkly.com> Co-authored-by: Ember Stevens <ember.stevens@launchdarkly.com> Co-authored-by: ember-stevens <79482775+ember-stevens@users.noreply.github.com> Co-authored-by: Matthew M. Keeler <keelerm84@gmail.com> Co-authored-by: charukiewicz <charukiewicz@protonmail.com> Co-authored-by: Joey Malinowski <joemalin95@gmail.com> Co-authored-by: Christian Charukiewicz <christian@foxhound.systems> Co-authored-by: Matthew M. Keeler <mkeeler@launchdarkly.com> Co-authored-by: Colin Henwood <aretenz@users.noreply.github.com> Co-authored-by: Colin Henwood <colin.henwood@xero.com>
1 parent b98ef21 commit 529fa36

File tree

97 files changed

+6352
-3413
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+6352
-3413
lines changed

.circleci/config.yml

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ workflows:
99
- test-on-linux:
1010
matrix:
1111
parameters:
12-
php-version: ["7.3", "7.4", "8.0", "8.1"]
12+
php-version: ["8.0", "8.1"]
1313
composer-dependencies: ["lowest", "highest"]
1414
- test-on-windows
1515

@@ -77,22 +77,12 @@ jobs:
7777
name: downgrade to lowest versions
7878
command: composer update --prefer-lowest --prefer-stable
7979

80-
- run:
81-
name: psalm linting
82-
command: ./vendor/bin/psalm --no-cache
83-
- when:
84-
condition:
85-
not:
86-
equal: [ "8.1", <<parameters.php-version>> ]
87-
steps:
88-
- run:
89-
name: php-cs-fixer check
90-
command: composer cs-check
9180
- run:
9281
name: run tests
93-
command: php -d xdebug.mode=coverage vendor/bin/phpunit
94-
environment:
95-
XDEBUG_MODE: coverage
82+
command: make test
83+
- run:
84+
name: lint
85+
command: make lint
9686

9787
- run:
9888
name: build contract test service

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*.iml
55
composer.phar
66
.php_cs.cache
7+
.php-cs-fixer.cache
78
.vagrant
89
integration-tests/vendor
910
composer.lock

.ldrelease/config.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ publications:
99
description: Packagist
1010

1111
jobs:
12-
- template:
12+
- docker:
13+
image: ldcircleci/php-sdk-release:4 # Releaser's default for PHP is still php-sdk-release:3, which is PHP 7.x
14+
template:
1315
name: php
1416

1517
documentation:

CONTRIBUTING.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ composer install
2828

2929
To run all unit tests:
3030

31-
```
31+
```shell
3232
./vendor/bin/phpunit
33+
34+
# Or, as a shortcut in Linux:
35+
make test
3336
```
3437

3538
It is preferable to run tests against all supported minor versions of PHP (as described in `README.md` under Requirements), or at least the lowest and highest versions, prior to submitting a pull request. However, LaunchDarkly's CI tests will run automatically against all supported versions.
@@ -45,3 +48,13 @@ To run the SDK contract test suite in Linux (see [`test-service/README.md`](./te
4548
```bash
4649
make contract-tests
4750
```
51+
52+
To run the Psalm linter and cs-check:
53+
54+
```shell
55+
./vendor/bin/psalm --no-cache
56+
composer cs-check
57+
58+
# Or, as a shortcut in Linux:
59+
make lint
60+
```

Makefile

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11

2+
test:
3+
php -d xdebug.mode=coverage vendor/bin/phpunit
4+
5+
lint:
6+
./vendor/bin/psalm --no-cache
7+
composer cs-check
8+
9+
210
TEMP_TEST_OUTPUT=/tmp/sse-contract-test-service.log
311

412
# TEST_HARNESS_PARAMS can be set to add -skip parameters for any contract tests that cannot yet pass
513
# Explanation of current skips:
6-
# - "secondary": In the PHP SDK this is not an addressable attribute for clauses; in other
7-
# SDKs, it is. This was underspecified in the past; in future major versions, the other
8-
# SDKs and the contract tests will be in line with the PHP behavior.
9-
# - "date - bad syntax", "semver - bad type": The PHP SDK has insufficiently strict
10-
# validation for these types. We will definitely fix this in 5.0 but may or may not
11-
# address it in 4.x, since it does not prevent any valid values from working.
14+
# - "evaluation/parameterized/attribute references/array index is not supported": Due to how PHP
15+
# arrays work, there's no way to disallow an array index lookup without breaking object property
16+
# lookups for properties that are numeric strings.
1217
TEST_HARNESS_PARAMS := $(TEST_HARNESS_PARAMS) \
13-
-skip 'evaluation/parameterized/secondary' \
14-
-skip 'evaluation/parameterized/operators - date - bad syntax' \
15-
-skip 'evaluation/parameterized/operators - semver - bad type'
18+
-skip 'evaluation/parameterized/attribute references/array index is not supported'
1619

1720
build-contract-tests:
1821
@cd test-service && composer install --no-progress
@@ -26,7 +29,7 @@ start-contract-test-service-bg:
2629

2730
run-contract-tests:
2831
@curl -s https://raw.githubusercontent.com/launchdarkly/sdk-test-harness/main/downloader/run.sh \
29-
| VERSION=v1 PARAMS="-url http://localhost:8000 -debug -stop-service-at-end $(TEST_HARNESS_PARAMS)" sh
32+
| VERSION=v2 PARAMS="-url http://localhost:8000 -debug -stop-service-at-end $(TEST_HARNESS_PARAMS)" sh
3033

3134
contract-tests: build-contract-tests start-contract-test-service-bg run-contract-tests
3235

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
## Supported PHP versions
1414

15-
This version of the LaunchDarkly SDK is compatible with PHP 7.3 and higher.
15+
This version of the LaunchDarkly SDK is compatible with PHP 8.0 and higher.
1616

1717
## Getting started
1818

composer.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
}
1515
],
1616
"require": {
17-
"php": ">=7.3",
18-
"monolog/monolog": "^1.6|^2.0|^3.0",
17+
"php": ">=8.0",
18+
"monolog/monolog": "^2.0|^3.0",
1919
"psr/log": "^1.0|^2.0|^3.0"
2020
},
2121
"require-dev": {
22-
"friendsofphp/php-cs-fixer": ">=2.18.0 <3.0",
23-
"guzzlehttp/guzzle": "^6.3 | ^7",
22+
"friendsofphp/php-cs-fixer": "^3.12.0",
23+
"guzzlehttp/guzzle": "^7",
2424
"kevinrob/guzzle-cache-middleware": "^4.0",
2525
"phpunit/php-code-coverage": "^9",
2626
"phpunit/phpunit": "^9",

src/LaunchDarkly/EvaluationDetail.php

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace LaunchDarkly;
46

57
/**
@@ -9,43 +11,39 @@
911
*/
1012
class EvaluationDetail
1113
{
12-
/** @var int|null */
13-
private $_variationIndex = null;
14-
15-
/** @var mixed|null */
16-
private $_value = null;
17-
18-
/** @var EvaluationReason */
19-
private $_reason;
14+
private ?int $_variationIndex = null;
15+
private mixed $_value = null;
16+
private EvaluationReason $_reason;
2017

2118
/**
2219
* EvaluationDetail constructor.
23-
* @param mixed|null $value the value of the flag variation
20+
* @param mixed $value the value of the flag variation
2421
* @param int|null $variationIndex the index of the flag variation, or null if it was the default value
2522
* @param EvaluationReason $reason evaluation reason properties
2623
*/
27-
public function __construct($value, ?int $variationIndex, EvaluationReason $reason)
24+
public function __construct(mixed $value, ?int $variationIndex, EvaluationReason $reason)
2825
{
2926
$this->_value = $value;
3027
$this->_variationIndex = $variationIndex;
3128
$this->_reason = $reason;
3229
}
3330

3431
/**
35-
* Returns the value of the flag variation for the user.
32+
* Returns the result of the flag evaluation. This will be either one of the flag's variations or the default
33+
* value that was passed to the {@see \LaunchDarkly\LDClient::variationDetail()} method.
3634
*
37-
* @return mixed
35+
* @return mixed the flag value
3836
*/
39-
public function getValue()
37+
public function getValue(): mixed
4038
{
4139
return $this->_value;
4240
}
4341

4442
/**
45-
* Returns the index of the flag variation for the user, e.g. 0 for the first variation -
46-
* or null if it was the default value.
43+
* The index of the returned value within the flag's list of variations, e.g. 0 for the first variation--
44+
* or null if it was the default value (evaluation failed).
4745
*
48-
* @return int | null
46+
* @return ?int the variation index if applicable
4947
*/
5048
public function getVariationIndex(): ?int
5149
{

src/LaunchDarkly/EvaluationReason.php

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace LaunchDarkly;
46

57
/**
@@ -81,23 +83,12 @@ class EvaluationReason implements \JsonSerializable
8183
*/
8284
const EXCEPTION_ERROR = 'EXCEPTION';
8385

84-
/** @var string */
85-
private $_kind;
86-
87-
/** @var string|null */
88-
private $_errorKind;
89-
90-
/** @var int|null */
91-
private $_ruleIndex;
92-
93-
/** @var string|null */
94-
private $_ruleId;
95-
96-
/** @var string|null */
97-
private $_prerequisiteKey;
98-
99-
/** @var bool */
100-
private $_inExperiment;
86+
private string $_kind;
87+
private ?string $_errorKind;
88+
private ?int $_ruleIndex;
89+
private ?string $_ruleId;
90+
private ?string $_prerequisiteKey;
91+
private bool $_inExperiment;
10192

10293
/**
10394
* Creates a new instance of the OFF reason.
@@ -228,7 +219,7 @@ public function getPrerequisiteKey(): ?string
228219
/**
229220
* Returns true if the evaluation resulted in an experiment rollout *and* served
230221
* one of the variations in the experiment. Otherwise it returns false.
231-
* @return boolean
222+
* @return bool
232223
*/
233224
public function isInExperiment(): bool
234225
{
@@ -242,11 +233,11 @@ public function __toString(): string
242233
{
243234
switch ($this->_kind) {
244235
case self::RULE_MATCH:
245-
return $this->_kind . '(' . ($this->_ruleIndex ?? 0) . ',' . ($this->_ruleId ?? '') . ')';
236+
return $this->_kind . '(' . ($this->_ruleIndex ?: 0) . ',' . ($this->_ruleId ?: '') . ')';
246237
case self::PREREQUISITE_FAILED:
247-
return $this->_kind . '(' . ($this->_prerequisiteKey ?? '') . ')';
238+
return $this->_kind . '(' . ($this->_prerequisiteKey ?: '') . ')';
248239
case self::ERROR:
249-
return $this->_kind . '(' . ($this->_errorKind ?? '') . ')';
240+
return $this->_kind . '(' . ($this->_errorKind ?: '') . ')';
250241
default:
251242
return $this->_kind;
252243
}

src/LaunchDarkly/FeatureFlagsState.php

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace LaunchDarkly;
46

57
use LaunchDarkly\Impl\Model\FeatureFlag;
@@ -15,14 +17,9 @@
1517
*/
1618
class FeatureFlagsState implements \JsonSerializable
1719
{
18-
/** @var bool */
19-
protected $_valid = false;
20-
21-
/** @var array */
22-
protected $_flagValues;
23-
24-
/** @var array<string, array{debugEventsUntilDate?: int|null, reason?: EvaluationReason, trackEvents?: true, variation?: int|null, version?: int}> **/
25-
protected $_flagMetadata;
20+
protected bool $_valid = false;
21+
protected array $_flagValues;
22+
protected array $_flagMetadata;
2623

2724
/**
2825
* @ignore
@@ -44,16 +41,15 @@ public function __construct(bool $valid)
4441
public function addFlag(
4542
FeatureFlag $flag,
4643
EvaluationDetail $detail,
44+
bool $forceReasonTracking = false,
4745
bool $withReason = false,
4846
bool $detailsOnlyIfTracked = false
4947
): void {
50-
$requireExperimentData = $flag->isExperiment($detail->getReason());
51-
5248
$this->_flagValues[$flag->getKey()] = $detail->getValue();
5349
$meta = [];
5450

55-
$trackEvents = $flag->isTrackEvents() || $requireExperimentData;
56-
$trackReason = $requireExperimentData;
51+
$trackEvents = $flag->isTrackEvents() || $forceReasonTracking;
52+
$trackReason = $forceReasonTracking;
5753

5854
$omitDetails = false;
5955
if ($detailsOnlyIfTracked) {
@@ -100,9 +96,9 @@ public function isValid(): bool
10096
* @param string $key the feature flag key
10197
* @return mixed the flag's value; null if the flag returned the default value, or if there was no such flag
10298
*/
103-
public function getFlagValue(string $key)
99+
public function getFlagValue(string $key): mixed
104100
{
105-
return isset($this->_flagValues[$key]) ? $this->_flagValues[$key] : null;
101+
return $this->_flagValues[$key] ?? null;
106102
}
107103

108104
/**
@@ -115,11 +111,7 @@ public function getFlagValue(string $key)
115111
*/
116112
public function getFlagReason(string $key): ?EvaluationReason
117113
{
118-
if (isset($this->_flagMetadata[$key])) {
119-
$meta = $this->_flagMetadata[$key];
120-
return isset($meta['reason']) ? $meta['reason'] : null;
121-
}
122-
return null;
114+
return ($this->_flagMetadata[$key] ?? [])['reason'] ?? null;
123115
}
124116

125117
/**
@@ -155,7 +147,7 @@ public function jsonSerialize(): array
155147
$metaMap = [];
156148
foreach ($this->_flagMetadata as $key => $meta) {
157149
$meta = array_replace([], $meta);
158-
if (isset($meta['reason'])) {
150+
if ($meta['reason'] ?? null) {
159151
$meta['reason'] = $meta['reason']->jsonSerialize();
160152
}
161153
$metaMap[$key] = $meta;

0 commit comments

Comments
 (0)