hpatoio/bitly-bundle
Symfony bundle that integrates the Bitly API for URL shortening and link management. Provides service configuration to generate short links from controllers or templates. Small, low-activity package; verify maintenance and API compatibility before use.
Install and register
composer require hpatoio/bitly-bundle:^1.0.5
Pin the constraint explicitly. The bundle predates Flex, so there is almost certainly no recipe. Register it manually:
// Symfony 4+: config/bundles.php
Hpatoio\BitlyBundle\HpatoioBitlyBundle::class => ['all' => true],
// Symfony 2/3: app/AppKernel.php
new Hpatoio\BitlyBundle\HpatoioBitlyBundle(),
Configure the token
The 1.0.5 release exists solely to fix the bundle's YAML format for Symfony 4 (See #6). That means the Symfony 4 config file format is now the supported one — write it under config/packages/:
# config/packages/hpatoio_bitly.yaml
hpatoio_bitly:
access_token: '%env(BITLY_ACCESS_TOKEN)%'
If you are maintaining a Symfony 2/3 app, the pre-1.0.5 YAML shape is what you get from older tags; upgrade or hand-write the equivalent. Either way, don't guess the key names — dump them:
bin/console config:dump-reference hpatoio_bitly
If %env()% fails to resolve at compile time, that is the bundle's extension not being env-aware (the YAML fix did not touch the PHP Configuration/Extension classes). Bind it as a parameter instead:
# config/services.yaml
parameters:
bitly_access_token: '%env(BITLY_ACCESS_TOKEN)%'
Find the service before writing code
bin/console debug:container bitly --show-private
bin/console debug:container --tag=data_collector | grep -i bitly
The bundle registers a client service (something like hpatoio_bitly.api or bitly.api) that is a thin wrapper around hpatoio/bitly-api, decorated with a logging layer. Grab the real ID from the command above; the exact ID has varied across versions and the 1.0.5 patch did not add autowiring aliases.
First use case: shorten a URL
// src/Controller/ShareController.php
public function share(string $url): Response
{
$short = $this->get('hpatoio_bitly.api')->shorten($url);
return $this->json(['short' => (string) $short]);
}
Verify the return shape (plain string vs. object vs. array) against the hpatoio/bitly-api README for the pinned version — the client returns raw-ish decoded responses and this is the number one source of "works in my head, not in prod" bugs. The 1.0.5 release changed configuration YAML only; it did not touch the client or its response handling.
Then open any page in dev and check the web profiler toolbar for a Bitly panel. That panel is the fastest sanity check that the token, HTTP client, and logging decorator are all wired correctly.
Wrap the client in your own service. Never inject the bundle's client directly into controllers. A single ShortLinkManager gives you one place to handle caching, retries, error degradation, and tests.
// src/ShortLink/ShortLinkManager.php
final class ShortLinkManager
{
public function __construct(
private object $bitly, // the bundle service
private CacheItemPoolInterface $cache,
private LoggerInterface $logger,
) {}
public function shorten(string $url): ?string
{
try {
$key = 'bitly.shorten.'.hash('xxh3', $url);
return $this->cache->get($key, function (ItemInterface $item) use ($url) {
$item->expiresAfter(86400 * 30);
return (string) $this->bitly->shorten($url);
});
} catch (\Throwable $e) {
$this->logger->warning('Bitly shorten failed', ['url' => $url, 'e' => $e]);
return null; // caller falls back to the full URL
}
}
}
Autowire via an alias. Old bundles register private services with no alias, and 1.0.5 did not change that. Add one in config/services.yaml:
services:
hpatoio_bitly.api: '@Hpatoio\Bitly\Client' # or whatever debug:container shows
Then type-hint the client class (or your own interface) instead of using $this->get().
Persist the back-half / bitlink ID, not just the short URL. Bitly analytics endpoints need the link identifier, not the shortened string. If you plan to pull clicks/referrers later, store both:
$entity->setShortUrl($short); // "http://bit.ly/2xY..."
$entity->setBitlyId($idFromResponse); // needed for clicks(), countries(), referrers()
Resolving the ID after the fact means an extra expand/lookup round trip per record — do it at write time.
Cache aggressively on the read path. Expanding an inbound short link on every request is a latency tax and a rate-limit hazard. Short link → long link is immutable in practice; cache it forever (or a year).
Move writes off the request thread. Shortening inside a web request couples your page load to Bitly's availability and to your token's rate budget. If you generate links in bulk (campaigns, invites, imports), dispatch a Messenger job and write the result back to the entity.
Use it for a Twig helper if sharing is a UI concern:
{{ bitly_shorten(app.request.uri) ?? app.request.uri }}
Register the Twig function in your own extension that delegates to ShortLinkManager, not to the bundle client directly.
Analytics sync as a console command. The bundle adds nothing for scheduling; you own it.
// src/Command/SyncBitlyStatsCommand.php
foreach ($repo->findLinksNeedingStats() as $link) {
$stats = $this->bitly->clicks($link->getBitlyId(), 'month'); // verify signature
$repo->updateStats($link, $stats);
usleep(100_000); // be polite; rate limits are real
}
Profiler as a debugging workflow. In dev, the data collector gives you the request URL, response, timing, and status per Bitly call. Use it to spot:
try/catch swallowed,AbstractBundle, and a Configuration tree that may not resolve %env()% at compile time even though the YAML file itself is now SF4-valid.1.0.5 is a compatibility patch, not a maintenance commitment. There is no changelog trail for the HTTP client, Guzzle constraint, or the profiler template. Keep composer.lock and review diffs before any bump beyond 1.0.5.access_token + login) and early v4 (OAuth bearer), and 1.0.5 did not move that line. Sending a v4 token to v3 endpoints returns confusing auth errors, not a clean 401. Test one shorten by hand with curl before blaming the bundle.$this->get(). On Symfony 4+, the client may not be publicly accessible from the container. Either add the alias above or inject it properly — don't fight the container.access_token query parameters end up in var/log/dev.log and, worse, in production if you raise the log level. Check the decorator's output format and scrub/redact before enabling in prod.RetryableHttpClient or stop()/retry() on your own decorator) and a token-bucket if you're doing bulk work.ShortLinkManager, tests use a stub. For integration tests, swap the client service with a fake via services_test.yaml — the bundle's client is not final and can be decorated.composer.lock is your friend; a composer update can silently break a transitive Guzzle major.ShortLinkManager around it and never touch the client again, (3) add your own data collector only if the shipped one is too coarse — it's tied to old profiler template APIs and is the least portable part of the bundle.How can I help you explore Laravel packages today?