Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0dc65a9
feat: update by openapi refactor
mogita Feb 16, 2026
ca6304b
fix: map missing fields to null with fromJson()
mogita Feb 16, 2026
aa920ec
feat: update by openapi refactor
mogita Feb 17, 2026
9bacb4a
feat: update by openapi refactor
mogita Feb 17, 2026
be430a7
fix: correct array for map
mogita Feb 19, 2026
42bc228
style: clean up empty comments
mogita Feb 19, 2026
c1e0e43
feat: update argument position for constructors
mogita Feb 19, 2026
d98e987
chore: bump jwt to v7 and commit composer.lock
mogita Feb 19, 2026
5a9c36e
feat: stablize constructor parameter ordering
mogita Feb 20, 2026
ca58c20
feat: update by openapi refactor
mogita Feb 24, 2026
ac5b0e5
feat: add chat integration test helpers and base test case
mogita Feb 24, 2026
baa8d47
feat: add all 16 user integration tests for chat SDK parity
mogita Feb 24, 2026
0a52a0a
feat: add channel integration tests (32 tests)
mogita Feb 24, 2026
2ecd94f
feat: add message integration tests (27 tests)
mogita Feb 24, 2026
4e1f916
feat: add chat reaction integration tests (send, get, delete, enforce…
mogita Feb 24, 2026
c6fabc4
feat: add chat polls integration tests (create, query, vote)
mogita Feb 24, 2026
9fa7c5a
feat: add misc chat integration tests (devices, blocklists, commands,…
mogita Feb 24, 2026
12fee41
feat: add moderation integration tests (ban/unban, mute/unmute, flag …
mogita Feb 24, 2026
0d5551a
feat: add video integration tests (17 tests)
mogita Feb 24, 2026
9b0b48f
test: fix queryUsers, Guzzle error handling and more
mogita Feb 25, 2026
df0af38
test: set test groups properly
mogita Feb 25, 2026
d19bd15
fix: add eventual consistency handling for integration tests
mogita Feb 25, 2026
72fd6cf
test: running time optimization
mogita Feb 25, 2026
b3a3ab7
test: improve wait intervals
mogita Feb 25, 2026
acb088e
test: remove offset and sort case
mogita Feb 25, 2026
881896d
test: fine tuning
mogita Feb 25, 2026
514aaf5
test: fine tuning
mogita Feb 25, 2026
fb8bdab
test: parallel testing
mogita Feb 26, 2026
ebfdbda
test: fine tuning
mogita Feb 26, 2026
2c55dcc
test: fine tuning
mogita Feb 26, 2026
d13d123
test: fine tuning
mogita Feb 26, 2026
a88c2c7
test: fine tuning
mogita Feb 26, 2026
812fa5b
test: update parallel testing
mogita Feb 26, 2026
668bdf1
test: fine tuning
mogita Feb 26, 2026
66d8e99
chore: set platform version to php 8.1
mogita Feb 26, 2026
32e22e2
test: fine tuning
mogita Feb 26, 2026
7b5cc04
test: fine tuning
mogita Feb 26, 2026
2d821d2
test: run parallel unit test and one integration test
mogita Feb 27, 2026
a2479d5
test: fine tuning
mogita Feb 27, 2026
cb5dc00
ci: add pre-release workflow and update changelog
mogita Mar 2, 2026
df2893a
test: fine tuning
mogita Mar 2, 2026
8273787
ci: specify the environment
mogita Mar 2, 2026
20f49c9
ci: update parameter format for packagist
mogita Mar 2, 2026
0a1008a
feat: update by openapi refactor
mogita Mar 4, 2026
bdd7a82
feat: update by openapi refactor
mogita Mar 4, 2026
d625025
feat: update by openapi refactor
mogita Mar 4, 2026
4965d6b
test: chat and video clients
mogita Mar 5, 2026
4c094bc
test: fine tuning
mogita Mar 5, 2026
c993bef
feat: error handling on encoding failure
mogita Mar 5, 2026
5c44be9
feat: update by openapi refactor
mogita Mar 5, 2026
ee295e3
test: fine tuning
mogita Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
24 changes: 19 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: ci

on:
on:
pull_request:
push:
branches: [master, main]

concurrency:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true

Expand All @@ -14,7 +14,7 @@ jobs:
name: 🧪 Test & lint
environment: ci
runs-on: ubuntu-latest

strategy:
matrix:
php-version: ['8.1', '8.2', '8.3']
Expand All @@ -29,13 +29,27 @@ jobs:
extensions: curl, json
tools: composer:v2

- name: Cache composer dependencies
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ matrix.php-version }}-${{ hashFiles('composer.lock') }}
restore-keys: composer-${{ matrix.php-version }}-

- name: Install dependencies
run: composer install --prefer-dist --no-interaction

- name: Test
- name: Lint & unit tests
env:
STREAM_API_KEY: ${{ vars.STREAM_API_KEY }}
STREAM_API_SECRET: ${{ secrets.STREAM_API_SECRET }}
run: |
make lint
make test
make test-unit

- name: Integration tests
if: matrix.php-version == '8.1'
env:
STREAM_API_KEY: ${{ vars.STREAM_API_KEY }}
STREAM_API_SECRET: ${{ secrets.STREAM_API_SECRET }}
run: make test-integration
47 changes: 47 additions & 0 deletions .github/workflows/prerelease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Pre-release

on:
release:
types: [prereleased]

jobs:
prerelease:
name: 🚀 Pre-release
environment: ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Update Packagist
env:
PACKAGIST_TOKEN: ${{ secrets.PACKAGIST_TOKEN }}
PACKAGIST_USERNAME: ${{ vars.PACKAGIST_USERNAME }}
run: |
if [ -z "$PACKAGIST_TOKEN" ]; then
echo "⚠️ PACKAGIST_TOKEN secret is not set. Skipping Packagist update."
exit 0
fi

if [ -z "$PACKAGIST_USERNAME" ]; then
echo "⚠️ PACKAGIST_USERNAME var is not set. Skipping Packagist update."
exit 0
fi

echo "🔄 Updating Packagist package..."
response=$(curl -s -w "\n%{http_code}" -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PACKAGIST_USERNAME:$PACKAGIST_TOKEN" \
-d '{"repository":{"url":"https://github.com/GetStream/getstream-php"}}' \
"https://packagist.org/api/update-package")

http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')

if [ "$http_code" -eq 200 ] || [ "$http_code" -eq 202 ]; then
echo "✅ Packagist update triggered successfully"
echo "$body"
else
echo "❌ Failed to update Packagist (HTTP $http_code)"
echo "$body"
exit 1
fi
5 changes: 3 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
jobs:
release:
name: 🚀 Release
environment: ci
if: github.event.pull_request.merged && startsWith(github.head_ref, 'release-')
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -53,9 +54,9 @@ jobs:
echo "🔄 Updating Packagist package..."
response=$(curl -s -w "\n%{http_code}" -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PACKAGIST_TOKEN" \
-H "Authorization: Bearer $PACKAGIST_USERNAME:$PACKAGIST_TOKEN" \
-d '{"repository":{"url":"https://github.com/GetStream/getstream-php"}}' \
"https://packagist.org/api/update-package?username=$PACKAGIST_USERNAME")
"https://packagist.org/api/update-package")

