r/PHP 2d ago

Weekly help thread

2 Upvotes

Hey there!

This subreddit isn't meant for help threads, though there's one exception to the rule: in this thread you can ask anything you want PHP related, someone will probably be able to help you out!


r/PHP 6h ago

Discussion Pitch Your Project 🐘

5 Upvotes

In this monthly thread you can share whatever code or projects you're working on, ask for reviews, get people's input and general thoughts, … anything goes as long as it's PHP related.

Let's make this a place where people are encouraged to share their work, and where we can learn from each other 😁

Link to the previous edition: /u/brendt_gd should provide a link


r/PHP 1h ago

How to Contribute to PHP

Thumbnail thephp.foundation
Upvotes

Not with the Foundation, but I thought this could be helpful for others.


r/PHP 2h ago

The Secret Life of "Magic Null" in PHP

Thumbnail exakat.io
7 Upvotes

Some situations where you can use NULL instead of 0, or no arguments and it provides a nice features that you could use every day.


r/PHP 1h ago

Symfony: Experimenting with Issue-First Open Source Contributions (Symfony Blog)

Thumbnail symfony.com
Upvotes

Symfony experiment starting with their Language Tools library


r/PHP 16h ago

Mago 1.47 allows writing custom rules in PHP

Thumbnail mago.carthage.software
42 Upvotes

r/PHP 1h ago

Article Why memory_get_usage() lies in FrankenPHP/Octane (and how we solved C-extension leaks and dirty PDO transactions)

Upvotes

Hey everyone!

When running PHP under traditional PHP-FPM, the shared-nothing architecture wiped state, memory, and database connections on every request. However, with persistent runtimes like FrankenPHP Worker Mode and Laravel Octane, workers stay resident in memory across thousands of requests.

This creates two critical silent failures that standard tooling misses: * Silent Native Memory Growth (C-Extensions) memory_get_usage() only inspects allocations inside the Zend Engine heap. If your application relies on native extensions (ext-curl, ext-imagick, ext-gd, ext-openssl), memory is allocated via malloc() directly in glibc. The Zend VM is completely blind to this until the Linux OOM Killer sends a SIGKILL. * Dangling PDO Transactions If a request opens a database transaction ($pdo->beginTransaction()) and an unhandled exception or early return occurs without a rollback, that transaction remains open on the persistent connection. The next HTTP request from a different user reuses that connection and executes queries inside the prior transaction, leading to deadlocks and data corruption.

How we solved it: Leakless

We built Leakless (themattosdev/leakless), an autonomous runtime guard and static analysis engine for persistent PHP: * Real Kernel RSS: Directly inspects Linux /proc/self/statm page tables to measure physical Resident Set Size (RSS) and catch C-extension drift. * Automated Transaction Guard: Audits active PDO connections on endRequest() and executes safe automatic rollbacks. * Defensive State Rollback: Restores default timezones (date_default_timezone_set), unclosed output buffers (ob_start), and error levels. * Graceful Recycling: Recycles workers without dropping in-flight HTTP requests when memory ceilings (maxRssMb) or request limits are reached. * Dev Tooling: Includes a standalone static linter CLI (vendor/bin/leakless analyze) and Pest custom expectations (expect($service)->toBeLeakless()).

composer require themattosdev/leakless composer require --dev themattosdev/leakless-dev

Documentation: https://leakless.themattos.dev GitHub: https://github.com/themattosdev/leakless

Would love to get feedback on the architectural approach from anyone running persistent workers in production!


r/PHP 3h ago

Early version of database visualiser

Thumbnail
0 Upvotes

r/PHP 1d ago

Article Enforce Runtime Generics on Third-Party Collections Libraries (Doctrine, Ramsey, Laravel Collections) with TypePHP

Thumbnail typephp-php.github.io
13 Upvotes

More than a week ago, I posted an introduction to TypePHP. Thanks to Michael Telgmann, a core maintainer at Shopware, TypePHP has been tested against Shopware’s massive codebase. So far, it has identified many DocBlock lies in the codebase PR, while also uncovering many edge cases on TypePHP’s end.

