Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
62d76fe
docs: add project development guidance
andrepimpao Jul 31, 2026
068d029
feat(core): add foundational API value types
andrepimpao Jul 31, 2026
5962ca0
refactor!: establish clean SDK 3 baseline
andrepimpao Jul 31, 2026
959587d
feat(core): add SDK 3 API facade
andrepimpao Jul 31, 2026
074b428
feat(hydration): add typed nullable payload reader
andrepimpao Jul 31, 2026
f16351b
test(hydration): remove redundant SDK integration test
andrepimpao Jul 31, 2026
c366a8e
feat(formatting): add measurement formatter
andrepimpao Jul 31, 2026
2951a89
feat(errors): add API exception hierarchy
andrepimpao Jul 31, 2026
be54354
feat(resources): add immutable unit and language overrides
andrepimpao Jul 31, 2026
9a7a92f
feat(resources): resolve unit and language overrides
andrepimpao Jul 31, 2026
1b654be
docs(tests): document response fixture conventions
andrepimpao Jul 31, 2026
af922b6
test(geocoding): add response fixtures
andrepimpao Jul 31, 2026
8c7edc4
feat(geocoding): add location entities
andrepimpao Jul 31, 2026
03d7825
feat(geocoding): add location name lookup
andrepimpao Jul 31, 2026
9ee9658
docs: organize geocoding documentation
andrepimpao Jul 31, 2026
cd737b2
feat(geocoding): add postal code lookup
andrepimpao Jul 31, 2026
70bfcbf
feat(geocoding): add reverse lookup
andrepimpao Jul 31, 2026
0fff976
refactor(geocoding): streamline requests and test setup
andrepimpao Jul 31, 2026
f1566df
refactor(errors): consolidate status handling
andrepimpao Aug 1, 2026
a63bdde
refactor(geocoding): expose coordinates and share validation
andrepimpao Aug 1, 2026
e514576
refactor(geocoding): simplify optional queries
andrepimpao Aug 1, 2026
62c7bb6
refactor(validation): centralize country code assertion
andrepimpao Aug 1, 2026
af53b16
test(weather): add captured current and forecast fixtures
andrepimpao Aug 1, 2026
5e9e6d1
feat(weather): add current weather response entities
andrepimpao Aug 1, 2026
c5a152a
feat(weather): add current weather resource
andrepimpao Aug 1, 2026
d9f466b
feat(weather): add forecast period entities
andrepimpao Aug 1, 2026
11f5574
feat(weather): add forecast response entities
andrepimpao Aug 1, 2026
57b7b24
feat(weather): add forecast endpoint
andrepimpao Aug 1, 2026
d40b200
fix(weather): align entities with API contract
andrepimpao Aug 1, 2026
5847182
docs(weather): align section structure
andrepimpao Aug 1, 2026
a277d39
test(air-pollution): add response fixtures
andrepimpao Aug 1, 2026
2aa867f
feat(air-pollution): add current response model
andrepimpao Aug 1, 2026
ff13199
feat(air-pollution): expose current endpoint
andrepimpao Aug 2, 2026
81add16
feat(air-pollution): add forecast period entity
andrepimpao Aug 2, 2026
2589d77
refactor(weather): remove textual forecast timestamp
andrepimpao Aug 2, 2026
569027c
feat(air-pollution): add forecast response entity
andrepimpao Aug 2, 2026
523de68
feat(air-pollution): expose forecast endpoint
andrepimpao Aug 2, 2026
28294ef
feat(air-pollution): add historical period entity
andrepimpao Aug 2, 2026
389b2c2
refactor(air-pollution): centralize air quality hydration
andrepimpao Aug 2, 2026
ba10535
feat(air-pollution): add historical response entity
andrepimpao Aug 2, 2026
be70b0e
feat(air-pollution): expose historical endpoint
andrepimpao Aug 2, 2026
09614bb
refactor(entities): share nested coordinates
andrepimpao Aug 2, 2026
a84fa0a
test(one-call): add current weather fixtures
andrepimpao Aug 2, 2026
14ec726
feat(one-call): add current weather entity
andrepimpao Aug 2, 2026
331f9f0
test(one-call): add timeline and alert fixtures
andrepimpao Aug 2, 2026
a828903
feat(one-call): add minute timeline entity
andrepimpao Aug 2, 2026
70dcf02
feat(one-call): add fifteen-minute timeline entity
andrepimpao Aug 2, 2026
e728a6f
refactor(entities): standardize primary date-time getters
andrepimpao Aug 2, 2026
4954c80
feat(one-call): add one-hour timeline entity
andrepimpao Aug 2, 2026
d141049
feat(one-call): add one-day timeline period
andrepimpao Aug 2, 2026
39ba37d
feat(one-call): add one-day timeline entity
andrepimpao Aug 2, 2026
03dfc38
feat(one-call): add detailed alert entity
andrepimpao Aug 2, 2026
5772340
feat(one-call): expose current weather resource
andrepimpao Aug 2, 2026
8af3325
feat(one-call): expose minute timeline
andrepimpao Aug 2, 2026
b7d30e3
feat(one-call): expose fifteen-minute timeline
andrepimpao Aug 2, 2026
ead55e0
feat(one-call): expose one-hour timeline
andrepimpao Aug 2, 2026
374e0e9
feat(one-call): allow timeline count selection
andrepimpao Aug 2, 2026
5727754
docs: simplify API usage guides
andrepimpao Aug 2, 2026
c5e29e6
feat(one-call): expose one-day timeline
andrepimpao Aug 2, 2026
f0b080f
feat(one-call): expose alert lookup
andrepimpao Aug 2, 2026
282974e
refactor(one-call): simplify pagination URL handling
andrepimpao Aug 2, 2026
38ca841
refactor(one-call): normalize pagination URLs with PSR-7
andrepimpao Aug 2, 2026
e49cdc4
refactor(one-call): prepare resolver-based pagination
andrepimpao Aug 7, 2026
015662d
feat(one-call): add one-hour timeline pagination
andrepimpao Aug 8, 2026
0ead8ae
refactor(one-call): share timeline pagination
andrepimpao Aug 8, 2026
f89688a
feat(one-call): paginate remaining timelines
andrepimpao Aug 8, 2026
3f08552
feat(one-call): expose pagination availability
andrepimpao Aug 8, 2026
c9b46f3
fix(one-call): defer pagination resolver access
andrepimpao Aug 8, 2026
30112b8
refactor(api): clarify temporal arguments and pagination calls
andrepimpao Aug 8, 2026
86f7fde
feat(one-call): allow starting 15-minute timelines
andrepimpao Aug 8, 2026
b7981a5
test(maps): add weather tile response fixtures
andrepimpao Aug 8, 2026
73dec0e
feat(maps): add layers and tile validation
andrepimpao Aug 8, 2026
b128da1
feat(response): support JSON and binary payloads
andrepimpao Aug 8, 2026
ae6f21d
feat(maps): add authenticated tile fetching
andrepimpao Aug 8, 2026
288f990
feat(api): mark API key parameters as sensitive
andrepimpao Aug 8, 2026
32f929b
feat(maps): inject API key into resource
andrepimpao Aug 8, 2026
2a58157
feat(maps): generate authenticated tile URLs
andrepimpao Aug 8, 2026
cc34673
docs(maps): improve tile usage examples
andrepimpao Aug 8, 2026
7502d26
docs(maps): explain authentication and tile ranges
andrepimpao Aug 8, 2026
9b6e7b4
feat(maps): add tile URL templates
andrepimpao Aug 8, 2026
0c8d56f
test(fixtures): add station lifecycle captures
andrepimpao Aug 8, 2026
90743d8
test(fixtures): add station measurement captures
andrepimpao Aug 9, 2026
24cb752
feat(stations): add read endpoints
andrepimpao Aug 9, 2026
7db3a3b
refactor(stations): require core metadata
andrepimpao Aug 9, 2026
4fdc8be
feat(stations): add station creation
andrepimpao Aug 9, 2026
5322dbc
feat(stations): add station updates
andrepimpao Aug 9, 2026
dfddcd4
feat(stations): add station deletion
andrepimpao Aug 9, 2026
ad9da64
feat(stations): add measurement submission
andrepimpao Aug 9, 2026
35e35f5
feat(stations): add cloud layer measurements
andrepimpao Aug 9, 2026
4a7e884
feat(stations): add METAR weather inputs
andrepimpao Aug 9, 2026
e72f041
refactor(stations): separate station ID from measurements
andrepimpao Aug 9, 2026
bc2cec2
test(stations): add captured measurement aggregates
andrepimpao Aug 10, 2026
72c9c08
feat(stations): add measurement aggregate entities
andrepimpao Aug 10, 2026
12304ba
feat(stations): add measurement aggregate retrieval
andrepimpao Aug 10, 2026
1797031
docs(stations): document measurement retrieval
andrepimpao Aug 10, 2026
7ff8dda
test(stations): capture delayed aggregate results
andrepimpao Aug 14, 2026
8d9726a
docs: refine maps and stations guidance
andrepimpao Aug 14, 2026
51327e3
refactor(one-call): standardize dew point getters
andrepimpao Aug 14, 2026
de117e0
build: clean up Composer package metadata
andrepimpao Aug 14, 2026
6b25d08
docs: expand README getting started guide
andrepimpao Aug 14, 2026
ff61950
feat(weather): normalize precipitation probabilities
andrepimpao Aug 14, 2026
9b0bfaf
docs: clarify API usage guides
andrepimpao Aug 14, 2026
b9d76ce
docs: refine alert and METAR examples
andrepimpao Aug 14, 2026
f65e91b
docs: add setup configuration guide
andrepimpao Aug 14, 2026
81276e4
docs: add error handling guide
andrepimpao Aug 14, 2026
dd64ac1
docs: clarify client guide links
andrepimpao Aug 14, 2026
3485d8c
docs: add version 4 upgrade notice
andrepimpao Aug 14, 2026
0ce9801
chore: complete release readiness cleanup
andrepimpao Aug 14, 2026
057356a
ci: add composer validation and job timeout
andrepimpao Aug 14, 2026
0bcdaec
refactor(one-call): simplify timeline naming
andrepimpao Aug 14, 2026
9e5db00
docs: simplify API usage guidance
andrepimpao Aug 14, 2026
7f0039a
docs: clarify API usage details
andrepimpao Aug 14, 2026
9ed07dd
chore: update Composer package metadata
andrepimpao Aug 14, 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
  •  
  •  
  •  