http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
Expand Down
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# Dependencies
/vendor/

# Composer
composer.lock

# IDE files
.idea/
.vscode/
Expand Down Expand Up @@ -34,4 +31,4 @@ logs/
# src/Models/
# src/Requests/
# src/Generated/
# composer-generated.json
# composer-generated.json
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [4.0.0-beta.1] - 2026-02-27

### Breaking Changes

- Type names across all products now follow the OpenAPI spec naming convention: response types are suffixed with `Response`, input types with `Request`. See [MIGRATION_v3_to_v4.md](./MIGRATION_v3_to_v4.md) for the complete rename mapping.
- `Event` (WebSocket envelope type) renamed to `WSEvent`. Base event type renamed from `BaseEvent` to `Event` (with field `type` instead of `T`).
- Event composition changed from monolithic `*Preset` embeds to modular `Has*` types.
- `Pager` renamed to `PagerResponse` and migrated from offset-based to cursor-based pagination (`next`/`prev` tokens).

### Added

- Full product coverage: Chat, Video, Moderation, and Feeds APIs are all supported in a single SDK.
- **Feeds**: activities, feeds, feed groups, follows, comments, reactions, collections, bookmarks, membership levels, feed views, and more.
- **Video**: calls, recordings, transcription, closed captions, SFU, call statistics, user feedback analytics, and more.
- **Moderation**: flags, review queue, moderation rules, config, appeals, moderation logs, and more.
- Push notification types, preferences, and templates.
- Webhook support: `WHEvent` envelope class for receiving webhook payloads, utility functions for decoding and verifying webhook signatures, and a full set of individual typed event classes for every event across all products (Chat, Video, Moderation, Feeds) usable as discriminated event types.
- Cursor-based pagination across all list endpoints.

