Skip to content

Commit 144d04e

Browse files
oschwaldclaude
andcommitted
Add CLAUDE.md documentation for AI assistants
This adds guidance for Claude Code and other AI assistants when working with the codebase, including architecture patterns, testing conventions, and development workflows. Also update .gitignore to exclude Claude Code directories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 444ddcb commit 144d04e

File tree

2 files changed

+332
-0
lines changed

2 files changed

+332
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ t.php
1818
vendor/
1919
*.sw?
2020
*.old
21+
22+
# Claude Code directories
23+
.claude/
24+
.claude_cache/

CLAUDE.md

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
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+
**GeoIP2-php** is MaxMind's official PHP client library for:
8+
- **GeoIP2/GeoLite2 Web Services**: Country, City, and Insights endpoints
9+
- **GeoIP2/GeoLite2 Databases**: Local MMDB file reading for various database types (City, Country, ASN, Anonymous IP, Anonymous Plus, ISP, etc.)
10+
11+
The library provides both web service clients and database readers that return strongly-typed model objects containing geographic, ISP, anonymizer, and other IP-related data.
12+
13+
**Key Technologies:**
14+
- PHP 8.1+ (uses modern PHP features like readonly properties and strict types)
15+
- MaxMind DB Reader for binary database files
16+
- MaxMind Web Service Common for HTTP client functionality
17+
- PHPUnit for testing
18+
- php-cs-fixer, phpcs, and phpstan for code quality
19+
20+
## Code Architecture
21+
22+
### Package Structure
23+
24+
```
25+
GeoIp2/
26+
├── Model/ # Response models (City, Insights, AnonymousIp, etc.)
27+
├── Record/ # Data records (City, Location, Traits, etc.)
28+
├── Exception/ # Custom exceptions for error handling
29+
├── Database/Reader # Local MMDB file reader
30+
├── WebService/Client # HTTP client for MaxMind web services
31+
└── ProviderInterface # Common interface for database and web service
32+
```
33+
34+
### Key Design Patterns
35+
36+
#### 1. **Readonly Properties for Immutable Data**
37+
All model and record classes use PHP 8.1+ `readonly` properties for immutability and performance:
38+
39+
```php
40+
class AnonymousPlus extends AnonymousIp
41+
{
42+
public readonly ?int $anonymizerConfidence;
43+
public readonly ?string $networkLastSeen;
44+
public readonly ?string $providerName;
45+
}
46+
```
47+
48+
**Key Points:**
49+
- Properties are set in the constructor and cannot be modified afterward
50+
- Use `readonly` keyword for all public properties
51+
- Nullable properties use `?Type` syntax
52+
- Non-nullable booleans typically default to `false` in constructor logic
53+
54+
#### 2. **Inheritance Hierarchies**
55+
56+
Models follow clear inheritance patterns:
57+
- `Country` → base model with country/continent data
58+
- `City` extends `Country` → adds city, location, postal, subdivisions
59+
- `Insights` extends `City` → adds additional web service fields
60+
- `Enterprise` extends `City` → adds enterprise-specific fields
61+
62+
Records have similar patterns:
63+
- `AbstractNamedRecord` → base with names/locales
64+
- `AbstractPlaceRecord` extends `AbstractNamedRecord` → adds confidence, geonameId
65+
- Specific records (`City`, `Country`, etc.) extend these abstracts
66+
67+
#### 3. **JsonSerializable Implementation**
68+
69+
All model and record classes implement `\JsonSerializable` for consistent JSON output:
70+
71+
```php
72+
public function jsonSerialize(): ?array
73+
{
74+
$js = parent::jsonSerialize();
75+
76+
if ($this->anonymizerConfidence !== null) {
77+
$js['anonymizer_confidence'] = $this->anonymizerConfidence;
78+
}
79+
80+
return $js;
81+
}
82+
```
83+
84+
- Only include non-null values in JSON output
85+
- Use snake_case for JSON keys (matching API format)
86+
- Properties use camelCase in PHP
87+
88+
#### 4. **Constructor Array Parameter Pattern**
89+
90+
Models and records are constructed from associative arrays (from JSON/DB):
91+
92+
```php
93+
public function __construct(array $raw)
94+
{
95+
parent::__construct($raw);
96+
$this->anonymizerConfidence = $raw['anonymizer_confidence'] ?? null;
97+
$this->networkLastSeen = $raw['network_last_seen'] ?? null;
98+
}
99+
```
100+
101+
- Use `$raw['snake_case_key'] ?? null` pattern for optional fields
102+
- Use `$raw['snake_case_key'] ?? false` for boolean fields
103+
- Call parent constructor first if extending another class
104+
105+
#### 5. **Web Service Only vs Database Models**
106+
107+
Some models are only used by web services and do **not** need MaxMind DB support:
108+
109+
**Web Service Only Models**:
110+
- Models that are exclusive to web service responses
111+
- Simpler implementation without database parsing logic
112+
- Example: `Insights` (extends City but used only for web service)
113+
114+
**Database-Supported Models**:
115+
- Models used by both web services and database files
116+
- Must handle MaxMind DB format data structures
117+
- Example: `City`, `Country`, `AnonymousIp`, `AnonymousPlus`
118+
119+
## Testing Conventions
120+
121+
### Running Tests
122+
123+
```bash
124+
# Install dependencies
125+
composer install
126+
127+
# Run all tests
128+
vendor/bin/phpunit
129+
130+
# Run specific test class
131+
vendor/bin/phpunit tests/GeoIp2/Test/Model/InsightsTest.php
132+
133+
# Run with coverage (if xdebug installed)
134+
vendor/bin/phpunit --coverage-html coverage/
135+
```
136+
137+
### Linting and Static Analysis
138+
139+
```bash
140+
# PHP-CS-Fixer (code style)
141+
vendor/bin/php-cs-fixer fix --verbose --diff --dry-run
142+
143+
# Apply fixes
144+
vendor/bin/php-cs-fixer fix
145+
146+
# PHPCS (PSR-2 compliance)
147+
vendor/bin/phpcs --standard=PSR2 src/
148+
149+
# PHPStan (static analysis)
150+
vendor/bin/phpstan analyze
151+
152+
# Validate composer.json
153+
composer validate
154+
```
155+
156+
### Test Structure
157+
158+
Tests are organized by model/class:
159+
- `tests/GeoIp2/Test/Database/` - Database reader tests
160+
- `tests/GeoIp2/Test/Model/` - Response model tests
161+
- `tests/GeoIp2/Test/WebService/` - Web service client tests
162+
163+
### Test Patterns
164+
165+
When adding new fields to models:
166+
1. Update the test method to include the new field in the `$raw` array
167+
2. Add assertions to verify the field is properly populated
168+
3. Test both presence and absence of the field (null handling)
169+
4. Verify JSON serialization includes the field correctly
170+
171+
Example:
172+
```php
173+
public function testFull(): void
174+
{
175+
$raw = [
176+
'anonymizer_confidence' => 99,
177+
'network_last_seen' => '2025-04-14',
178+
'provider_name' => 'FooBar VPN',
179+
// ... other fields
180+
];
181+
182+
$model = new AnonymousPlus($raw);
183+
184+
$this->assertSame(99, $model->anonymizerConfidence);
185+
$this->assertSame('2025-04-14', $model->networkLastSeen);
186+
$this->assertSame('FooBar VPN', $model->providerName);
187+
}
188+
```
189+
190+
## Working with This Codebase
191+
192+
### Adding New Fields to Existing Models
193+
194+
1. **Add the readonly property** with proper type hints and PHPDoc:
195+
```php
196+
/**
197+
* @var int|null description of the field
198+
*/
199+
public readonly ?int $fieldName;
200+
```
201+
2. **Update the constructor** to set the field from the raw array:
202+
```php
203+
$this->fieldName = $raw['field_name'] ?? null;
204+
```
205+
3. **Update `jsonSerialize()`** to include the field:
206+
```php
207+
if ($this->fieldName !== null) {
208+
$js['field_name'] = $this->fieldName;
209+
}
210+
```
211+
4. **Add comprehensive PHPDoc** describing the field, its source, and availability
212+
5. **Update tests** to include the new field in test data and assertions
213+
6. **Update CHANGELOG.md** with the change
214+
215+
### Adding New Models
216+
217+
When creating a new model class:
218+
219+
1. **Determine if web service only or database-supported**
220+
2. **Follow the pattern** from existing similar models
221+
3. **Extend the appropriate base class** (e.g., `Country`, `City`, or standalone)
222+
4. **Use `readonly` properties** for all public fields
223+
5. **Implement `\JsonSerializable`** interface
224+
6. **Provide comprehensive PHPDoc** for all properties
225+
7. **Add corresponding tests** with full coverage
226+
227+
### Deprecation Guidelines
228+
229+
When deprecating fields:
230+
231+
1. **Use `@deprecated` in PHPDoc** with version and alternative:
232+
```php
233+
/**
234+
* @var bool This field is deprecated as of version 3.2.0.
235+
* Use the anonymizer object from the Insights response instead.
236+
*
237+
* @deprecated since 3.2.0
238+
*/
239+
public readonly bool $isAnonymous;
240+
```
241+
2. **Keep deprecated fields functional** - don't break existing code
242+
3. **Update CHANGELOG.md** with deprecation notices
243+
4. **Document alternatives** in the deprecation message
244+
245+
### CHANGELOG.md Format
246+
247+
Always update `CHANGELOG.md` for user-facing changes.
248+
249+
**Important**: Do not add a date to changelog entries until release time.
250+
251+
- If there's an existing version entry without a date (e.g., `3.3.0 (unreleased)`), add your changes there
252+
- If creating a new version entry, use `(unreleased)` instead of a date
253+
- The release date will be added when the version is actually released
254+
255+
```markdown
256+
3.3.0 (unreleased)
257+
------------------
258+
259+
* A new `fieldName` property has been added to `GeoIp2\Model\ModelName`.
260+
This field provides information about...
261+
* The `oldField` property in `GeoIp2\Model\ModelName` has been deprecated.
262+
Please use `newField` instead.
263+
```
264+
265+
## Common Pitfalls and Solutions
266+
267+
### Problem: Incorrect Property Types
268+
Using wrong type hints can cause type errors or allow invalid data.
269+
270+
**Solution**: Follow these patterns:
271+
- Optional values: `?Type` (e.g., `?int`, `?string`)
272+
- Non-null booleans: `bool` (default to `false` in constructor if not present)
273+
- Arrays: `array` with PHPDoc specifying structure (e.g., `@var array<string>`)
274+
275+
### Problem: Missing JSON Serialization
276+
New fields not appearing in JSON output.
277+
278+
**Solution**: Always update `jsonSerialize()` to include new fields:
279+
- Check if the value is not null before adding to array
280+
- Use snake_case for JSON keys to match API format
281+
- Call parent's `jsonSerialize()` first if extending
282+
283+
### Problem: Test Failures After Adding Fields
284+
Tests fail because fixtures don't include new fields.
285+
286+
**Solution**: Update all related tests:
287+
1. Add field to test `$raw` array
288+
2. Add assertions for the new field
289+
3. Test null case if field is optional
290+
4. Verify JSON serialization
291+
292+
## Code Style Requirements
293+
294+
- **PSR-2 compliance** enforced by phpcs
295+
- **PHP-CS-Fixer** rules defined in `.php-cs-fixer.php`
296+
- **Strict types** (`declare(strict_types=1)`) in all files
297+
- **Yoda style disabled** - use normal comparison order (`$var === $value`)
298+
- **Strict comparison** required (`===` and `!==` instead of `==` and `!=`)
299+
- **No trailing whitespace**
300+
- **Unix line endings (LF)**
301+
302+
## Development Workflow
303+
304+
### Setup
305+
```bash
306+
composer install
307+
```
308+
309+
### Before Committing
310+
```bash
311+
# Run all checks
312+
vendor/bin/php-cs-fixer fix
313+
vendor/bin/phpcs --standard=PSR2 src/
314+
vendor/bin/phpstan analyze
315+
vendor/bin/phpunit
316+
```
317+
318+
### Version Requirements
319+
- **PHP 8.1+** required
320+
- Uses modern PHP features (readonly, union types, etc.)
321+
- Target compatibility should match current supported PHP versions (8.1-8.4)
322+
323+
## Additional Resources
324+
325+
- [API Documentation](https://maxmind.github.io/GeoIP2-php/)
326+
- [GeoIP2 Web Services Docs](https://dev.maxmind.com/geoip/docs/web-services)
327+
- [MaxMind DB Format](https://maxmind.github.io/MaxMind-DB/)
328+
- GitHub Issues: https://github.com/maxmind/GeoIP2-php/issues

0 commit comments

Comments
 (0)