Skip to content

Commit 7fdaaa6

Browse files
authored
Merge pull request #7 from json-structure/copilot/add-php-sdk-implementation
feat(php): Add PHP SDK implementation
2 parents e849506 + 805be9b commit 7fdaaa6

28 files changed

Lines changed: 13144 additions & 0 deletions

.github/workflows/php.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: PHP SDK
2+
3+
on:
4+
push:
5+
branches: [master, main]
6+
paths:
7+
- 'php/**'
8+
- '.github/workflows/php.yml'
9+
tags:
10+
- 'v[0-9]+.[0-9]+.[0-9]+'
11+
pull_request:
12+
branches: [master, main]
13+
paths:
14+
- 'php/**'
15+
- '.github/workflows/php.yml'
16+
17+
permissions:
18+
contents: read
19+
20+
jobs:
21+
test:
22+
name: Test PHP ${{ matrix.php-version }}
23+
runs-on: ubuntu-latest
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
php-version: ['8.1', '8.2', '8.3']
28+
29+
steps:
30+
- uses: actions/checkout@v4
31+
with:
32+
submodules: recursive
33+
34+
- name: Set up PHP ${{ matrix.php-version }}
35+
uses: shivammathur/setup-php@v2
36+
with:
37+
php-version: ${{ matrix.php-version }}
38+
extensions: bcmath, json, mbstring
39+
coverage: xdebug
40+
41+
- name: Validate composer.json
42+
working-directory: php
43+
run: composer validate --strict
44+
45+
- name: Install dependencies
46+
working-directory: php
47+
run: composer install --prefer-dist --no-progress
48+
49+
- name: Run tests
50+
working-directory: php
51+
run: vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml
52+
53+
- name: Upload coverage reports
54+
if: matrix.php-version == '8.2'
55+
uses: codecov/codecov-action@v4
56+
with:
57+
file: php/coverage.xml
58+
flags: php
59+
fail_ci_if_error: false

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,7 @@ TestResults/
7070
*.trx
7171
nupkg/
7272
go/coverage
73+
74+
# PHP
75+
php/vendor/
76+
php/composer.lock

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ that can be validated and mapped to programming language types.
1818
| [Perl](./perl/) | `JSON::Structure` | ✅ Available |
1919
| [Swift](./swift/) | `JSONStructure` | ✅ Available |
2020
| [C](./c/) | `json-structure` | ✅ Available |
21+
| [PHP](./php/) | `json-structure/sdk` | ✅ Available |
2122

2223
## Features
2324