## [3.0.0] - 2026-02-03

## [2.1.0] - 2026-01-15

## [2.0.2] - 2025-12-11

## [2.0.0] - 2025-09-30
202 changes: 202 additions & 0 deletions MIGRATION_v3_to_v4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# Migration Guide: v3 → v4

This guide covers all breaking changes when upgrading from `getstream-php` v3 to v4.

## Overview

v4 is a full OpenAPI-aligned release. The primary change is a **systematic type renaming**: classes that appear in API responses now have a `Response` suffix, and input classes have a `Request` suffix. There are no removed features — all functionality from v3 is available in v4. Additionally, v4 adds complete coverage of the **Feeds**, **Video**, and **Moderation** product APIs.

## Installation

```bash
composer require getstream/getstream-php:^4.0
```

## Naming Conventions

- **Classes**: `PascalCase` (e.g., `UserResponse`, `MessageRequest`)
- **Properties/fields**: `snake_case` (e.g., `$user->created_at`, `$message->reply_count`)

The general renaming rules:

- Classes returned in API responses: `Foo` → `FooResponse`
- Classes used as API inputs: `Foo` → `FooRequest`
- Some moderation action payloads: `FooRequest` → `FooRequestPayload`

## Breaking Changes

### Common / Shared Types

| v3 | v4 | Notes |
| --- | --- | --- |
| `ApplicationConfig` | `AppResponseFields` | App configuration in responses |
| `ChannelPushPreferences` | `ChannelPushPreferencesResponse` | Per-channel push settings |
| `Device` | `DeviceResponse` | Device data (push, voip) |
| `Event` | `WSEvent` | WebSocket event envelope |
| `FeedsPreferences` | `FeedsPreferencesResponse` | Feeds push preferences |
| `ImportV2Task` | `ImportV2TaskItem` | V2 import task |
| `OwnUser` | `OwnUserResponse` | Authenticated user data |
| `Pager` | `PagerResponse` | Now cursor-based (`next`/`prev`) |
| `PushPreferences` | `PushPreferencesResponse` | Push preferences |
| `PushTemplate` | `PushTemplateResponse` | Push template |
| `PrivacySettings` | `PrivacySettingsResponse` | Typing indicators, read receipts |
| `RateLimitInfo` | `LimitInfoResponse` | Rate limit info |
| `SortParam` | `SortParamRequest` | Sort parameter for queries |
| `User` | `UserResponse` | Full user in responses |
| `UserBlock` | `BlockedUserResponse` | Blocked user details |
| `UserCustomEvent` | `CustomEvent` | Custom user event |
| `UserMute` | `UserMuteResponse` | User mute details |

### Event System

| Before (v3) | After (v4) | Notes |
| --- | --- | --- |
| `BaseEvent` (field `T`) | `Event` (field `type`) | Base event type |
| `Event` (WS envelope) | `WSEvent` | WebSocket event wrapper |
| `*Preset` embeds | `Has*` composition types | e.g., `HasChannel`, `HasMessage` |
| — | `WHEvent` | New webhook envelope type |

### Chat Types

| v3 | v4 | Notes |
| --- | --- | --- |
| `Campaign` | `CampaignResponse` | |
| `CampaignStats` | `CampaignStatsResponse` | |
| `Channel` | `ChannelResponse` | |
| `ChannelConfigFields` | `ChannelConfigWithInfo` | Channel config + commands/grants |
| `ChannelMember` | `ChannelMemberResponse` | |
| `ChannelTypeConfigWithInfo` | `ChannelTypeConfig` | |
| `ConfigOverrides` | `ConfigOverridesRequest` | |
| `DraftMessage` / `DraftMessagePayload` | `DraftResponse` | Two classes merged into one |
| `Message` | `MessageResponse` | |
| `MessageReminder` | `ReminderResponseData` | |
| `PendingMessage` | `PendingMessageResponse` | |
| `Poll` | `PollResponse` | |
| `PollOption` | `PollOptionResponse` | |
| `PollVote` | `PollVoteResponse` | |
| `Reaction` | `ReactionResponse` | |
| `ReadState` | `ReadStateResponse` | |
| `Thread` | `ThreadResponse` | |

