Skip to content

Commit e43fbbc

Browse files
Add write protection with examples and tests (#115)
* Add write protection with examples and tests * post review * Linter * PubNub SDK 7.4.0 release. --------- Co-authored-by: PubNub Release Bot <120067856+pubnub-release-bot@users.noreply.github.com>
1 parent c476491 commit e43fbbc

22 files changed

+543
-163
lines changed

.pubnub.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
name: php
2-
version: 7.3.0
2+
version: 7.4.0
33
schema: 1
44
scm: github.com/pubnub/php
55
changelog:
6+
- date: 2025-02-18
7+
version: 7.4.0
8+
changes:
9+
- type: feature
10+
text: "Write protection with If-Match eTag header for setting channel and uuid metadata."
611
- date: 2025-02-05
712
version: 7.3.0
813
changes:
@@ -447,8 +452,8 @@ sdks:
447452
- x86-64
448453
- distribution-type: library
449454
distribution-repository: GitHub release
450-
package-name: php-7.3.0.zip
451-
location: https://github.com/pubnub/php/releases/tag/7.3.0
455+
package-name: php-7.4.0.zip
456+
location: https://github.com/pubnub/php/releases/tag/7.4.0
452457
requires:
453458
- name: rmccue/requests
454459
min-version: 1.0.0

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 7.4.0
2+
February 18 2025
3+
4+
#### Added
5+
- Write protection with If-Match eTag header for setting channel and uuid metadata.
6+
17
## 7.3.0
28
February 05 2025
39

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ You will need the publish and subscribe keys to authenticate your app. Get your
4141
{
4242
"require": {
4343
<!-- include the latest version from the badge at the top -->
44-
"pubnub/pubnub": "7.3.0"
44+
"pubnub/pubnub": "7.4.0"
4545
}
4646
}
4747
```

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"keywords": ["api", "real-time", "realtime", "real time", "ajax", "push"],
66
"homepage": "http://www.pubnub.com/",
77
"license": "proprietary",
8-
"version": "7.3.0",
8+
"version": "7.4.0",
99
"authors": [
1010
{
1111
"name": "PubNub",

examples/cli/partial_updates.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
set_time_limit(0);
4+
5+
require('../../vendor/autoload.php');
6+
7+
use PubNub\PNConfiguration;
8+
use PubNub\PubNub;
9+
10+
$pnconf = new PNConfiguration();
11+
$pnconf->setPublishKey("demo");
12+
$pnconf->setSubscribeKey("demo");
13+
$pnconf->setUuid("example");
14+
15+
$pubnub = new PubNub($pnconf);
16+
17+
$channel = "demo_example";
18+
19+
print("We're setting the channel's $channel additional info.\n");
20+
print("\tTo exit type '/exit'\n");
21+
print("\tTo show the current object type '/show'\n");
22+
print("\tTo show this help type '/help'\n");
23+
24+
print("Enter the channel name: ");
25+
$name = trim(fgets(STDIN));
26+
27+
print("Enter the channel description: ");
28+
$description = trim(fgets(STDIN));
29+
30+
// Set channel metadata
31+
$pubnub->setChannelMetadata()
32+
->channel($channel)
33+
->meta([
34+
"name" => $name,
35+
"description" => $description,
36+
])
37+
->sync();
38+
39+
print("The channel has been created with name and description.\n");
40+
41+
while (true) {
42+
// Fetch current channel metadata
43+
$response = $pubnub->getChannelMetadata()
44+
->channel($channel)
45+
->sync();
46+
47+
print("Enter the field name: ");
48+
$fieldName = trim(fgets(STDIN));
49+
50+
if ($fieldName === '/exit') {
51+
exit();
52+
} elseif ($fieldName === '/show') {
53+
print_r($response);
54+
continue;
55+
} elseif ($fieldName === '/help') {
56+
print("\tTo exit type '/exit'\n");
57+
print("\tTo show the current object type '/show'\n");
58+
print("\tTo show this help type '/help'\n");
59+
continue;
60+
}
61+
62+
print("Enter the field value: ");
63+
$fieldValue = trim(fgets(STDIN));
64+
65+
// Prepare custom fields
66+
$custom = (array)$response->getCustom();
67+
68+
if (isset($custom[$fieldName])) {
69+
print("Field $fieldName already has a value. Overwrite? (y/n): ");
70+
$confirmation = trim(fgets(STDIN));
71+
72+
if (strtolower($confirmation) !== 'y') {
73+
print("Object will not be updated.\n");
74+
continue;
75+
}
76+
}
77+
78+
// Update custom field
79+
$custom[$fieldName] = $fieldValue;
80+
81+
// Writing the updated object back to the server
82+
$pubnub->setChannelMetadata()
83+
->channel($channel)
84+
->meta([
85+
"name" => $response->getName(),
86+
"description" => $response->getDescription(),
87+
"custom" => $custom,
88+
])
89+
->sync();
90+
print("Object has been updated.\n");
91+
}

examples/cli/pn.jpg

1.71 KB
Loading

examples/cli/using_etag.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
require_once 'vendor/autoload.php';
4+
5+
use PubNub\Exceptions\PubNubServerException;
6+
use PubNub\PubNub;
7+
use PubNub\PNConfiguration;
8+
9+
$config = new PNConfiguration();
10+
$config->setPublishKey('demo');
11+
$config->setSubscribeKey('demo');
12+
$config->setUuid("example");
13+
14+
$config_2 = clone $config;
15+
$config_2->setUuid("example_2");
16+
17+
$pubnub = new PubNub($config);
18+
$pubnub_2 = new PubNub($config_2);
19+
20+
$sample_user = [
21+
"uuid" => "SampleUser",
22+
"name" => "John Doe",
23+
"email" => "jd@example.com",
24+
"custom" => ["age" => 42, "address" => "123 Main St."]
25+
];
26+
27+
// One client creates a metadata for the user "SampleUser" and successfully writes it to the server.
28+
$set_result = $pubnub->setUUIDMetadata()->uuid("SampleUser")->meta($sample_user)->sync();
29+
30+
// We store the eTag for the user for further updates.
31+
$original_e_tag = $set_result->getETag();
32+
33+
print("We receive the eTag for the user: $original_e_tag" . PHP_EOL);
34+
35+
// Another client sets the user meta with the same UUID but different data.
36+
$overwrite_result = $pubnub_2->setUUIDMetadata()->uuid("SampleUser")->meta(["name" => "Jane Doe"])->sync();
37+
$new_e_tag = $overwrite_result->getETag();
38+
39+
// We can verify that there is a new eTag for the user.
40+
print(
41+
"After overwrite there's a new eTag: $original_e_tag === $new_e_tag? "
42+
. ($original_e_tag === $new_e_tag ? "true" : "false") . PHP_EOL
43+
);
44+
45+
// We modify the user and try to update it.
46+
$updated_user = array_merge($sample_user, ["custom" => ["age" => 43, "address" => "321 Other St."]]);
47+
48+
try {
49+
$update_result = $pubnub->setUUIDMetadata()
50+
->uuid("SampleUser")
51+
->meta($updated_user)
52+
->ifMatchesETag($original_e_tag)
53+
->sync();
54+
print_r($update_result);
55+
} catch (PubNubServerException $e) {
56+
if ($e->getStatusCode() === 412) {
57+
print(
58+
"Update failed because eTag mismatch: " . $e->getServerErrorMessage()
59+
. "\nHTTP Status Code: " . $e->getStatusCode() . PHP_EOL
60+
);
61+
} else {
62+
print( "Unexpected error: " . $e->getMessage() . PHP_EOL);
63+
}
64+
} catch (Exception $e) {
65+
echo "Unexpected error: " . $e->getMessage() . PHP_EOL;
66+
}

phpstan-baseline.neon

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3326,21 +3326,6 @@ parameters:
33263326
count: 1
33273327
path: src/PubNub/Models/Consumer/Objects/Channel/PNGetAllChannelMetadataResult.php
33283328

3329-
-
3330-
message: "#^Undefined variable\\: \\$data$#"
3331-
count: 1
3332-
path: src/PubNub/Models/Consumer/Objects/Channel/PNGetAllChannelMetadataResult.php
3333-
3334-
-
3335-
message: "#^Undefined variable\\: \\$data_string$#"
3336-
count: 1
3337-
path: src/PubNub/Models/Consumer/Objects/Channel/PNGetAllChannelMetadataResult.php
3338-
3339-
-
3340-
message: "#^Variable \\$data in empty\\(\\) is never defined\\.$#"
3341-
count: 1
3342-
path: src/PubNub/Models/Consumer/Objects/Channel/PNGetAllChannelMetadataResult.php
3343-
33443329
-
33453330
message: "#^Method PubNub\\\\Models\\\\Consumer\\\\Objects\\\\Channel\\\\PNGetChannelMetadataResult\\:\\:__construct\\(\\) has parameter \\$custom with no value type specified in iterable type array\\.$#"
33463331
count: 1

src/PubNub/Endpoints/Endpoint.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace PubNub\Endpoints;
44

5+
use Exception;
56
use PubNub\Enums\PNOperationType;
67
use PubNub\Enums\PNHttpMethod;
78
use PubNub\Enums\PNStatusCategory;
@@ -32,6 +33,9 @@ abstract class Endpoint
3233
protected int $endpointOperationType;
3334
protected string $endpointName;
3435

36+
/** @var string[] */
37+
protected array $customHeaders = [];
38+
3539
protected const RESPONSE_IS_JSON = true;
3640

3741
/** @var PubNub */
@@ -178,7 +182,7 @@ protected function defaultHeaders()
178182

179183
protected function customHeaders()
180184
{
181-
return [];
185+
return $this->customHeaders;
182186
}
183187

184188
/**

src/PubNub/Endpoints/Objects/Channel/SetChannelMetadata.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PubNub\Endpoints\Objects\Channel;
44

55
use PubNub\Endpoints\Endpoint;
6+
use PubNub\Endpoints\Objects\MatchesETagTrait;
67
use PubNub\Enums\PNHttpMethod;
78
use PubNub\Enums\PNOperationType;
89
use PubNub\Exceptions\PubNubValidationException;
@@ -11,6 +12,8 @@
1112

1213
class SetChannelMetadata extends Endpoint
1314
{
15+
use MatchesETagTrait;
16+
1417
protected const PATH = "/v2/objects/%s/channels/%s";
1518

1619
/** @var string */
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace PubNub\Endpoints\Objects;
4+
5+
trait MatchesETagTrait
6+
{
7+
protected ?string $eTag = null;
8+
protected array $customHeaders = [];
9+
10+
/**
11+
* @param string $eTag
12+
* @return $this
13+
*/
14+
public function ifMatchesETag(string $eTag): self
15+
{
16+
$this->eTag = $eTag;
17+
$this->customHeaders['If-Match'] = $eTag;
18+
return $this;
19+
}
20+
}

src/PubNub/Endpoints/Objects/ObjectsCollectionEndpoint.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,4 @@ public function sort($sort)
6161

6262
return $this;
6363
}
64-
65-
}
64+
}

src/PubNub/Endpoints/Objects/UUID/SetUUIDMetadata.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PubNub\Endpoints\Objects\UUID;
44

55
use PubNub\Endpoints\Endpoint;
6+
use PubNub\Endpoints\Objects\MatchesETagTrait;
67
use PubNub\Enums\PNHttpMethod;
78
use PubNub\Enums\PNOperationType;
89
use PubNub\Exceptions\PubNubValidationException;
@@ -11,6 +12,8 @@
1112

1213
class SetUUIDMetadata extends Endpoint
1314
{
15+
use MatchesETagTrait;
16+
1417
protected const PATH = "/v2/objects/%s/uuids/%s";
1518

1619
/** @var string */

0 commit comments

Comments
 (0)