Version history for envless/sdk, the PHP client for the Envless API, published on Packagist. Keep it current for the latest coverage, security patches and type fixes. When a newer version is out, the client says so once on a terminal, with the composer update command to run.
The TypeScript SDK, @goenvless/sdk, has its own changelog, and so do the Python SDK and the Ruby SDK. They cover the same API, and a change to one lands in the others.
Next release
Changed
- Each list method takes only the options its route reads, the same set as its TypeScript twin:
search:androleId:onmemberslists,search:andsortBy:onroleslists,search:oninviteslists,search:andorder:onproducts,products->listProjects(),keysandwebhookslists, those plusproduct:onprojectsandenvironmentslists,product:onvariablesandversionslists and their history and entries, and onlylimit:andoffset:onworkspacesandproducts->accesslists. Passing an option the route never read now fails with PHP'sUnknown named parametererror instead of being ignored variables->getById(),updateById(),deleteById(),versions->get()andversions->rollback()no longer takeproduct:or send?product=, since the id already names the project
Added
isSecretonVariableInputandVariablePatch, andnameon anApplyUpdateEntry, which renames the variable in the samevariables->apply()transactionroleId:onmembers->list(),listAll()anditerate(), andsortBy:on theroleslists
v0.0.2
Fixed
Envless::isCiphertext()no longer makes PHPStan report that a call "will always evaluate to true" when it is given astring. It now declares@phpstan-assert-if-true =string, so PHPStan still narrows astring|nullvalue tostringinside theifwithout treating the call as a type test every string passes. Psalm reads the unchanged@psalm-assert-if-true string
Changed
- The package links its own documentation, this section, with a guide and a page for each of the 113 methods. Every example on those pages is type-checked with PHPStan and run against a sample API before a release
v0.0.1
Initial release. The PHP twin of the TypeScript SDK, covering 100% of the public HTTP API: all 113 methods across 13 namespaces, under their TypeScript names. A parity check replays every method against the requests the TypeScript SDK records, with no options and with every option set, and fails the build on any difference in the request, the retries, the errors or the result.
Client
Envless\Envlesswith areadonlyproperty for every namespace:me,workspace(withworkspace->domain),workspaces,products(withproducts->access),projects,environments,variables,versions,members,invites,roles,keys,webhooksnew Envless()readsENVLESS_TOKENandENVLESS_API_ENDPOINTfor whatever you leave out.Envless::init(...)configures a shared client once at startup,Envless::getClient()returns it, building one from the environment on first use, andEnvless::resetClient()drops it- A request body is one array keyed by the API's own field names, and every result is the decoded array. The options of a call are named arguments:
product:,limit:,offset:,search:,order:,status:,reattempt:, andtimeout:on every method $envless->raw->request($path, $method, query:, body:)reaches an endpoint no method wraps yet, with the client's key, base URL, timeout and retries- Bring your own transport: Guzzle or any PSR-18 client through
Envless\Http\Psr18HttpClient, or anything implementingEnvless\Http\HttpClient. Redirects, retries and the deadline stay in the client - A client is immutable once built and keeps one connection alive, while
POSTandPATCHalways start on a fresh one so a dropped connection never makes them run twice. After afork()the child opens its own
Pagination
- Every collection has
listfor one page as anEnvless\Result\Page,listAllfor everything as an array, anditerate, aGeneratorthat fetches the next page only when the loop gets there - Secondary collections get their own trio:
products->listProjects,products->access->list,variables->listHistory,versions->listEntries,webhooks->listDeliveries, each withlistAll*anditerate*forms - The walk ends on
hasMorebeing false or an empty page, and advances by the items actually received
Errors
Envless\Exception\ApiExceptioncarries the stableerrorCodeplusstatus,resource,field,requestIdandretryAfterSeconds, with predicates for the common branches:isAuth(),isScopeMissing(),isValidation(),needsUpgrade(),isNotFound(),isConflict(),isRateLimited()Envless\Exception\NetworkExceptionfor transport failures, withisTimeout()when the deadline passed.Envless\Exception\DecryptionExceptionandEnvless\Exception\WebhookVerificationExceptionfor the crypto and webhook helpers, andEnvless\Exception\InvalidArgumentExceptionfor a mistake in the call itself. All of them implementEnvless\Exception\EnvlessExceptionGET,HEAD,PUTandDELETEare retried on408,429,500,502,503and504with exponential backoff capped at 30 seconds, honouringRetry-After.POSTandPATCHare never retriedtimeoutis a hard deadline for each attempt, redirects and the response body included.0turns it off- A variable name is checked before anything is sent, and an unusable one throws the same
ApiExceptionthe API would, withrequestIdleftnull
Encryption
Envless::encryptValueandEnvless::decryptValue, byte-for-byte compatible with the TypeScript, Python and Ruby SDKs, the CLI, the dashboard and the runtimes: AES-256-GCM under a PBKDF2-SHA-256 key with 200,000 iterations and the workspace salt.v1:andv2:<iterations>:envelopes both decryptderiveWorkspaceKeyandimportWorkspaceKeyreturn anEnvless\Crypto\WorkspaceKeythat never prints its bytes and refuses to be serialised, forencryptWithKeyanddecryptWithKeyacross a batchisCiphertext,passphraseStrength,variableNameValidationandEnvless\Constants\EncryptionParametersrotateWorkspacePassphrase(...)re-encrypts an environment's live variables and its version history, and lists infailuresany version entry whose encrypted default cannot be rewritten because the entry's own value is not ciphertext- Plaintext, passphrases and workspace ids must be valid UTF-8, so binary data passed by mistake throws instead of producing ciphertext nothing else can read
Webhooks
Envless::verifyWebhookSignature(payload:, headers:, secret:)checks the HMAC-SHA256 signature in constant time and the timestamp drift, then returns the parsed event. It takes headers from an array in any case,$_SERVER, a PSR-7 request, or a Symfony or Laravel header bag
Runtime
- PHP 8.2 or newer with the
curl,jsonandopensslextensions, and no Composer dependencies - A PHPStan and Psalm array shape on
Envless\Typesfor every body and response, and every method declares the ones it takes and returns - Every request carries
User-Agent: envless-sdk-php/0.0.1