### Video Types

| v3 | v4 | Notes |
| --- | --- | --- |
| `AudioSettings` | `AudioSettingsResponse` | |
| `BackstageSettings` | `BackstageSettingsResponse` | |
| `BroadcastSettings` | `BroadcastSettingsResponse` | |
| `Call` | `CallResponse` | |
| `CallEgress` | `EgressResponse` | |
| `CallMember` | `MemberResponse` | Note: not `CallMemberResponse` |
| `CallParticipant` | `CallParticipantResponse` | |
| `CallParticipantFeedback` | *(removed)* | Use `CollectUserFeedbackRequest` |
| `CallSession` | `CallSessionResponse` | |
| `CallSettings` | `CallSettingsResponse` | |
| `CallType` | `CallTypeResponse` | |
| `EventNotificationSettings` | `EventNotificationSettingsResponse` | |
| `FrameRecordSettings` | `FrameRecordingSettingsResponse` | `Recording` inserted in name |
| `GeofenceSettings` | `GeofenceSettingsResponse` | |
| `HLSSettings` | `HLSSettingsResponse` | |
| `IndividualRecordSettings` | `IndividualRecordingSettingsResponse` | `Recording` inserted in name |
| `IngressSettings` | `IngressSettingsResponse` | |
| `IngressSource` | `IngressSourceResponse` | |
| `IngressAudioEncodingOptions` | `IngressAudioEncodingResponse` | Shortened name |
| `IngressVideoEncodingOptions` | `IngressVideoEncodingResponse` | Shortened name |
| `IngressVideoLayer` | `IngressVideoLayerResponse` | |
| `LimitsSettings` | `LimitsSettingsResponse` | |
| `NotificationSettings` | `NotificationSettingsResponse` | |
| `RawRecordSettings` | `RawRecordingSettingsResponse` | `Recording` inserted in name |
| `RecordSettings` | `RecordSettingsResponse` | |
| `RingSettings` | `RingSettingsResponse` | |
| `RTMPSettings` | `RTMPSettingsResponse` | |
| `ScreensharingSettings` | `ScreensharingSettingsResponse` | |
| `SessionSettings` | `SessionSettingsResponse` | |
| `SIPCallConfigs` | `SIPCallConfigsResponse` | |
| `SIPCallerConfigs` | `SIPCallerConfigsResponse` | |
| `SIPDirectRoutingRuleCallConfigs` | `SIPDirectRoutingRuleCallConfigsResponse` | |
| `SIPInboundRoutingRules` | `SIPInboundRoutingRuleResponse` | Plural → singular |
| `SIPPinProtectionConfigs` | `SIPPinProtectionConfigsResponse` | |
| `SIPTrunk` | `SIPTrunkResponse` | |
| `ThumbnailsSettings` | `ThumbnailsSettingsResponse` | |
| `TranscriptionSettings` | `TranscriptionSettingsResponse` | |
| `VideoSettings` | `VideoSettingsResponse` | |

### Moderation Types

