posthog / posthog-php
PostHog PHP Library
Requires
- php: >=8.2
- ext-json: *
- symfony/clock: ^6.2|^7.0|^8.0
- symfony/polyfill-iconv: ^1.31
- symfony/polyfill-mbstring: ^1.31
Requires (Dev)
- ext-zlib: *
- open-telemetry/api: ^1.0
- open-telemetry/sdk: ^1.0
- phpunit/phpunit: ^11.0
- squizlabs/php_codesniffer: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 4.22.0
- 4.21.0
- 4.20.2
- 4.20.1
- 4.20.0
- 4.19.0
- 4.18.2
- 4.18.1
- 4.18.0
- 4.17.1
- 4.17.0
- 4.16.3
- 4.16.2
- 4.16.1
- 4.16.0
- 4.15.0
- 4.14.4
- 4.14.3
- 4.14.2
- 4.14.1
- 4.14.0
- 4.13.8
- 4.13.7
- 4.13.6
- 4.13.5
- 4.13.4
- 4.13.3
- 4.13.2
- 4.13.1
- 4.13.0
- 4.12.1
- 4.12.0
- 4.11.0
- 4.10.0
- 4.9.0
- 4.8.10
- 4.8.9
- 4.8.8
- 4.8.7
- 4.8.6
- 4.8.5
- 4.8.4
- 4.8.3
- 4.8.2
- 4.8.1
- 4.8.0
- 4.7.0
- 4.6.1
- 4.6.0
- 4.5.0
- 4.4.4
- 4.4.3
- 4.4.2
- 4.4.1
- 4.4.0
- 4.3.0
- 4.2.4
- 4.2.3
- 4.2.2
- 4.2.1
- 4.2.0
- 4.1.1
- 4.1.0
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 3.7.3
- 3.7.2
- 3.7.1
- 3.7.0
- 3.6.0
- 3.5.0
- 3.4.0
- 3.3.5
- 3.3.4
- 3.3.3
- 3.3.2
- 3.3.1
- 3.3.0
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.0
- 3.0.8
- 3.0.7
- 3.0.6
- 3.0.5
- 3.0.4
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.1.1
- 2.1.0
- 2.0.6
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- dev-feat/symfony-mailer-transport
- dev-feat/emails-integration-option
- dev-feat/laravel-queued-delivery
- dev-feat/emails-send
- dev-feat/laravel-env-options
- dev-fix/laravel-package-metadata
- dev-feat/laravel-request-ip
- dev-fix/laravel-personless-events
- dev-chore/laravel-numeric-tags
- dev-chore/single-approval-release
- dev-feat/laravel-anonymous-identity
- dev-feat/emails-cc-bcc-attachments
- dev-posthog/php-flat-exception-type-message
- dev-fix/null-property-serialization-sqq3ojkm
- dev-hp/exception-event-metadata
- dev-posthog-code/feature-flag-evaluations-default-value
- dev-ci/skip-heavy-ci-non-impacting-changes
- dev-fix/sdk-harness-options-20260630
- dev-fix/use-uuid-v7
- dev-feat/add-public-api-workflow-probe
- dev-chore/add-dependency-update-delays
- dev-chore/release-version-preflight
- dev-posthog-code/php-evaluate-flags-api
- dev-chore/release-workflow-push-main
- dev-feat/semver-targeting
- dev-matheus-vb/fix-failed-flag-overwrite
- dev-feat/get-feature-flag-result
- dev-ci/notify-release-rejected
- dev-vdekrijger-add-github-releaser-app
- dev-vdekrijger-bump-to-php85
- dev-fix/send-feature-flags
- dev-haacked/etag-support
- dev-cut-releases-when-deploying-new-version-php
- dev-feat/publish-evaluated-at-events
- dev-posthog/task-PHA-102
- dev-posthog/task-PHA-104
- dev-posthog/task-PHA-103
- dev-posthog/task-MCP-6
- dev-posthog/task-PHA-100
- dev-posthog/task-PHA-99
- dev-posthog/task-PHA-89
- dev-haacked/static-cohort-local-eval-fix
- dev-andyzzhao/dont-sort-variant-overrides
- dev-feat/transactional-queue-prevent-data-loss
- dev-fix/handle-flushing-events-with-32kb-limit
- dev-haacked/ignore-flag-dependencies
- dev-chore/bump-version
- dev-chore/use-flags
- dev-fix/try-this-instead
- dev-feat/feature-flag-payloads
- dev-fix-makefile-version
- dev-non-blocking-lib-curl
- dev-pnctl-fork-async
- dev-fix-bool-match
- dev-cohort-local-eval
- dev-add-flag-timeout
- dev-update-names
- dev-local-prop
- dev-prop-matching
- dev-neilkakkar-patch-1
- dev-fix-decide-v2
- dev-alisqi/master
- dev-fix-bug
- dev-groups-fix
- dev-timeout-configurable
- dev-php-unit/cs-version
- dev-update-version-3.0.1
- dev-ff-overrides
- dev-update-history-3.0.0
- dev-ff-remove-defaultvalue
- dev-turbo
- dev-group-analytics-flags
- dev-group-analytics-support
- dev-handle-host
- dev-feature-flags
- dev-zidsa/master
This package is auto-updated.
Last update: 2026-11-05 13:23:56 UTC
README
Please see the main PostHog docs.
SDK usage examples and code snippets live in the official documentation so they stay up to date.
Documentation
- PHP library docs
- Laravel integration docs
- Upgrade notes, including GeoIP configuration and compatibility
OpenTelemetry context
The optional PostHog\OpenTelemetry\ContextSpanProcessor adds session and person IDs to application spans inside this client's request context. Register it on the application's existing OpenTelemetry provider. OpenTelemetry remains an optional application dependency.
A client with no project key keeps request context but adds no span attributes. A valid-key client with the noop consumer still adds them.
The registered processor also enables request span enrichment when withContext() starts. This lookup skips Fibers because uninitialized OpenTelemetry Fiber storage can return another execution path's span. Child spans still use the active Fiber's PostHog context. New Fibers and queue jobs need explicit context propagation.
Existing span attributes take precedence. The processor preserves the application's provider, sampling, and exporters. See the PHP tracing guide and Laravel guide for setup.
Transactional email
emails() sends one email to one recipient, from an email template made transactional in PostHog or from content your code rendered, and reads what happened to it.
The client needs a project secret API key (phs_...) as secretKey. Create it with the "Send transactional email" preset (transactional_email:write) in your project settings, and add transactional_email:read if you read statuses with a key that can't send. Keys made with hog_flow:write still work. A client whose host is PostHog Cloud sends to its region's app host. For any other host, such as a reverse proxy, set the app_host option to your PostHog app host: email requests never go to a custom host. Loading flag definitions still sends the secret key to host, so when your proxy must not see the key, build the client with loadFeatureFlags: false and don't call loadFlags():
$client = new \PostHog\Client('phc_...', [ 'host' => 'ph.example.com', // your proxy 'app_host' => 'https://eu.posthog.com', ], loadFeatureFlags: false, secretKey: 'phs_...'); PostHog::init(client: $client);
PostHog::init('phc_...', ['host' => 'https://eu.i.posthog.com'], secretKey: 'phs_...'); // A template made in PostHog, by its ID $result = PostHog::emails()->send([ 'to' => 'user@example.com', 'template_id' => '0190f6d2-7c1e-7b3a-9f5e-1c2d3e4f5a6b', 'template_data' => ['name' => 'Ada'], // fills {{ variables.name }} 'distinct_id' => 'user_123', 'idempotency_key' => 'welcome:user_123', ]); // Content your code rendered, named by a key PostHog::emails()->send([ 'to' => 'user@example.com', 'template_key' => 'password-reset', 'subject' => 'Reset your password', 'html' => $html, 'distinct_id' => 'user_123', ]); // Later, such as in a job: for a few seconds after the send, get() throws not_found $status = PostHog::emails()->get($result->getId()); // queued, sent, delivered, bounced, ...
- Two forms. Pass
template_idwith optionaltemplate_data, ortemplate_keywithsubjectandhtml,textor both. The content form also takesfrom(a verified sender),reply_toandcapture_engagement(defaulttrue). PostHog derives a missingtextfrom thehtml. To send an email built with a template library such as React Email, render it to HTML in your app and passhtml. The SDK adds no rendering dependency. - Pass
distinct_id. Without it, the email's engagement events go to a person whose distinct ID is the email address. - A result means queued, not delivered.
send()returns aSendEmailResultwith the message ID.get()reads the status for 30 days after the send. For a few seconds after the send, it throwsnot_found. - Errors. A send that is not queued throws
PostHog\EmailError. Branch ongetErrorCode(), such asrecipient_suppressedormissing_app_host, or ongetErrorName(), such asvalidation_error. An invalid argument throws before any request, with aninvalid_*code. - Retries. Network errors, rate limits and an unavailable service are retried up to
email_request_max_retriestimes (default 2) with the same idempotency key, so a retry never sends twice. Each request times out afteremail_request_timeout_ms(default 15000). When a job runner already retries the send, pass'max_retries' => 0and anidempotency_keyderived from what the email is for, such aswelcome:user_123. A generated key does not survive a job retry. - Moving from another provider. Set the
migrated_fromoption, such as'resend', so PostHog knows where your sends came from.
Questions?
Join our Slack community.
Contributing
See CONTRIBUTING.md for local setup and test instructions.
Releasing
See RELEASING.md.