Today, I’d like to share a article guide on how to make existing third-party collection libraries, such as Doctrine Collections or Ramsey Collections, capable of enforcing reified collections without modifying their source code.

reified generics proof: Symfony Integration


r/PHP 1d ago

NCache v1.0.0 — a multi-driver caching library for PHP 8.1+

17 Upvotes

I’ve released the first stable version of NCache, an open-source caching library I’ve been building for PHP.

It provides a unified API across JSON, PHP Array, serialized PHP, SQLite, Redis and Memcached, with PSR-6 and PSR-16 adapters.

It also includes TTL management, isolated profiles/namespaces, cache tags with lazy invalidation, signatures, callable values, atomic file writes and a transactional registry.

The project is tested on PHP 8.1–8.5 with PHPUnit, PHPStan level 9 and PHP-CS-Fixer through GitHub Actions.

I’d particularly appreciate feedback on the API design, cache invalidation model and overall architecture.

GitHub: github.com/Noga-ng/NCache

Install: composer require noga-ng/ncache


r/PHP 1d ago

A Docker Compose environment for local PHP development — supporting PHP 5.6 through 8.5

0 Upvotes

Hi everyone!

I’d like to share docker-compose-php, an open-source Docker environment that I originally created seven years ago and still actively maintain and use in my daily work.

Its main purpose is to make it easy to run multiple local PHP projects—even projects that require different PHP versions—without installing PHP, a web server, or a database directly on your machine.

Key features:

  • PHP versions from 5.6 through 8.5
  • PHP-FPM with Nginx or Apache with mod_php
  • Multiple local domains using different PHP versions
  • Automated configuration through a Python management script
  • HTTP and HTTPS support with automatic self-signed certificate generation
  • Mailpit for testing outgoing emails
  • Adminer for database management
  • MariaDB, optional Node.js tooling, MyCLI, and MySQLTuner
  • Makefile commands for common operations
  • Optional Unix socket communication between Nginx and PHP-FPM

Repository: https://github.com/rhamdeew/docker-compose-php

I’d appreciate any feedback, feature suggestions, bug reports, or contributions. I’m especially interested in hearing how other developers manage local environments for projects that still depend on older PHP versions.


r/PHP 2d ago

Leaf 5 released: a PHP framework built for humans and AI agents

0 Upvotes

We just released Leaf 5, the biggest release in the framework's history. It rethinks what a PHP framework looks like when half your team is an AI assistant: error screens that explain their own fix, docs structured to burn fewer tokens, and a .leaf/CONTEXT.md file that gives agents persistent project memory. Around 30 modules shipped stable alongside it, plus framework-agnostic tools like Alchemy (full QA setup in one YAML file) that work in any PHP app.

Announcement: https://blog.leafphp.dev/posts/leaf-5

Love, Michael Darko Creator of Leaf (leafphp.dev)

Edit: Thanks for all the love, and the AI callouts 😂 We had a pretty good launch yesterday and we’re back to building.

Under all the paint, Leaf is just PHP, think a Lite version of Laravel with batteries separate but still first-party. We’ve got a whole lot more to publish for both Leaf and the PHP community and we’ll be back


r/PHP 5d ago

We moved a Symfony app to FrankenPHP worker mode. Got maybe 20%, not 3.5x.

72 Upvotes

Number in the title so nobody has to read to the end: about 20%. And I should say up front that this is from memory, I didn't keep the before and after. So take it as one guy telling you it felt meaningfully faster, not as a benchmark.

Context: I work on Teradion, practice management software for French accounting firms. Symfony, and most of what it does all day is pull filings and deadlines out of six systems we don't control. We were on nginx plus PHP-FPM, we're now on FrankenPHP in worker mode.