| v3 | v4 | Notes |
| --- | --- | --- |
| `ActionLog` | `ActionLogResponse` | |
| `Appeal` | `AppealItemResponse` | |
| `AutomodDetails` | `AutomodDetailsResponse` | |
| `Ban` | `BanInfoResponse` | |
| `BanOptions` | *(removed)* | Merged into `BanActionRequestPayload` |
| `BanActionRequest` | `BanActionRequestPayload` | |
| `BlockActionRequest` | `BlockActionRequestPayload` | |
| `BlockedMessage` | *(removed)* | Internal only |
| `CustomActionRequest` | `CustomActionRequestPayload` | |
| `DeleteMessageRequest` | `DeleteMessageRequestPayload` | |
| `DeleteUserRequest` | `DeleteUserRequestPayload` | |
| `EntityCreator` | `EntityCreatorResponse` | |
| `Evaluation` | `EvaluationResponse` | |
| `FeedsModerationTemplate` | `QueryFeedModerationTemplate` | No `Response` suffix |
| `FeedsModerationTemplateConfig` | `FeedsModerationTemplateConfigPayload` | |
| `Flag` | *(removed)* | Use `ModerationFlagResponse` |
| `Flag2` | `ModerationFlagResponse` | |
| `FlagDetails` | `FlagDetailsResponse` | |
| `FlagFeedback` | `FlagFeedbackResponse` | |
| `FlagMessageDetails` | `FlagMessageDetailsResponse` | |
| `FlagReport` | *(removed)* | Internal only |
| `FutureChannelBan` | `FutureChannelBanResponse` | |
| `MarkReviewedRequest` | `MarkReviewedRequestPayload` | |
| `Match` | `MatchResponse` | |
| `ModerationActionConfig` | `ModerationActionConfigResponse` | |
| `ModerationBulkSubmitActionRequest` | `BulkSubmitActionRequest` | `Moderation` prefix dropped |
| `ModerationConfig` | `ConfigResponse` | |
| `ModerationFlags` | *(removed)* | Use array of `ModerationFlagResponse` |
| `ModerationLog` | *(removed)* | Use `ActionLogResponse` |
| `ModerationLogResponse` | *(removed)* | Use `QueryModerationLogsResponse` |
| `ModerationUsageStats` | `ModerationUsageStatsResponse` | |
| `RestoreActionRequest` | `RestoreActionRequestPayload` | |
| `ReviewQueueItem` | `ReviewQueueItemResponse` | |
| `Rule` | `RuleResponse` | |
| `ShadowBlockActionRequest` | `ShadowBlockActionRequestPayload` | |
| `Task` | `TaskResponse` | |
| `Trigger` | `TriggerResponse` | |
| `UnbanActionRequest` | `UnbanActionRequestPayload` | |
| `UnblockActionRequest` | `UnblockActionRequestPayload` | |
| `VideoEndCallRequest` | `VideoEndCallRequestPayload` | |
| `VideoKickUserRequest` | `VideoKickUserRequestPayload` | |

### Feeds Types

| v3 | v4 | Notes |
| --- | --- | --- |
| `Activity` | `ActivityResponse` | |
| `ActivityFeedback` | `ActivityFeedbackRequest` | Request-only (no `Response` suffix) |
| `ActivityMark` | `MarkActivityRequest` | |
| `ActivityPin` | `ActivityPinResponse` | |
| `AggregatedActivity` | `AggregatedActivityResponse` | |
| `Bookmark` | `BookmarkResponse` | |
| `BookmarkFolder` | `BookmarkFolderResponse` | |
| `Collection` | `CollectionResponse` | |
| `Comment` | `CommentResponse` | |
| `CommentMedia` | *(removed)* | Embedded inline in `CommentResponse` |
| `CommentMention` | *(removed)* | Embedded inline in `CommentResponse` |
| `DenormalizedFeedsReaction` | *(removed)* | Internal only |
| `Feed` | `FeedResponse` | |
| `FeedGroup` | `FeedGroupResponse` | |
| `FeedMember` | `FeedMemberResponse` | |
| `FeedsReaction` | `FeedsReactionResponse` | |
| `FeedsReactionGroup` | `FeedsReactionGroupResponse` | |
| `FeedSuggestion` | `FeedSuggestionResponse` | |
| `FeedView` | `FeedViewResponse` | |
| `FeedVisibilityInfo` | `FeedVisibilityResponse` | |
| `Follow` | `FollowResponse` | |
| `MembershipLevel` | `MembershipLevelResponse` | |
| `ThreadedComment` | `ThreadedCommentResponse` | |

## Getting Help

- [Stream documentation](https://getstream.io/docs/)
- [GitHub Issues](https://github.com/GetStream/getstream-php/issues)
- [Stream support](https://getstream.io/contact/support/)
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ test: test-unit test-integration ## Run all tests
test-unit: ## Run unit tests only
./vendor/bin/phpunit tests --exclude-group integration

test-integration: ## Run integration tests only
./vendor/bin/phpunit tests/Integration/
test-integration: ## Run integration tests in parallel (8 workers, method-level)
./vendor/bin/paratest --processes=8 --runner=WrapperRunner --colors tests/Integration/

test-specific: ## Run a specific test (usage: make test-specific TEST=TestClassName::testMethodName)
./vendor/bin/phpunit --filter $(TEST)
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ echo "Duration: " . $response->duration;

## Models

> **Note:** When constructing models directly, always use **named arguments** (e.g. `new Message(text: 'hello')`).
> Positional argument usage is not supported and may break across SDK updates as parameter order is not guaranteed.

### Automatic JSON Parsing

Generated models automatically handle JSON parsing and serialization:
Expand Down
Loading