Skip to content

Contributing

Thanks for helping make pennylane-sdk better! Contributions of every size are welcome: bug reports, docs fixes, new endpoint coverage, ideas.

Development setup

Requirements: PHP 8.2 or later, and Composer.

git clone https://github.com/GoatAndCow7/pennylane-sdk-php
cd pennylane-sdk-php
composer install

Run the checks the CI runs:

composer test              # phpunit, all mocked, no token needed
composer stan               # PHPStan at level max
composer cs                  # coding style, dry run (use composer cs:fix to apply)
composer coverage-audit     # every spec operation implemented, no unknown calls

Or run all of them at once with composer check.

Project layout

specs/                        vendored official OpenAPI specs (source of truth)
scripts/update-specs.php      refresh the specs from pennylane.readme.io
scripts/check-coverage.php    audits that every spec operation exists in the resource classes
scripts/show-endpoint.php     inspect one endpoint's schema from the specs
src/
  Pennylane.php                Company API v2 client
  PennylaneFirm.php            Firm API v1 client
  Internal/                    HTTP engine: auth, retries, throttle, error mapping
  Pagination/                  cursor and page-number auto-paginating pages
  Resources/Company|Firm/      one class per URL namespace, extending AbstractResource
  Types/Company|Firm/           response DTOs, extending PennylaneObject
tests/                        mirrors src/, mocked with php-http/mock-client
docs/                         mkdocs-material site

Adding or fixing an endpoint

  1. Find the operation in the spec: php scripts/show-endpoint.php company "customer_invoices/{id}/finalize" put
  2. Follow the conventions in docs/design/resource-map.md: method naming, DTO shape, docblock format.
  3. Write the resource method, its DTO if needed, and tests.
  4. Make the four checks above pass. composer coverage-audit must report every operation covered, with no unknown calls.

Style rules

  • English everywhere in code and docs.
  • No em dashes or en dashes in text; use plain punctuation instead.
  • Resource methods call the AbstractResource helpers (doGet, doGetPage, doGetNumberedPage, doPost, doPut, doDelete, and their variants) with the endpoint path as a single double quoted string literal, interpolating variables only in {$variable} form. This is what scripts/check-coverage.php parses; never build a path with concatenation, sprintf or an intermediate variable.
  • Docblocks state the required OAuth scope on a Scope: line and link the official reference page on a Reference: line.
  • Response DTO fields are nullable except id; monetary amounts and quantities stay ?string, never float.

Releasing (maintainers)

  1. Update CHANGELOG.md (move the relevant Unreleased entries under a new version heading) and bump Version::SDK_VERSION in src/Version.php.
  2. Commit both changes together.
  3. Tag: git tag v0.x.y && git push --tags.
  4. The release workflow tests, builds and publishes the package to Packagist.

When the API changes

Refresh the vendored specs and see what the coverage audit reports:

composer update-specs
composer coverage-audit

New operations show up as MISSING; removed ones as UNKNOWN CALL. Please open an issue even if you cannot fix it yourself.