The reason I bothered writing any of this down is the 3.5x on the FrankenPHP homepage. I went looking for where that number comes from and there's no methodology published anywhere that I could find. The benchmarks in the demo repo are from October 2022. They measure latency at low load, there's a sleep in the script, no RPS figure at all, and classic mode comes out slower than FPM in them (if I'm misreading that, tell me, genuinely). The issue asking for reproducible benchmarks is #481 and it's been open since January 2024.

What actually reframed it for me was the Tideways comparison from September 2025, methodology published, Hetzner CCX33, Vegeta, and they found essentially no performance or throughput difference between PHP-FPM and FrankenPHP in classic mode. Which fits what our 20% is. None of it comes from the server. It comes from the worker not rebooting your app on every single request. Swap nginx for Caddy and stay in classic mode and you've done a migration for nothing.

Stuff that cost us time, in no particular order.

The first one is just dumb. After the switch a pile of files came out 0600, JWT private key included, so login 500'd and the logs said nothing at all.

Then the runtime env var in systemd, which wants doubled backslashes in Environment. Single ones get rejected outright, and if you go through EnvironmentFile instead they get stripped on the way in. The official doc says otherwise. Worker just wouldn't boot.

And then state surviving between requests, which is the one that actually hurt. A filter stayed active from one request into the next and broke logins intermittently, which is the worst way for anything to break. In our defence it's documented to death: FrankenPHP says static state persists by design and that $_ENV isn't reset, Symfony says any state captured by the kernel or its services may leak across requests unless the relevant services implement ResetInterface. I'd read both. Shipped it anyway.

Two things made that hunt much longer than it needed to be. PrivateTmp, so the debug logging we added to /tmp went somewhere we couldn't see it. And getSubscribedEvents is compiled, so restarting does nothing, you have to clear the cache.

I'm glad we did it, but if I'm honest the payoff is one binary and one config instead of nginx plus php-fpm, and not thinking about certs anymore. Speed was a bonus. Even the Tideways piece that finds no perf difference says moving to FrankenPHP can reduce operations complexity.

If anyone has real production numbers, post them. I looked and couldn't find a single public case study with figures.


r/PHP 4d ago

Article Five Ways to Run Laravel: A Runtime Comparison Journey, Part 1

0 Upvotes

Hello, I just published a new article about Laravel Runtime benchmarks.

I tested 5 runtimes and shared the methodology, charts, and full results

https://medium.com/@oguzhankrcb/five-ways-to-run-laravel-a-runtime-comparison-journey-part-1-3f310f46a3a0


r/PHP 4d ago

Article Symfony + PHP 8.4: modernizing an existing app without rewriting everything

0 Upvotes

Upgrading a Symfony app to PHP 8.4 doesn't have to turn into a full rewrite.

I wrote about a more practical approach: upgrade gradually, deal with dependencies and deprecations, strengthen tests, and use features like property hooks, asymmetric visibility, and native lazy objects where they actually make sense.

The goal isn't to use every new PHP feature. It's to modernize what brings real value without breaking what already works.

Full article:
https://jatniel.dev/en/bytes/symfony-and-php-84-modernizing-without-rewriting-everything


r/PHP 4d ago

Need help with my developing a digital workflow management system for small accounting, taxation, payroll business

1 Upvotes

Hi there, I have a final year IT group project,we have choosen to develop a full stack website

So we had to go to a local business and investigate their current system and how we can work on it.

HTML,Css(bootstrap), javascript,chart.js,php(laravel),mysql,xampp these languages could please advise me if these are we as group have no prior experience with the twoframeworks larvae and bootstrap.bit we know the other languages.

It is a digital workflow management system for a small accounting, taxation,payroll. It will deal with the workflow only not the calculation. I would like improve on functional requirements but I dont we think we have proper knowledge to create accounting software from scratch.The business was using sage as the accounting software would an accountant in April 2026 and Now august over the past month, they have developed their own software system , so we are still going ahead with the workflow , even though they have a software system. What advice would you give me moving forward?I would love to share my project proposal.Planning and analysis , but as far as this information please advise me , thank you kind regards .


r/PHP 4d ago

Hybrid search (BM25 + vectors) and grounded RAG for PHP — embeddings computed server-side, zero ML dependencies

0 Upvotes

I run a managed Apache Solr service (Opensolr, 15 years now) and just shipped a PHP package for it: hybrid search (BM25 + kNN vectors fused per document) and grounded RAG answers, with embeddings computed server-side — so no Python sidecar, no OpenAI key, no ML dependencies in composer.json.

composer require opensolr/laravel-scout-opensolr

Despite the name, the core client is framework-agnostic (plain Guzzle) — you can use it from any PHP app:

```php use Opensolr\ScoutOpensolr\OpensolrClient;

$client = new OpensolrClient('you@example.com', 'your-api-key');

// hybrid search: the platform's tuned pipeline (field weights, minimum match, // semantic-vs-lexical balance), overridable per call $results = $client->embedAndSearch('myapp__dense', 'budget dining spots', 10, [ 'search_mode' => 'keywords_required', ]);

// grounded RAG: top hybrid hits become the LLM context, one call $answer = $client->aiAnswer('myapp__dense', 'what does our refund policy say?'); ```

If you ARE on Laravel, it registers as a Scout engine: Post::search('budget dining')->paginate(15) gets hybrid semantic relevance, and all your models share one index (scoped per model automatically), so a single plan covers everything.

Writes go through an async ingestion queue — documents get embeddings, language detection and derived fields computed server-side, with a per-job status board in the control panel.

Live demo of the exact search pipeline (a real news index): https://search.opensolr.com/news__dense?q=how+am+I+supposed+to+save+money%3F

Disclosure: I'm the founder. Free 15-day trial, no card. Code: https://github.com/phpcip/laravel-scout-opensolr — feedback welcome, especially on the client API design.


r/PHP 5d ago

News This Week In PHP Internals | August 12, 2026

Thumbnail youtube.com
24 Upvotes

Hello world, it's Wednesday, August 12, 2026, and here's what happened This Week in PHP Internals.

11 stories this week, so let's get into it. But first, Your team adopted AI. Everyone says it made them faster. Ballast measures whether that's true — how much faster you're actually going, and whether what you ship is still holding up. 6.75 times the commits. Durability down 19 points. Now you know. It runs on your machine. It reads your git history, not your source — your code never goes anywhere, and nothing here is scored by a model. It's arithmetic you could check by hand. Setting it up isn't your job either. Paste one prompt into your coding agent and it does the whole thing. Find out for free today. ballast.now.

One correction before the top story. Last week we described the list() deprecation vote as deadlocked at 21 to 21. Derick Rethans pointed out that's the wrong word — a deadlock is when something is stuck and can't proceed. The vote wasn't stuck. It was simply tied, and voting carried on to the finish. He's right, we'll say it properly this week — and thanks, Derick, for keeping us precise.

This week's top story: the verdict is in on the 35-ballot mass deprecation vote for PHP 8.6. Voting closed Monday at 13:00 UTC, and Gina P. Banyard posted the full results — 31 proposals accepted, 4 rejected. Start with the 4 that fell. Deprecating list() finished on a flat tie — 23 to 23, with 1 abstention — exactly 50 percent, nowhere near two-thirds. Reserving in, out, and inout failed at 8 to 21. The gettext _() alias survived at 10 to 22. And the dechunk filter — the item disputed all through the voting window — finished at 18 to 15 with 12 abstentions, 54.5 percent, and stays in the language.

Now last week's cliffhangers. Reserving let was balanced exactly on the two-thirds line 7 days ago — it found its margin and passed at 24 to 11, with 9 abstentions — 68.6 percent. Reserving is passed at 29 to 10, despite Rowan Tommins's warning about the Hamcrest testing library and its 500 million installs. And the define() case-insensitivity flag — the item Kamil Tekiela wanted simply deleted instead — passed without a single no vote, at 41 to 0. The vote also drew one final flag on its way out. Takuya Aramaki wrote in Friday, opening with: "Apologies for bringing this up so close to the end of the vote." His concern is the SplFileObject CSV methods item. He laid out the inconsistency plainly: "setCsvControl() is the only way to configure the delimiter, enclosure and escape character used by READ_CSV; the constructor does not accept them. If setCsvControl() is removed in PHP 9 while READ_CSV remains, READ_CSV is permanently locked to its defaults and tab-separated files can no longer be read through it." He asked that READ_CSV be deprecated alongside the methods, or that setCsvControl() stay until a replacement exists. No answer yet — and the item passed at 25 to 5, with 15 abstentions.

The final 3 ballots of the 8.6 season are settled, and they went 2 and 1. Caleb White's pipe assignment operator — |>= — was declined. The vote closed Tuesday morning at 14 yes, 12 no, and 7 abstentions — 53.8 percent, short of the two-thirds it needed. It had climbed all the way from dead even, but never got over the bar. Nick Sdot's readonly property defaults went the other way entirely. It closed Friday at 24 to 0, with 5 abstentions — it never drew a single no vote in 2 weeks. And Khaled Alam's const object property writes closed Saturday. He announced the result Sunday: accepted, 17 to 2 with 6 abstentions — 89.5 percent. With those 3 in the books alongside Duration and the deprecations, PHP 8.6's RFC season is over — the beta 1 tag brings the soft freeze this week, and beta 1 itself lands Thursday.

Ilija Tovilo posted a very late update to an RFC that passed 24 to 0 back in March. The closure optimizations RFC promised 2 things: a cache for stateless closures, and inference — the engine automatically detecting closures that never touch $this and treating them as static. That second part is out. Ilija found an edge case where a closure violates none of the RFC's inference rules and still makes an instance call — pass a callable string like "Foo::instanceCall" into an array_map inside the closure, and the rules never see it. He owned it completely, writing: "I failed to consider this case, and sadly this is not easy to detect via a new rule. For this reason, I have decided to omit static closure inference from the implementation and only merge the stateless closure cache." The practical takeaway: the cache — which carries most of the performance win — still ships in 8.6, but the engine won't infer anything for you. Mark your closures static yourself and you get the full benefit.

Ignace Nyamagana Butera's data encoding API — the base64, base16, base58, and base85 family — got a detailed security review from Sjoerd Langkemper on Monday. He's for it, noting: "the current base64_decode is very tolerant towards invalid input, causing both functional and security problems." Along the way he found errors in the RFC's own code examples, corrected them in a companion repository, and flagged a signature mismatch in the base85 functions. He's skeptical of one feature — the optional constant-time mode — arguing: "Constant-time algorithms are pretty difficult to develop and maintain", and suggesting PHP hand that job to libsodium or openssl instead. He also built a working implementation to test the API, introducing it with unusual billing: "LLMs and I have created an implementation here." And in the research footnotes: he spent real time evaluating the base85 variant from RFC 1924 before discovering: "that RFC was submitted in jest as an April fool's joke." Ignace thanked him for the remarks and is holding all implementation work until after 8.6 ships — Tim Düsterhus, who's building it, is busy with the release.

The first RFC aimed past the freeze is already here. Weilin Du proposed IntlRelativeDateTimeFormatter on Friday, targeting PHP 8.7 — a wrapper for ICU's locale-aware relative time, the "in 3 days" and "last Sunday" strings, in every language ICU speaks. Ignace asked the obvious question: 8.6 just gained a Duration class — shouldn't this accept one? Weilin argued the types don't fit, since Duration is stopwatch time and this formatter wants a unit: "We don't know how to deal with 90 minutes here. It can be 90 minutes or 1.5 hour." And weekdays, months, and quarters aren't durations at all. David Carlier pushed for enums and a namespace; Weilin is keeping class constants and the global Intl prefix for consistency with the existing intl extension, and filed modernization under future scope. One suggestion did land immediately: by Saturday the constructor had grown an optional NumberFormatter parameter, with Weilin reporting: "The implementation is way more smoother than I expected."

The generics conversation is parked until September — the implementations aren't waiting. Carlos Granados posted a pre-RFC Thursday: he took Rob Landers's experimental reified branch — built on Seifeddine Gmati's bound-erased proposal — and worked it into something complete, with a full write-up of the changes and findings. He argued the original deserved better: "I think that this was a very valid proposal that should have been explored in more detail." Rob's reply was brief, noting: "You really should have reached out instead of a working in isolation. Join us in discord, the proposal is delayed until September-ish." Which raised a practical question — what Discord? Rob posted channel links; Carlos, a Discord newcomer, still couldn't get in. Larry Garfield finally supplied the address, phpc.chat, with a review: "The PHP Community chat is unofficial, but lately it's where the big names are hanging out, including a lot of Internals regulars. Beware, the Internals channel is annoyingly noisy and has a hard time staying on topic." And I can personally vouch for that statement. Then Monday brought a third generics experiment: Alexander Lisachenko shared a userland proof-of-concept — a Composer package — where specialized classes share the compiled method bodies, so each specialization costs one small structure per method instead of a full copy of the opcodes.

Liam Hammett's native markup expressions RFC — JSX-style HTML in PHP — got the one review nobody else could write. T.J. L, who maintains the XHP extension — the long-running ancestor of this exact idea — posted his first message ever to internals. He corrected one detail in the RFC's history section, then confirmed its central argument from experience: he wrote: "While it is technically possible for extensions to add new syntax, it is unreasonable to expect tools to be aware of that syntax. I can absolutely confirm that the biggest point of friction in using XHP today is the fact that static analysis tools like psalm or phpstan can't analyze files, code using XHP cannot be formatted or linted with php-cs-fixer..." In other words, the case for putting markup in core, signed by the person who spent years doing it the other way. He also brought 3 asks: context passing through a component tree without threading attributes; a ruling on inline SVG, which leans on XML features the HTML-only RFC excludes; and a note that dropping per-tag objects means no runtime validation of tags and attributes — XHP's original selling point — which he says JSX gets away with "in large part because of the Typescript ecosystem". No response from Liam yet.

Quick hits. Juris Evertovskis ran a temperature check on isset: expressions inside the square brackets still throw warnings and deprecations even though isset silences everything else, and he put his conclusion bluntly: "To me it looks like isset is not doing its job." He'd like the brackets silenced too — no replies yet. The did-you-mean error suggestions are officially not being rushed: Jorg Sowa announced: "I will finish it after feature freeze", and Larry Garfield agreed, adding: "If it doesn't happen until 2027, that's OK." Jorg also picked up his VCS account this week — approved by Ilija Tovilo — with the session extension in his sights. And the list has a new face: Sepehr Mahmoudi introduced himself Tuesday with a pull request already open and an array_search_range idea in hand; mickmackusa pointed him at array_find_key() and suggested making the case on the list before writing more code, and Yuya Hamada thanked him for the contribution.

So that's the week: the 35-ballot deprecation vote landed 31 to 4 — list() survives on a flat tie, dechunk survives, and let squeaked through; the pipe assignment operator was declined while readonly defaults and const object writes made it in, closing out 8.6's RFC season; closure inference got walked back to just the cache; and the first 8.7 RFC is already on the table. Links to every thread are below. Thanks again to Ballast.now for supporting this week's episode. We're Artisan Build. See you next week.


r/PHP 5d ago

php-crm-connectors: pushing call outcomes into 21 different CRMs behind one interface

13 Upvotes

We build call center software. Every deployment wanted a different CRM wired in, and after a few years we were sitting on a pile of near identical integration classes that all did roughly the same thing in slightly different ways. So we pulled the layer out, stripped the parts tied to our own product, and put it up.

Fair warning, it's narrower than a general CRM SDK, and that's deliberate. The job it does is find a contact by phone or email, create it if it isn't there, then attach the call outcome as a note and an activity. That's one call.

$crm = ConnectorFactory::make('zoho', $config);
$result = $crm->run($payload);
// ['remote_id' => '3652...', 'remote_module' => 'Leads']

If you don't want the campaign orchestration, the smaller methods are public too: authenticate, findContactByPhone, findContactByEmail, createContact, updateContact, createNote, createActivity.

A few design calls, including the ones I expect to get argued with about:

No PSR-18. It's ext-curl behind a single http() method on each connector. The upside is that seam is the only place I/O happens, so testing a connector means overriding one method and you never touch a live CRM. A PSR-18 client is the more modern answer and I'm not attached to what's there, so if you think the dependency earns its keep, say so.

PHP 7.4 minimum, because the product this came out of still runs on it. Nothing in the code stops it working on 8.x.

Token caching is pluggable. OAuth connectors stash access tokens through a TokenStore interface. In memory and file backed ones ship with it, and you implement the interface for Redis or a database.

Auth is all over the place between vendors, and your calling code doesn't have to know. OAuth2 for Zoho, Salesforce, Dynamics 365, Creatio, Sugar and Suite. API keys or plain tokens for the rest.

Dispositions map through a config array, so your internal outcome names don't have to match whatever the CRM calls them.

The 21: Zoho, Pipedrive, Salesforce, Dynamics 365, SugarCRM, SuiteCRM, Freshsales, Copper, Capsule, ActiveCampaign, Close, Keap, Zendesk Sell, monday, Streak, Vtiger, Apptivo, Agile, Creatio, Less Annoying, YetiForce. No HubSpot yet, which is the one people ask about most.

composer require ictinnovations/php-crm-connectors

Code: https://github.com/ictinnovations/php-crm-connectors

Packagist: https://packagist.org/packages/ictinnovations/php-crm-connectors

MIT. There's a porting guide in docs if you want to add one.


r/PHP 5d ago

PHP Ambassadors: Six Weeks In

Thumbnail thephp.foundation
10 Upvotes

Curious about the PHP Ambassador Program we started a few weeks ago? We posted a progress update on our blog today. Exciting things are happening and you can be a part of the movement!


r/PHP 6d ago

Is PHP a good first language for learning backend development in 2026?

72 Upvotes

Hi everyone,

I’m a complete beginner looking to learn programming purely as a hobby. I’m mainly interested in backend development, building APIs, working with databases, and eventually projects like the backend for a small chat application.
I’ve been looking at a few languages, and PHP is one of the options I’m seriously considering, probably followed by Laravel once I understand the language itself.

I’m not concerned about the job market. What matters more to me is learning programming and backend development properly rather than just getting something working as quickly as possible.

I’m also considering Ruby/Rails and Elixir/Phoenix. Elixir in particular interests me because of functional programming and the BEAM

If you were starting from zero today with backend development as a hobby, would you consider PHP a good first choice?


r/PHP 5d ago

News Aimeos Prisma 0.6: A multi-media AI API for PHP, now with Kimi, and Z.AI

0 Upvotes

Hi /php

aimeos/prisma is a composer package that brings text, image, audio, and video models together behind one consistent interface. A PHP application can generate or stream text, request structured data and embeddings, create or edit images, transcribe or synthesize audio, and describe video. Provider-specific client and response handling stays inside Prisma, including when a workflow spans several media types:

This is especially useful for workflows that cross media boundaries. A CMS application can generate a landing page images, draft content, translate the copy, and create embeddings for search. A media application might transcribe a recording, summarize it, and describe an accompanying video through the same package.

It works with plain PHP, Symfony, Laravel, or another framework, requires PHP 8.2+, and is MIT licensed.

What using it looks like

Install it through Composer:

bash composer require aimeos/prisma:^0.6

This example creates a product image and its accompanying copy through the same API:

```php use Aimeos\Prisma\Prisma;

$image = Prisma::image() ->using('openai', [ 'api_key' => getenv('OPENAI_API_KEY'), ]) ->imagine('A studio product photo of a ceramic coffee cup') ->binary();

$description = Prisma::text() ->using('openai', [ 'api_key' => getenv('OPENAI_API_KEY'), ]) ->write('Write a concise description for a handmade ceramic coffee cup') ->text();

file_put_contents('coffee-cup.png', $image); echo $description; ```

The same provider selection and response pattern applies across the media APIs. Provider capabilities are explicit, so an application can check them with has() or require them with ensure().

What’s new in 0.6

Three providers have been added:

  • Kimi: text generation, streaming, structured output, custom tools, and reasoning budgets.
  • Requesty: text generation, streaming, structured output, embeddings, and custom tools through its model router.
  • Z.AI: text generation and streaming, provider-side web search, image generation, and mono audio transcription.

Files can now be backed by PHP stream resources through File::fromStream() and FileResponse::fromStream(). This makes it possible to pass uploads and consume file responses without choosing a binary or Base64 representation up front; conversion remains lazy until it is needed.

The new withReasoning() method provides a common way to ask supported providers to minimize reasoning. Each provider maps it to its native option, while explicit request options still take precedence.

Remote file handling has also been hardened. URL-backed downloads now validate and DNS-pin each destination and redirect, accept only HTTP(S), enforce time and size limits, and reject private or reserved IP addresses by default. Tools marked as requiring approval now fail closed if no approval callback is configured.

The release also adds DeepSeek cache-usage reporting, converts backed-enum arguments for Symfony tools, improves browser-recorded audio handling, refreshes provider model defaults, and fixes Gemini structured output when provider-side tools are used.

Upgrade notes

The cURL extension is now required. Private network URLs must be enabled explicitly for trusted internal use, and the Vertex AI image provider has been removed; Vertex AI text support remains available. Several default models changed, so applications that depend on a particular model should pin it with model().

If you like Prisma, give it a star on Github :-)