11 changes: 10 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@ on:
- main
- "*.x"

permissions:
contents: read

jobs:
tests:
name: PHP ${{ matrix.php }} Test
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
php: ['8.1', '8.2', '8.3', '8.4', '8.5']

steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand All @@ -29,6 +34,10 @@ jobs:
tools: composer:v2
coverage: none

- name: Validate Composer configuration
if: matrix.php == '8.1'
run: composer validate --strict --no-check-lock

- name: Install dependencies
run: composer update --prefer-dist --no-interaction --no-progress

Expand Down
21 changes: 19 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
/composer.lock
/composer.phar
/auth.json
/.env
/.env.*
!/.env.example
/phpunit.xml
/.phpunit.result.cache
/.phpunit.cache/
/vendor/
/logs/
/.idea
/index.php
/coverage/
/clover.xml
/coverage.xml
/.idea/
/.vscode/
/.fleet/
/index.php
/plans/

.DS_Store
Thumbs.db
*.swp
*.swo
*~
77 changes: 77 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Project Instructions

## Project Overview

This repository contains a PHP library for accessing OpenWeather APIs. It uses
Composer with PSR-4 autoloading under the
`ProgrammatorDev\OpenWeatherMap` namespace and is built on
`programmatordev/php-api-sdk`.

## Sources Of Truth