@@ -357,6 +358,39 @@ let instanceResult = instanceValidator.validate(instance)
357358
print("Instance valid: \(instanceResult.isEmpty)")
358359
```
359360

361+
### PHP
362+
363+
```bash
364+
composer require json-structure/sdk
365+
```
366+
367+
```php
368+
<?php
369+
370+
use JsonStructure\SchemaValidator;
371+
use JsonStructure\InstanceValidator;
372+
373+
// Validate a schema
374+
$schema = [
375+
'$schema' => 'https://json-structure.org/meta/core/v0/#',
376+
'$id' => 'https://example.com/person.struct.json',
377+
'name' => 'Person',
378+
'type' => 'object',
379+
'properties' => [
380+
'name' => ['type' => 'string'],
381+
'age' => ['type' => 'int32']
382+
]
383+
];
384+
385+
$schemaValidator = new SchemaValidator();
386+
$schemaErrors = $schemaValidator->validate($schema);
387+
388+
// Validate an instance
389+
$instance = ['name' => 'Alice', 'age' => 30];
390+
$instanceValidator = new InstanceValidator($schema);
391+
$instanceErrors = $instanceValidator->validate($instance);
392+
```
393+
360394
## Documentation
361395

362396
- [JSON Structure Specification](https://github.com/json-structure/core)

php/README.md

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
# JSON Structure PHP SDK
2+
3+
A PHP SDK for JSON Structure schema and instance validation.
4+
5+
## Requirements
6+
7+
- PHP 8.1 or higher
8+
- BCMath extension (for large integer validation)
9+
- JSON extension
10+
11+
## Installation
12+
13+
### Via Composer
14+
15+
```bash
16+
composer require json-structure/sdk
17+
```
18+
19+
### Manual Installation
20+
21+
Clone the repository and run:
22+
23+
```bash
24+
cd php
25+
composer install
26+
```
27+
28+
## Usage
29+
30+
### Schema Validation
31+
32+
Validate a JSON Structure schema document:
33+
34+
```php
35+
<?php
36+
37+
use JsonStructure\SchemaValidator;
38+
39+
$schema = [
40+
'$id' => 'https://example.com/person.struct.json',
41+
'$schema' => 'https://json-structure.org/meta/core/v0/#',
42+
'name' => 'Person',
43+
'type' => 'object',
44+
'properties' => [
45+
'name' => ['type' => 'string'],
46+
'age' => ['type' => 'int32'],
47+
'email' => ['type' => 'string']
48+
],
49+
'required' => ['name']
50+
];
51+
52+
$validator = new SchemaValidator(extended: true);
53+
$errors = $validator->validate($schema);
54+
55+
if (count($errors) === 0) {
56+
echo "Schema is valid!\n";
57+
} else {
58+
echo "Schema validation errors:\n";
59+
foreach ($errors as $error) {
60+
echo " - " . $error . "\n";
61+
}
62+
}
63+
```
64+
65+
### Instance Validation
66+
67+
Validate a JSON instance against a JSON Structure schema:
68+
69+
```php
70+
<?php
71+
72+
use JsonStructure\InstanceValidator;
73+
74+
$schema = [
75+
'$id' => 'https://example.com/person.struct.json',
76+
'$schema' => 'https://json-structure.org/meta/core/v0/#',
77+
'name' => 'Person',
78+
'type' => 'object',
79+
'properties' => [
80+
'name' => ['type' => 'string'],
81+
'age' => ['type' => 'int32'],
82+
'email' => ['type' => 'string']
83+
],
84+
'required' => ['name']
85+
];
86+
87+
$instance = [
88+
'name' => 'John Doe',
89+
'age' => 30,
90+
'email' => 'john@example.com'
91+
];
92+
93+
$validator = new InstanceValidator($schema, extended: true);
94+
$errors = $validator->validate($instance);
95+
96+
if (count($errors) === 0) {
97+
echo "Instance is valid!\n";
98+
} else {
99+
echo "Instance validation errors:\n";
100+
foreach ($errors as $error) {
101+
echo " - " . $error . "\n";
102+
}
103+
}
104+
```
105+
106+
### Extended Validation
107+
108+
Enable extended validation features (conditional composition, validation keywords):
109+
110+
```php
111+
<?php
112+
113+
use JsonStructure\SchemaValidator;
114+
use JsonStructure\InstanceValidator;
115+
116+
$schema = [
117+
'$id' => 'https://example.com/user.struct.json',
118+
'$schema' => 'https://json-structure.org/meta/core/v0/#',
119+
'$uses' => ['JSONStructureValidation'],
120+
'name' => 'User',
121+
'type' => 'object',
122+
'properties' => [
123+
'username' => [
124+
'type' => 'string',
125+
'minLength' => 3,
126+
'maxLength' => 20,
127+
'pattern' => '^[a-zA-Z][a-zA-Z0-9_]*$'
128+
],
129+
'age' => [
130+
'type' => 'int32',
131+
'minimum' => 0,
132+
'maximum' => 150
133+
]
134+
],
135+
'required' => ['username']
136+
];
137+
138+
// Validate schema
139+
$schemaValidator = new SchemaValidator(extended: true);
140+
$schemaErrors = $schemaValidator->validate($schema);
141+
142+
if (count($schemaErrors) > 0) {
143+
echo "Schema errors: " . count($schemaErrors) . "\n";
144+
}
145+
146+
// Validate instance
147+
$instance = [
148+
'username' => 'johndoe',
149+
'age' => 30
150+
];
151+
152+
$instanceValidator = new InstanceValidator($schema, extended: true);
153+
$instanceErrors = $instanceValidator->validate($instance);
154+
155+
if (count($instanceErrors) === 0) {
156+
echo "Valid!\n";
157+
}
158+
```
159+
160+
## Supported Types
161+
162+
### Primitive Types (34)
163+
164+
| Type | Description |
165+
|------|-------------|
166+
| `string` | UTF-8 string |
167+
| `boolean` | `true` or `false` |
168+
| `null` | Null value |
169+
| `number` | Any JSON number |
170+
| `integer` | Alias for `int32` |
171+
| `int8` | -128 to 127 |
172+
| `int16` | -32,768 to 32,767 |
173+
| `int32` | -2³¹ to 2³¹-1 |
174+
| `int64` | -2⁶³ to 2⁶³-1 (as string) |
175+
| `int128` | -2¹²⁷ to 2¹²⁷-1 (as string) |
176+
| `uint8` | 0 to 255 |
177+
| `uint16` | 0 to 65,535 |
178+
| `uint32` | 0 to 2³²-1 |
179+
| `uint64` | 0 to 2⁶⁴-1 (as string) |
180+
| `uint128` | 0 to 2¹²⁸-1 (as string) |
181+
| `float8` | 8-bit float |
182+
| `float` | 32-bit IEEE 754 |
183+
| `double` | 64-bit IEEE 754 |
184+
| `decimal` | Arbitrary precision (as string) |
185+
| `date` | RFC 3339 date (`YYYY-MM-DD`) |
186+
| `time` | RFC 3339 time (`HH:MM:SS[.sss]`) |
187+
| `datetime` | RFC 3339 datetime |
188+
| `duration` | ISO 8601 duration |
189+
| `uuid` | RFC 9562 UUID |
190+
| `uri` | RFC 3986 URI |
191+
| `binary` | Base64-encoded bytes |
192+
| `jsonpointer` | RFC 6901 JSON Pointer |
193+
194+
### Compound Types
195+
196+
| Type | Description |
197+
|------|-------------|
198+
| `object` | JSON object with typed properties |
199+
| `array` | Homogeneous list |
200+
| `set` | Unique homogeneous list |
201+
| `map` | Dictionary with string keys |
202+
| `tuple` | Fixed-length typed array |
203+
| `choice` | Discriminated union |
204+
| `any` | Any JSON value |
205+
206+
## Error Handling
207+
208+
All validation errors use standardized error codes:
209+
210+
```php
211+
<?php
212+
213+
use JsonStructure\ErrorCodes;
214+
215+
// Schema validation errors start with SCHEMA_
216+
ErrorCodes::SCHEMA_TYPE_INVALID;
217+
ErrorCodes::SCHEMA_REF_NOT_FOUND;
218+
ErrorCodes::SCHEMA_REQUIRED_PROPERTY_NOT_DEFINED;
219+
220+
// Instance validation errors start with INSTANCE_
221+
ErrorCodes::INSTANCE_TYPE_MISMATCH;
222+
ErrorCodes::INSTANCE_REQUIRED_PROPERTY_MISSING;
223+
ErrorCodes::INSTANCE_ENUM_MISMATCH;
224+
```
225+
226+
Each `ValidationError` includes:
227+
- `code`: Standardized error code
228+
- `message`: Human-readable error message
229+
- `path`: JSON Pointer path to the error location
230+
- `severity`: `ERROR` or `WARNING`
231+
- `location`: Line/column position (when source text is available)
232+
233+
## Testing
234+
235+
Run the test suite:
236+
237+
```bash
238+
composer test
239+
```
240+
241+
Run tests with coverage:
242+
243+
```bash
244+
composer test-coverage
245+
```
246+
247+
## License
248+
249+
MIT License - see [LICENSE](../LICENSE) for details.
250+
251+
## Related Resources
252+
253+
- [JSON Structure Specification](https://json-structure.github.io/core/)
254+
- [SDK Guidelines](../SDK-GUIDELINES.md)
255+
- [Test Assets](../test-assets/)

0 commit comments

Comments
 (0)