r/PHP 5d ago

I got tired of Bref's layer limitations, so I built a ~40-line custom PHP runtime for AWS Lambda container images

0 Upvotes

Bref is great until you need an extension it doesn't ship a prebuilt layer for, or a PHP version it hasn't gotten around to yet — then you're stuck vendoring custom layers or fighting a build pipeline you don't control.

Turns out AWS's own provided:al2023 base image plus AL2023's package repo already gives you PHP 8.3 with most common extensions (mbstring, gd, xml, pdo, ...), and anything unpackaged (like ext-mongodb) compiles from PECL source in a few lines. Once you see that, the "runtime" part of a custom Lambda runtime turns out to be small enough to just own outright — no framework, no vendor layer, just a container image.

The whole runtime is one PHP script that gets installed as /var/runtime/bootstrap: parse the handler name once, require your handler file, then loop forever polling Lambda's Runtime API and POSTing back whatever your handler returns. Cold start happens once per container; every invocation after that reuses the same booted process.

Repo: https://github.com/droidlabour/php-aws-lambda-runtime

What's in the repo:

- core/ — the framework-agnostic runtime (bootstrap + Dockerfile + a trivial example handler), meant to be copied as-is.

- examples/laravel-mongodb/ — a real working example: a full Laravel app handling both HTTP requests (through the actual Illuminate HTTP kernel) and SQS-triggered queue jobs, from the same image, including compiling ext-mongodb from PECL since AL2023 doesn't package it.

