stingus/jira-bundle
A PHP bundle for Jira integration that provides configuration and service bindings to connect your application to Jira issues, projects, and workflows. It simplifies Jira API access and dependency injection, reducing setup boilerplate so teams can add Jira connectivity faster.
stingus/jira-bundle is a Symfony bundle that wires up an OAuth 1.0a–signed HTTP client for talking to Jira (typically Jira Server / Data Center, where 1.0a is still the norm). The first thing to do is check compatibility before anything else — the last tagged release is from late 2019, so expect it to target Symfony 3.4/4.x and PHP 7.x. If you're on Symfony 5+, expect to fork the repo and bump composer.json constraints (the surface area is small, so this is usually a 15-minute patch).
Minimal steps:
composer require stingus/jira-bundle (or dev-main if you've forked).config/bundles.php on Flex projects, or AppKernel::registerBundles() on Symfony 3.config/packages/stingus_jira.yaml) with your consumer key/secret, token/secret, and Jira base URL. Pull every value from environment variables — see Gotchas./rest/api/2/myself. This is the fastest way to confirm signing works before you build anything on top of it.Where to look first, in order:
DependencyInjection/Configuration.php — the authoritative list of config keys and defaults.Resources/config/services.xml (or .yaml) — the actual service IDs you'll inject.DependencyInjection/*Extension.php — how config maps onto the client.StingusJiraBundle.php — confirms it's a thin DI wrapper, not a full API SDK.First real use case: inject the Jira client into a small custom service and issue a GET /rest/api/2/issue/{key} call. If that 200s with a JSON payload, you have working OAuth 1.0a plumbing and can build up from there.
Wrap the client, don't scatter it. The bundle hands you a signed HTTP client, not a domain API. Create your own App\Jira\JiraClient that injects the bundle's client and exposes methods like createIssue(Project $p, array $fields), transition(IssueKey $k, string $name), searchJql(string $jql). If you ever swap to a different integration approach (Jira Cloud + API tokens, or bitly/oauth1 directly), you change one class.
Split consumer credentials from token credentials. OAuth 1.0a has two layers: the application (consumer key/secret) and the user (token/token secret). For multi-tenant or per-user integrations you need to store each user's token pair and build a signed client per user. The bundle typically configures a single client from YAML — so if you're doing per-user auth, you won't get much from the bundle beyond the signing logic and will want to instantiate clients dynamically.
Common endpoints you'll write within a week:
GET /rest/api/2/myself — health check.POST /rest/api/2/issue — create an issue from a webhook payload (e.g. a customer support ticket).POST /rest/api/2/issue/{key}/comment — sync comments both ways.POST /rest/api/2/issue/{key}/transitions — move issues through workflow (two-step: GET available transitions, then POST the id).GET /rest/api/2/search?jql=… — polling for state changes when webhooks aren't available.Pair with Jira webhooks, don't poll. The bundle is for outbound calls. Use Jira's outgoing webhooks to tell your app "issue updated" — then respond with API calls. Polling JQL on a cron is a fallback; it's slow and hits rate limits quickly on busy instances.
Integration tips:
$container->get() inline.decorates: key) rather than editing vendor code. Redact the Authorization header before logging — OAuth 1.0a signatures are bearer-equivalent.This package is effectively unmaintained. 5 stars, 0 dependents, no release since 2019. Treat it as a reference implementation, not a long-term dependency. Fork it into your own namespace, bump constraints, and pin to your fork. You'll be doing this anyway if you're on a modern Symfony.
OAuth 1.0a signing is fragile. The signature is computed over a canonicalized request — method, URL, sorted and percent-encoded parameters, and the signing key derived from consumer secret + token secret. Failure modes:
+ in query params) → 401 oauth_problem=signature_invalid.401 oauth_problem=timestamp_refused. Ensure NTP is running on the host.401 oauth_problem=nonce_used. If you retry a request, sign it fresh.Jira Cloud is not the target. Cloud has moved to API tokens and OAuth 2.0. If you're integrating with Cloud, you probably don't want this bundle at all — an API token with basic auth (or OAuth 2.0 3LO) is simpler and supported. This bundle is for Server/Data Center self-hosted installs.
Secrets leak trivially. The consumer secret and token secret are effectively bearer credentials. Keep them out of committed YAML and out of logs. In Symfony, prefer %env(JIRA_CONSUMER_SECRET)% and use a .env.local that isn't committed.
Guzzle version is the real constraint. Bundles of this era typically pull in guzzlehttp/guzzle 5.x or 6.x, or rely on eightpoints/guzzle-bundle. Neither plays well with Symfony's HttpClient, and Guzzle 5 is EOL. When you fork, move to Guzzle 7 + guzzle/plugin-oauth — but note that plugin was removed in Guzzle 7, so plan on either using an out-of-tree OAuth 1.0a signer or implementing the ~40 lines of signing yourself.
Error handling is your job. The bundle won't normalize Jira's error envelope. Parse errorMessages and errors from 400 responses (Jira returns both, sometimes with per-field detail). Distinguish 401 (auth/signing) from 403 (auth OK, permission missing) from 404 (bad key or no browse permission — Jira deliberately conflates these).
Extension points are limited. There's no event system or plugin layer — it's a DI wrapper around a client. If you need custom behavior, decorate the service, or skip the bundle and copy its signing logic into your own client. Given the size of the bundle and the lack of maintenance, the latter is often the pragmatic choice.
Debugging checklist when a call fails:
curl the same endpoint with the same params — if curl works and the bundle doesn't, it's signing.How can I help you explore Laravel packages today?