openfoodfacts / openfoodfacts-php
Open Food Facts API Wrapper (API v3.6), the open database about food.
Package info
github.com/openfoodfacts/openfoodfacts-php
pkg:composer/openfoodfacts/openfoodfacts-php
Requires
- php: ^8.1
- ext-curl: *
- ext-gd: *
- ext-json: *
- guzzlehttp/guzzle: ^7.4
- psr/log: ^3.0
- psr/simple-cache: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.9.5
- phpstan/phpstan: ^1.8.2
- phpstan/phpstan-phpunit: ^1.1.1
- phpunit/phpunit: ^10.5
- symfony/cache: ^6.1.3
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-19 14:40:31 UTC
README
π«π· Version franΓ§aise Β· π¬π§ English
Official PHP wrapper for Open Food Facts, the open database about food products.
The development branch migrates product reads, writes and image uploads to the Open Food Facts API v3.6 (product schema 1004) and fixes several robustness bugs present in v0.4.0, which used the legacy v0 API.
π Full documentation: doc/home.md β this README covers the migration from v0.4.0 and the main SDK changes.
The v3 migration covers products and their images. Api::search() still uses cgi/search.pl; getByFacets() and facet accessors use the websiteβs .json URLs. There is no v3 search endpoint: see the official API feature matrix. The separate SearchApi client exposes Search-a-licious.
Installation
With Composer:
composer require openfoodfacts/openfoodfacts-php
This documentation describes the development branch's migration to API v3.6. The command above installs the latest stable release published on Packagist, which may not yet include these changes. The package name remains openfoodfacts/openfoodfacts-php and PHP namespaces remain OpenFoodFacts\.
Quick start
// The user agent (1st argument) is mandatory β describe your application $api = new OpenFoodFacts\Api('MyApp - Web - 1.0 - https://example.org', 'food', 'fr'); // Read through API v3.6: filter the fields (recommended) and localize the response $product = $api->getProduct('3057640385148', ['product_name', 'nutriscore_grade'], 'fr'); echo $product->product_name; // Structured write (v3 PATCH) and image upload (v3 POST) β credentials required $api->authentification('user', 'password'); $api->updateProduct('3057640385148', ['product_name_fr' => 'Eau de Volvic', 'categories_tags' => ['en:waters']]); $api->uploadImage('3057640385148', 'front', '/path/to/image.jpg', 'fr');
Migrating from v0.4.0
1. Migration to API v3.6
Ref: API and product schema change log
| Feature | v0.4.0 | Development branch (API v3.6) |
|---|---|---|
| Product read | GET /api/v0/product/{code} |
GET /api/v3.6/product/{code} (product schema 1004 pinned) |
| Read parameters | none | fields, lc, cc, tags_lc (optional, backward compatible) |
| Product write | cgi/product_jqm2.pl (legacy form) |
new updateProduct() method: PATCH /api/v3.6/product/{code} with structured JSON (language-specific fields, tags, packagings, image selection) |
| Image upload | cgi/product_image_upload.pl (multipart, food only) |
POST /api/v3.6/product/{code}/images (base64 + selected structure per field/language, all flavors) |
| Response envelope | status 0/1 |
v3 envelope (status, result, errors[], warnings[]) with readable error messages extracted from errors[] |
| Partially rejected write | not detectable | success_with_errors β ProductUpdateException (the full envelope stays available through getResponse()); success_with_warnings logged through the logger |
| Product of another type (cross-flavor) | not handled | product_type parameter of getProduct() (e.g. 'all'): the server redirects to the right flavor, and the returned document matches that flavor (e.g. BeautyDocument) |
| Redirects | strict = false (a PATCH/POST redirected with 301/302 would be downgraded to a body-less GET) |
strict mode: method and body are preserved across redirects |
Implementation details:
Api::API_VERSION = '3.6'(public constant): every product route is versioned, so the response structure stays stable even as the server schema evolves.addNewProduct()(legacy cgi) is kept but deprecated in favor ofupdateProduct().Document::__getreturnsnullfor a missing field (instead of a PHP warning): essential with v3.6, which removes the*_hierarchyand*_lcfields in favor oftags_sources, and since v3.1 renamesecoscore_*toenvironmental_score_*.
2. Fixes for bugs present in v0.4.0
- POST responses are no longer cached: in v0.4.0, a second identical write (
addNewProduct,uploadImage) within the cache lifetime never hit the network β a silently lost write. On top of that, the cache key collided for every upload (resource not serializable byjson_encode). - TTL on the read cache (1 h): v0.4.0 cached product data with no expiration.
- Strict JSON decoding (
JSON_THROW_ON_ERROR) and HTTP status checks: an HTML error page from the server caused aTypeErrorin v0.4.0. - Barcode validation (digits only) and URL encoding: no more URL segment injection through
getProduct(). 404βProductNotFoundException, missing credentials βMissingCredentialsException, invalid barcode βInvalidBarcodeException(extendsInvalidParameterException, rejected value available throughgetBarcode()).activeTestMode()separates the staging htaccess from the contributor credentials:off/offis the htaccess (HTTP Basic) gate of the.nettest server, not a contributor account. The SDK sends it in theAuthorizationheader, while the contributor credentials β provided throughauthentification()β go into the body of v3 writes (user_id/password). Version v0.4.0 conflated the two:activeTestMode()overwrote the contributor credentials withoff/off, making every staging write fail.
3. Fixed documentation and examples
- The v0.4.0 README examples no longer compiled (the constructor has required
$userAgentas first argument since 0.4.0);cached_example.phpalso passed aninttogetProduct(string). - The dead Travis CI badge was removed.
4. Tests
- New unit suite
tests/Unit/OpenFoodFacts/ApiV3Test.php(GuzzleMockHandler): versioned URL, v3 envelope, 404, readable errors, PATCH body, base64 payload β with no dependency on the live API. - Integration tests brought in line (the "food-only upload" restriction no longer exists in v3).
Compatibility
- PHP 8.1 β 8.5 (no syntax beyond 8.1; notably tested on 8.3).
- Unchanged runtime dependencies: Guzzle 7, PSR-3, PSR-16.
- Public API backward compatible, with the following changes:
uploadImage()limits files to 10 MiB before encoding, now returns the v3 envelope and works for all flavors; non-numeric barcodes are rejected;activeTestMode()now only setsoff/offas the.nethtaccess β to write on staging, provide a contributor account throughauthentification(). - The new exceptions (
InvalidParameterException,UnknownException,ProductUpdateException) extendBadRequestException: acatch (BadRequestException)written against v0.4.0 keeps catching them.
License and reuse
The code is licensed under MIT.
- If you use this SDK, feel free to open a PR to document your application and its data reuse in this repository.
- Comply with the OdBL license for Open Food Facts data: credit the source, avoid combining it with non-free data you cannot legally release as open data, and contribute back any products you add using this SDK.
- Get in touch at reuse@openfoodfacts.org.
- You can also tell the community about your reuse and submit your application for a chance to be featured. This is optional.
Authors
- Roberto Moreno β Wrapper Developer.
- Colin Benoit β PHP Developer.
- The project contributors and the Open Food Facts community.