Skip to content

Commit 4b77933

Browse files
Merge pull request #22 from Palbahngmiyine/master
SOLAPI PHP SDK 5.1.0
2 parents b68825d + 1115546 commit 4b77933

36 files changed

Lines changed: 5716 additions & 81 deletions

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
22
.vscode/
3+
.env
34
log.txt
45
out/
56
*.swp
@@ -11,4 +12,11 @@ debug/
1112

1213
# Composer gitignore
1314
composer.phar
14-
/vendor/
15+
/vendor/
16+
17+
# PHPUnit
18+
.phpunit.result.cache
19+
.phpunit.cache/
20+
21+
# omc
22+
.omc/

AGENTS.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# SOLAPI PHP SDK
2+
3+
**Generated:** 2026-01-27
4+
**Commit:** a27d32f
5+
**Branch:** master
6+
7+
## OVERVIEW
8+
9+
PHP SDK for SOLAPI messaging API (SMS, LMS, MMS, Kakao Alimtalk/BMS, Voice, Fax) targeting Korean telecom. PSR-18 HTTP client abstraction, PHP 7.1+.
10+
11+
## STRUCTURE
12+
13+
```
14+
solapi-php/
15+
├── src/
16+
│ ├── Services/ # Entry point (SolapiMessageService)
17+
│ ├── Libraries/ # HTTP client, auth, utilities (4 files)
18+
│ ├── Models/
19+
│ │ ├── Request/ # API request DTOs (7 files)
20+
│ │ ├── Response/ # API response DTOs (17 files)
21+
│ │ ├── Kakao/ # Kakao options (4 files)
22+
│ │ │ └── Bms/ # Brand Message Service (14 files) ← See Bms/AGENTS.md
23+
│ │ ├── Voice/ # Voice options (3 files)
24+
│ │ └── Fax/ # Fax options (1 file)
25+
│ └── Exceptions/ # Custom exceptions (5 files)
26+
├── tests/
27+
│ ├── Models/ # Unit tests
28+
│ └── E2E/ # Integration tests
29+
├── composer.json # PSR-4: Nurigo\Solapi\ → src/
30+
└── phpunit.xml # Test configuration
31+
```
32+
33+
## WHERE TO LOOK
34+
35+
| Task | Location | Notes |
36+
|------|----------|-------|
37+
| Send messages | `Services/SolapiMessageService.php` | Main entry point, all public methods |
38+
| Build message | `Models/Message.php` | Fluent builder, extends BaseMessage |
39+
| HTTP requests | `Libraries/Fetcher.php` | Singleton, PSR-18 client |
40+
| Auth header | `Libraries/Authenticator.php` | HMAC-SHA256, static method |
41+
| HTTP transport | `Libraries/HttpClient.php` | stream_context-based, PSR-18 compliant |
42+
| Kakao Alimtalk | `Models/Kakao/KakaoOption.php` | pfId, templateId, buttons, variables |
43+
| Kakao BMS | `Models/Kakao/KakaoBms.php` | Brand messages, 8 chatBubbleTypes |
44+
| BMS validation | `Models/Kakao/Bms/BmsValidator.php` | Field requirements by type |
45+
| Voice options | `Models/Voice/VoiceOption.php` | voiceType, headerMessage, tailMessage |
46+
| Fax options | `Models/Fax/FaxOption.php` | fileIds array |
47+
| Error handling | `Exceptions/` | BaseException, HttpException, BmsValidationException |
48+
| Request DTOs | `Models/Request/` | SendRequest, GetMessagesRequest, etc. |
49+
| Response DTOs | `Models/Response/` | SendResponse, GroupMessageResponse, etc. |
50+
51+
## CODE MAP
52+
53+
**Entry Point:**
54+
```php
55+
$service = new SolapiMessageService($apiKey, $apiSecret);
56+
$response = $service->send($message);
57+
```
58+
59+
**Call Flow:**
60+
```
61+
SolapiMessageService
62+
→ Fetcher::getInstance() [singleton]
63+
→ Authenticator::getAuthorizationHeaderInfo() [static]
64+
→ NullEliminator::array_null_eliminate() [static]
65+
→ HttpClient::sendRequest() [PSR-18]
66+
→ stream_context + file_get_contents
67+
→ Response DTOs
68+
```
69+
70+
**Key Classes:**
71+
| Class | Type | Role |
72+
|-------|------|------|
73+
| `SolapiMessageService` | Service | Primary API (send, uploadFile, getMessages, getGroups, getBalance) |
74+
| `Message` | Model | Fluent builder with 12 setters |
75+
| `Fetcher` | Library | Singleton HTTP client, credential storage |
76+
| `HttpClient` | Library | PSR-18 stream-based implementation |
77+
| `Authenticator` | Library | HMAC-SHA256 auth header generation |
78+
| `NullEliminator` | Library | Recursive null removal for JSON |
79+
| `BmsValidator` | Validator | BMS field validation by chatBubbleType |
80+
81+
**Model Hierarchy:**
82+
```
83+
BaseMessage → Message (fluent builder)
84+
BaseKakaoOption → KakaoOption (fluent builder)
85+
└── KakaoBms (fluent builder, 8 types)
86+
└── Bms/* components (14 files)
87+
```
88+
89+
## CONVENTIONS
90+
91+
**Namespace:** `Nurigo\Solapi\*` (PSR-4 from `src/`)
92+
93+
**Patterns:**
94+
- Fluent builder: `$msg->setTo("...")->setFrom("...")->setText("...")`
95+
- Singleton: `Fetcher::getInstance($key, $secret)`
96+
- Public properties with getter/setter pairs on models
97+
- Korean PHPDoc comments (수신번호, 발신번호, 메시지 내용)
98+
99+
**Type Safety:**
100+
- Full type hints on method params/returns
101+
- PHPDoc `@var`, `@param`, `@return`, `@throws` annotations
102+
- PHP 7.1 compatible (no union types, no enums)
103+
104+
**Enum-Like Constants:**
105+
- `VoiceType::FEMALE`, `VoiceType::MALE`
106+
- `BmsChatBubbleType::TEXT`, `IMAGE`, `WIDE`, etc.
107+
- All have `values()` static method
108+
109+
**Tidy First (Kent Beck):**
110+
- Separate structural and behavioral changes into distinct commits
111+
- Tidy related code before making feature changes
112+
- Guard clauses, helper variables/functions, code proximity
113+
114+
## ANTI-PATTERNS
115+
116+
- **Silent null returns:** get* methods return `null` on any exception — always check response validity
117+
- **Singleton state:** Fetcher retains credentials — don't mix API keys in same process
118+
- **No interfaces:** Service/Fetcher have no contracts — mocking requires concrete class extension
119+
- **Hardcoded timezone:** `Asia/Seoul` set in Authenticator — affects global timezone
120+
- **Hardcoded country:** `"82"` default in BaseMessage — Korean-only by default
121+
122+
## UNIQUE STYLES
123+
124+
- **Korean comments:** PHPDoc descriptions in Korean
125+
- **PSR-18 via stream:** Uses `file_get_contents` + `stream_context_create`, not cURL
126+
- **Null elimination:** Removes nulls before JSON serialization
127+
128+
## COMMANDS
129+
130+
```bash
131+
# Install
132+
composer require solapi/sdk
133+
134+
# Run all tests
135+
composer test
136+
137+
# Run unit tests only
138+
composer test:unit
139+
140+
# Run E2E tests only
141+
composer test:e2e
142+
143+
# Run with coverage
144+
composer test:coverage
145+
```
146+
147+
## NOTES
148+
149+
- **Examples:** External repo at `github.com/solapi/solapi-php-examples`
150+
- **API docs:** `developers.solapi.com`
151+
- **PHP requirement:** 7.1+ (ext-json, allow_url_fopen or custom PSR-18 client)
152+
- **Dependencies:** psr/http-client, psr/http-message, nyholm/psr7
153+
- **BMS details:** See `src/Models/Kakao/Bms/AGENTS.md` for Brand Message Service specifics