- Use the official OpenWeather documentation for endpoint paths, parameters,
response fields, and current access requirements.
- Use the installed PHP API SDK documentation and source for its supported
authoring patterns.
- Read existing resources, entities, tests, and documentation before changing
related behavior.
- Do not infer API access from documentation sidebars; verify it against the
current official API documentation.

## Code Changes

- Prefer focused changes that follow the architecture and naming conventions
documented for the active target version. Do not preserve legacy patterns
when the active public contract intentionally replaces them.
- Organize source files by clear responsibility or domain and mirror that
structure in tests. Keep the root namespace focused on primary entry points;
avoid both unrelated root-level classes and arbitrary one-file folders.
- Reuse shared entities, helpers, resource behavior, test utilities, and
constants when they fit.
- Keep endpoint construction in resource classes and response mapping in typed
entity or response classes.
- Keep request-local fluent options immutable so they do not affect later
resource calls.
- Treat official documentation and representative response fixtures as
complementary schema evidence; neither source is exhaustive on its own.
- Tolerate missing, explicitly `null`, conditional, and unknown response fields.
Reject known non-null fields with invalid types through descriptive hydration
errors rather than silent coercion.
- Represent returned timestamps as nullable UTC `DateTimeImmutable` values and
keep location timezone identifiers or offsets as separate metadata.
- Make destructive operations explicit in method naming and documentation.
- Do not expose API keys through exceptions, logs, fixtures, or committed
example files.

## Dependencies And Tooling

