My slides from the Confoo 2022 conference are now available:
Thanks to everyone who attended and the engaging questions.
I have some content that I store in S3-compatible object storage, and wanted to be able to (a) push to that storage, and (b) serve items from that storage.
Easey-peasey: use the Flysystem AWS S3 adapter, point it to my storage, and be done!
Except for one monkey wrench: I’m using OpenSwoole.
What’s the issue, exactly?
By default, the AWS adapter uses the AWS PHP SDK, which in turn uses Guzzle.
Guzzle has a pluggable adapter system for HTTP handlers, but by default uses its
CurlMultiHandler when the cURL extension is present and has support for multi-exec.
This is a sane choice, and gives optimal performance in most scenarios.
Internally, when the handler prepares to make some requests, it calls
curl_multi_init(), and then memoizes the handle returned by that function.
This allows the handler to run many requests in parallel and wait for them each to complete, giving async capabilities even when not running in an async environment.
When using OpenSwoole, this state becomes an issue, particularly with services, which might be instantiated once, and re-used many times across many requests until the server is shutdown.
More specifically, it becomes an issue when coroutine support is enabled in OpenSwoole.
OpenSwoole has provided coroutine support for cURL for some time now.
However, when it comes to cURL’s multi-exec support, it only allows one multi-exec handle at a time.
This was specifically where my problem originated: I’d have multiple requests come in at once, each requiring access to S3, and each resulting in an attempt to initialize a new multi-exec handle.
The end result was a locking issue, which led to exceptions, and thus error responses.
(And boy, was it difficult to debug and get to the root cause of these problems!)
Guzzle allows you to specify your own handlers, thankfully, and the vanilla
use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use GuzzleHttp\Handler\CurlHandler; $client = new Client([ 'handler' => HandlerStack::create(new CurlHandler()), ]);
The next hurdle is getting the AWS S3 SDK to use this handler.
Fortunately, the S3 client constructor has an
http_handler option that allows you to pass an HTTP client handler instance.
I can re-use the existing
GuzzleHandler the SDK provides, passing it my client instance:
use Aws\Handler\GuzzleV6\GuzzleHandler; use Aws\S3\S3Client; $storage = new S3Client([ // .. connection options such as endpoint, region, and credentials 'http_handler' => new GuzzleHandler($client), ]);
While the namespace is
GuzzleHandlerin that namespace also works for Guzzle v7.
I can then pass that to Flysystem, and I’m ready to go.
But doesn’t switching to the vanilla
CurlHandler mean I lose out on async capabilities?
The great part about the OpenSwoole coroutine support is that when the cURL hooks are available, you essentially get the parallelization benefits of multi-exec with the vanilla cURL functionality.
As such, the approach I outline both fixes runtime errors I encountered and increases performance.
I like easy wins like this!
Unrelated to the OpenSwoole + AWS SDK issue, I had another problem I wanted to solve.
While I love Flysystem, there’s one place where using the AWS SDK for S3 directly is a really nice win: directly serving files.
When using Flysystem, I was using its
fileSize() APIs to get file metadata for the response, and then copying the file to an in-memory (i.e.
The repeated calls meant I was querying the API multiple times for the same file, degrading performance.
And buffering to an in-memory stream had the potential for out-of-memory errors.
One alternative I tried was copying the file from storage to the local filesystem; this would allow me to use a standard filesystem stream with PSR-7, which is quite performant and doesn’t require a lot of memory.
However, one point of having object storage was so that I could reduce the amount of local filesystem storage I was using.
As a result, for this specific use case, I switched to using the AWS S3 SDK directly and invoking its
The method returns an array/object mishmash that provides object metadata, including the MIME type and content l
Truncated by Planet PHP, read more at the original (another 1344 bytes)
Cal Evans hosts WordPress community leader David Bisset as they talk about the business and business models built around WordPress.
The post David Bisset talks about the business of WordPress appeared first on Voices of the ElePHPant.
A feature that we really needed to make our fiber integration complete is the cancellation of them. Or to be more
precise, the cancellation any awaited promise yielding operations in that fiber and as a consequence the fiber that
those are awaited in. This post goes into detail how different cancelation scenarios work for
the PR introducing it, and was originally part of that PR’s documentation
but was replaced by a simpler section.
One of the things that came up while upgrading packages is PSR-3’s new v2 and v3 releases. They add type hints to
methods and return type hints. For packages implementing this means that they can’t support all 3 versions. For
packages only consuming
psr/log all 3 versions can be used as you don’t have to build classes on them.
However, for packages implementing PSR-3 this suddenly became more complex. All of a sudden you need 3 major versions
if you want to support all PSR-3 versions. For a package that only implements PSR-3 this isn’t so much of an issue, but
when the implementation is embedded inside another package you all of a sudden reach dependency hell. And one thing I
learned while upgrading my packages is how deep our dependency on
psr/log goes these days.
The mistake I’ve made with at least one PR in the past few weeks is miss that a consumer of
psr/log is also an
implementer, and I missed that. So now I get to get back and make a new PR resolving that mess I introduced.
Cal Evans hosts WordPress community leader David Bisset as they talk about the WordPress community.
In this monthly update I explain what happened with Xdebug development in this past month. These are normally published on the first Tuesday after the 5th of each month. I am late this month. Sorry.
You can become a patron or support me through GitHub Sponsors. I am currently 45% towards my $2,500 per month goal. If you are leading a team or company, then it is also possible to support Xdebug through a subscription.
In January, I spend my time triaging issues, and planning for this year.
I spend most of my time reflecting on what I can do to make Xdebug even better in 2022, and I have come to the conclusion that this is going to be done through multiple improvements.
Creating an Xdebug Course: explaining in great detail how Xdebug works, how you use it, how you can get the most out of it, and many scenarios on how to set-up debugging in different environments. This needs to go beyond referential documentation pages, and will hence become a combined set of videos and tutorials, with examples and work-along exercises.
Developing an set-up-free debugger: A new tool that can be used through Xdebug Cloud, that would allow you to debug without IDE.
Xdebug Recorder and Player: A new feature in Xdebug which would allow for a full request to be stored in a file, including every intermediate state. Combined with a player, which would allow for replaying the request and interrogating every variable at every point during the execution of said script, through the debugging protocol and interacting with your IDE. The recorded files would be self contained, without needing access to the (original) source code. Besides “step over” it would also have a “step back”, and perhaps even a slider to slide back and forwards through time.
Rewriting Xdebug’s Profiler: so that it is more lightweight, and so that it can be enabled for specific parts of an application/request. In addition to this I am looking at sending the profiling data over the debugging protocol, so that visualisation tools do not need to find and read files.
Creating a profile analysis tool: To automatically analyse profiling files and apply logic so that it can point to the most likely cause of bottlenecks.
Let me know which one of these interests you most, and whether you would be willing to pay for such a tool.
Xdebug Cloud is the Proxy As A Service platform to allow for debugging in more scenarios, where it is hard, or impossible, to have Xdebug make a connection to the IDE. It is continuing to operate as Beta release. Packages start at £49/month.
If you want to be kept up to date with Xdebug Cloud, please sign up to the mailinglist, which I will use to send out an update not more than once a month.
I did not create any new Xdebug videos this month on my YouTube channel. But as I mentioned earlier, I am working on a more comprehensive course. Stay tuned!
Cal Evans hosts WordPress community leader David Bisset as they talk about the tech of WordPress.
Join host Cal Evans as he talks with PHP 8 author and trainer Doug Bierer about PHP 8, Doug’s new book, and all things happening with Doug.