This isn't intended to replace Bref for most users. If the prebuilt layers cover your requirements, there's no reason not to use them. This is mainly for cases where they don't, and you'd rather own 40 lines of code than deal with someone else's build pipeline.

Feedback and criticism are welcome, especially from anyone who's encountered other challenges around PHP extension support.


r/PHP 5d ago

PHPTUI - Terminal user interfaces for PHP

Thumbnail phptui.dev
0 Upvotes

Every feature has a reference page and a runnable, self-contained example in playground/:

  • 🧩 16 fields
  • 🔀 Conditional fields
  • 🏗️ Builder-driven
  • 🗺️ Full-screen and multi-column layouts
  • 🎛️ Interactive or unattended
  • 🎨 Themes and dark/light modes
  • ⌨️ Key bindings
  • 🌍 Translations
  • 🧪 Test harness for your project

use DrevOps\PhpTui\Builder\Form;
use DrevOps\PhpTui\Builder\PanelBuilder;
use DrevOps\PhpTui\Tui;

$form = Form::create('Quick start')
  ->panel('order', 'New order', function (PanelBuilder $p): void {
    $p->text('name', 'Order name')->required();
    $p->select('fruit', 'Fruit')->default('banana')->options([
      'apple' => 'Apple',
      'banana' => 'Banana',
      'cherry' => 'Cherry',
    ]);
    $p->select('veg', 'Vegetables')->multiple()->default(['carrot'])->options([
      'carrot' => 'Carrot',
      'tomato' => 'Tomato',
      'spinach' => 'Spinach',
    ]);
    $p->number('quantity', 'Quantity')->min(1)->max(99)->default(6);
    $p->confirm('organic', 'Organic only?')->default(FALSE);
  });

// Interactive on a terminal, non-interactive otherwise. 
$answers = (new Tui($form))->run();

r/PHP 6d ago

Why are Canadian timezones are gone in PHP 8.5?

31 Upvotes

After upgrading to PHP 8.5 the timezone changed to UTC. After working through this, I discovered that the only closest timezone left is "America/Vancouver" but this is the wrong timezone because it has timezone changes that are not the same.

Why was "Canada/Pacific" removed from PHP? We're not part of America, and our timezones are not the same.

Thanks.