CLAUDE.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
SOLAPI PHP SDK - A messaging SDK for Korean telecommunications (SMS, LMS, MMS, Kakao Alimtalk, Kakao BMS, Voice, Fax). Version 5.1.0, requires PHP 7.1+ with json extension and `allow_url_fopen` enabled (or a custom PSR-18 HTTP client).
8+
9+
**Dependencies:** PSR HTTP interfaces (psr/http-client, psr/http-message) + nyholm/psr7
10+
11+
## Commands
12+
13+
```bash
14+
# Install dependencies
15+
composer install
16+
17+
# Run all tests
18+
composer test
19+
20+
# Unit tests only (no API calls required)
21+
composer test:unit
22+
23+
# E2E tests (requires environment variables - see below)
24+
composer test:e2e
25+
26+
# Run with coverage report
27+
composer test:coverage
28+
29+
# Run a single test
30+
./vendor/bin/phpunit --filter testName
31+
```
32+
33+
### E2E Test Environment Variables
34+
35+
```bash
36+
SOLAPI_API_KEY=xxx
37+
SOLAPI_API_SECRET=xxx
38+
SOLAPI_KAKAO_PF_ID=xxx
39+
SOLAPI_SENDER_NUMBER=01012345678
40+
SOLAPI_RECIPIENT_NUMBER=01087654321
41+
```
42+
43+
## Architecture
44+
45+
**Entry Point:** `SolapiMessageService` in `src/Services/` - all public API methods
46+
47+
**Call Flow:**
48+
```
49+
SolapiMessageService → Fetcher (singleton) → Authenticator (static) → HttpClient (stream_context) → api.solapi.com
50+
```
51+
52+
**Key Classes:**
53+
- `SolapiMessageService` - Primary API: send(), uploadFile(), getMessages(), getGroups(), getBalance()
54+
- `Message` (`Models/Message.php`) - Fluent builder for message construction
55+
- `Fetcher` (`Libraries/Fetcher.php`) - Singleton HTTP client orchestrator
56+
- `HttpClient` (`Libraries/HttpClient.php`) - PSR-18 implementation using `stream_context` + `file_get_contents`
57+
- `Authenticator` (`Libraries/Authenticator.php`) - HMAC-SHA256 auth header generation
58+
59+
**Models Structure:**
60+
- `Models/Request/` - 7 request DTOs (SendRequest, GetMessagesRequest, etc.)
61+
- `Models/Response/` - 17 response DTOs (SendResponse, GroupMessageResponse, etc.)
62+
- `Models/Kakao/` - Kakao message options (pfId, templateId, buttons)
63+
- `Models/Kakao/Bms/` - Kakao Brand Message Service (14 files, 8 chatBubbleTypes) - see `src/Models/Kakao/Bms/AGENTS.md`
64+
- `Models/Voice/` - Voice message options
65+
- `Models/Fax/` - Fax message options
66+
67+
## Code Conventions
68+
69+
**Namespace:** `Nurigo\Solapi\*` (PSR-4 autoload from `src/`)
70+
71+
**Patterns:**
72+
- Fluent builder: `$msg->setTo("...")->setFrom("...")->setText("...")`
73+
- Singleton: `Fetcher::getInstance($apiKey, $apiSecret)`
74+
- Public properties with getters/setters on all model classes
75+
- Full type hints on method params/returns with PHPDoc annotations
76+
77+
**Language Notes:**
78+
- PHPDoc comments are in Korean (수신번호, 발신번호, 메시지 내용)
79+
- Default country code is "82" (Korea) in BaseMessage
80+
- Timezone hardcoded to Asia/Seoul in Authenticator
81+
82+
## Tidy First Principles
83+
84+
Follow Kent Beck's "Tidy First" principles when making code changes:
85+
86+
**Core Principles:**
87+
- **Separate Structure from Behavior**: Separate structural changes (tidying) and behavioral changes (features) into distinct commits
88+
- **Tidy First**: Tidy related code before making feature changes to improve changeability
89+
- **Small Steps**: Keep tidying work completable within minutes to hours
90+
91+
**Practical Techniques:**
92+
- Use guard clauses for early returns to eliminate nested if statements
93+
- Use helper variables/functions to clarify complex expressions
94+
- Keep related code physically close together
95+
- Express identical logic in identical ways (normalize symmetry)
96+
- Delete unused code immediately
97+
98+
**When to Apply:**
99+
- Before adding new features, tidy the affected area
100+
- Before fixing bugs, clarify related code
101+
- During code review, identify tidying opportunities
102+
103+
## Important Behaviors
104+
105+
- **Singleton State:** Fetcher singleton retains credentials - don't mix different API keys in the same process
106+
- **Null Returns:** Many get* methods return `null` on any exception instead of throwing - always check response validity
107+
- **No Interfaces:** Service/Fetcher lack contracts - mocking requires concrete class extension
108+
- **PSR-18 HTTP Client:** Default HttpClient uses `stream_context` + `file_get_contents`. A custom PSR-18 client can be injected if needed (e.g., for cURL or Guzzle)
109+
- **SSL Verification:** Enabled by default in HttpClient; can be disabled via constructor options
110+
111+
## External Resources
112+
113+
- API Documentation: https://developers.solapi.com
114+
- Examples Repository: https://github.com/solapi/solapi-php-examples

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@ You can send text messages(SMS, LMS, MMS), Kakao friendtalk(include notification
44
package.
55
This package is 100% compatible with SOLAPI family services (Purple Book, Nurigo, etc.).
66

7+
## Requirements
8+
9+
- PHP 7.1 or higher
10+
- `ext-json` extension
11+
- `allow_url_fopen` enabled in php.ini (for the default HTTP client)
12+
13+
> **Note:** If `allow_url_fopen` is disabled in your environment (common in shared hosting), you can inject a custom PSR-18 HTTP client (e.g., Guzzle) via the `Fetcher` constructor or `getInstance()` method.
14+
715
## Installing
816

9-
To use the SDK, simply use npm package manager CLI. Type the following into a terminal window.
17+
To use the SDK, simply use Composer. Type the following into a terminal window.
1018

1119
```bash
1220
composer require solapi/sdk

composer.json

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
{
22
"name": "solapi/sdk",
33
"description": "SOLAPI SDK for PHP",
4-
"version": "5.0.6",
4+
"version": "5.1.0",
55
"type": "library",
66
"license": "MIT",
77
"autoload": {
88
"psr-4": {
99
"Nurigo\\Solapi\\": "src/"
1010
}
1111
},
12+
"autoload-dev": {
13+
"psr-4": {
14+
"Nurigo\\Solapi\\Tests\\": "tests/"
15+
}
16+
},
1217
"repositories": [
1318
{
1419
"type": "vcs",
@@ -41,7 +46,18 @@
4146
"homepage": "https://solapi.com",
4247
"require": {
4348
"php": ">=7.1",
44-
"ext-curl": "*",
45-
"ext-json": "*"
49+
"ext-json": "*",
50+
"psr/http-client": "^1.0",
51+
"psr/http-message": "^1.0 || ^2.0",
52+
"nyholm/psr7": "^1.5"
53+
},
54+
"require-dev": {
55+
"phpunit/phpunit": "^9.5"
56+
},
57+
"scripts": {
58+
"test": "phpunit",
59+
"test:unit": "phpunit --testsuite=Unit",
60+
"test:e2e": "phpunit --testsuite=E2E",
61+
"test:coverage": "phpunit --coverage-text"
4662
}
4763
}

0 commit comments

Comments
 (0)