- Run project PHP and Composer commands through DDEV.
- Respect the PHP versions declared by `composer.json` and CI.
- Remove a dependency only when its remaining usages have been eliminated.
- Do not introduce a formatter, static analyzer, or new test framework without
explicit approval.

## Testing

- Use PHPUnit and the existing PSR-18 mock-client approach.
- Do not make live OpenWeather requests in the automated test suite.
- Build automated response tests from sanitized real API captures, supplemented
by synthetic edge-case fixtures. Keep credentials and private or
account-specific data out of committed fixtures.
- Test endpoint method, URL, path parameters, query parameters, headers, body,
response mapping, error mapping, and immutable resource-chain behavior.
- Add focused entity tests for present, missing, explicitly `null`,
conditionally present, unknown, invalidly typed, and nested fields.
- Cover empty-body responses for successful write and delete operations.
- Run the full test suite before handing off an implementation batch when
practical.

## Documentation

- Update public documentation alongside implemented API areas.
- Keep method signatures, examples, supported endpoints, and response entities
aligned with the implementation.
- Avoid hard-coded claims about plans, prices, quotas, or allowances. Link to
the official OpenWeather documentation for current access requirements.
- Document destructive behavior and actions that send additional requests
prominently.
115 changes: 88 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,118 @@
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Tests](https://github.com/programmatordev/openweathermap-php-api/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/programmatordev/openweathermap-php-api/actions/workflows/ci.yml?query=branch%3Amain)

OpenWeatherMap PHP library that provides convenient access to the OpenWeatherMap API.
A fluent PHP client for OpenWeather APIs covering current and forecast weather,
air pollution, geocoding, maps, stations, and One Call. Responses use typed
entities that safely handle conditional, missing, and `null` data.

Supports [PSR-18 HTTP clients](https://www.php-fig.org/psr/psr-18), [PSR-17 HTTP factories](https://www.php-fig.org/psr/psr-17), [PSR-6 caches](https://www.php-fig.org/psr/psr-6) and [PSR-3 logs](https://www.php-fig.org/psr/psr-3).
The library is built on
[`programmatordev/php-api-sdk`](https://github.com/programmatordev/php-api-sdk),
which provides HTTP client discovery and optional caching, logging, plugins,
and request hooks.

## Requirements

- PHP 8.1 or higher.

## API Key

A key is required to be able to make requests to the API.
You must sign up for an [OpenWeatherMap account](https://openweathermap.org/appid#signup) to get one.
- PHP 8.1 or higher
- An OpenWeather API key

## Installation

Install the library via [Composer](https://getcomposer.org/):
Install the library with Composer:

```bash
composer require programmatordev/openweathermap-php-api
```

## Basic Usage
## Getting Started

Create the API client with an OpenWeather API key, then choose an API and call
one of its methods:

```php
use ProgrammatorDev\OpenWeatherMap\OpenWeatherMap;

$api = new OpenWeatherMap($_ENV['OPENWEATHERMAP_API_KEY']);

$current = $api->weather()->current(
latitude: 38.7223,
longitude: -9.1393,
);

echo $current->temperature();
echo $current->temperatureWithUnit();
```

Response properties may be missing or `null`, so getters return nullable values
where appropriate. Collection getters return empty arrays when the response
does not contain that collection.

Simple usage looks like:
## Configuration

The client defaults to metric units and English. The equivalent explicit
configuration is:

```php
use ProgrammatorDev\OpenWeatherMap\Enum\Language;
use ProgrammatorDev\OpenWeatherMap\Enum\Units;
use ProgrammatorDev\OpenWeatherMap\OpenWeatherMap;

// initialize
$api = new OpenWeatherMap('yourapikey');
$api = new OpenWeatherMap(
apiKey: $_ENV['OPENWEATHERMAP_API_KEY'],
options: [
'units' => Units::METRIC,
'language' => Language::ENGLISH,
],
);
```

Weather and One Call requests can override those values for one fluent request
chain. The client-wide configuration remains unchanged for later requests:

// get current weather by coordinate (latitude, longitude)
$weather = $api->weather()->getCurrent(50, 50);
// show current temperature
echo $weather->getTemperature();
```php
$current = $api
->weather()
->withUnits(Units::IMPERIAL)
->withLanguage(Language::PORTUGUESE)
->current(latitude: 38.7223, longitude: -9.1393);
```

`withLanguage()` also accepts a non-empty language-code string, allowing new
OpenWeather languages to be used without waiting for an enum update.

See OpenWeather's
[units of measurement](https://openweathermap.org/api/current?collection=current_forecast#data) and
[multilingual support](https://openweathermap.org/api/current?collection=current_forecast#multi)
documentation for the currently supported values.

## Documentation

- [Usage](docs/01-usage.md)
- [Configuration](docs/02-configuration.md)
- [Supported APIs](docs/03-supported-apis.md)
- [Error Handling](docs/04-error-handling.md)
- [Entities](docs/05-entities.md)
### APIs

These guides cover each API's endpoints, response entities, and usage examples:

- [One Call 4.0](docs/one-call.md)
- [Air Pollution](docs/air-pollution.md)
- [Weather](docs/weather.md)
- [Maps](docs/maps.md)
- [Stations](docs/stations.md)
- [Geocoding](docs/geocoding.md)

### Client Guides

These guides cover client configuration and failures shared across the APIs:

- [Setup](docs/setup.md) — Configure caching, logging, HTTP clients, plugins,
and request hooks.
- [Error Handling](docs/errors.md) — Handle OpenWeather API errors and client
failures.

## Contributing
## Upgrading

Any form of contribution to improve this library (including requests) will be welcome and appreciated.
Make sure to open a pull request or issue.
Version 4 is a complete rewrite without backward compatibility. Existing
integrations should treat it as a new implementation. See
[Upgrading To 4.0](UPGRADE-4.0.md) for the release expectations and current
baseline.

## License

This project is licensed under the MIT license.
Please see the [LICENSE](LICENSE) file distributed with this source code for further information regarding copyright and licensing.
This project is licensed under the [MIT License](LICENSE).
33 changes: 33 additions & 0 deletions UPGRADE-4.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Upgrading To 4.0

Version 4 is a complete rewrite. It is not backward compatible with earlier
releases. Existing integrations should adopt it as a new implementation, even
where the usage looks familiar.

## Upgrade Expectations

- Previous resources, entities, methods, namespaces, and configuration are not
part of the current public contract.
- Compatibility aliases, transitional APIs, and an old-to-new API mapping are
not provided.
- Integrations should be rebuilt against the current [README](README.md) and
[API guides](README.md#apis).
- Application tests should be reviewed and updated before adopting the new
release.

## Current Baseline

- PHP 8.1 or later is required.
- The client is built on
[`programmatordev/php-api-sdk` 3](https://github.com/programmatordev/php-api-sdk).
- Current weather, forecasts, air pollution, geocoding, maps, stations, and One
Call 4.0 are supported.
- Metric units and English are the defaults. They can be configured for the
client or changed for one request chain where supported.
- Response entities accept missing, `null`, conditional, and unknown fields.
Known non-null fields with invalid types produce hydration errors.
- OpenWeather API failures use a documented exception hierarchy; transport,
decoding, and hydration failures remain distinguishable. See
[Error Handling](docs/errors.md).

The current documentation defines the supported behavior for this release.
15 changes: 4 additions & 11 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "programmatordev/openweathermap-php-api",
"description": "OpenWeatherMap PHP library that provides convenient access to the OpenWeatherMap API",
"description": "A fluent PHP client for OpenWeather APIs, including weather, air pollution, geocoding, maps, stations, and One Call",
"type": "library",
"keywords": ["openweathermap", "api", "php", "php8", "sdk", "psr-18", "psr-17", "psr-6", "psr-3"],
"keywords": ["openweather", "openweathermap", "weather", "forecast", "air-quality", "geocoding", "api", "php", "php8", "sdk", "psr-18", "psr-17", "psr-6", "psr-3"],
"license": "MIT",
"authors": [
{
Expand All @@ -13,23 +13,16 @@
],
"require": {
"php": ">=8.1",
"myclabs/deep-copy": "^1.13",
"programmatordev/php-api-sdk": "^2.1",
"symfony/options-resolver": "^6.4|^7.4|^8.0"
"php-http/discovery": "^1.20",
"programmatordev/php-api-sdk": "^3.3"
},
"require-dev": {
"monolog/monolog": "^3.10",
"nyholm/psr7": "^1.8",
"php-http/mock-client": "^1.6",
"phpunit/phpunit": "^10.5",
"symfony/cache": "^6.4|^7.4|^8.0",
"symfony/http-client": "^6.4|^7.4|^8.0",
"symfony/var-dumper": "^6.4|^7.4|^8.0"
},
"provide": {
"psr/http-client-implementation": "1.0",
"psr/http-factory-implementation": "1.0"
},
"autoload": {
"psr-4": {
"ProgrammatorDev\\OpenWeatherMap\\": "src/"
Expand Down
Loading