0% found this document useful (0 votes)
88 views36 pages

phparchitect-2024-04

The April 2024 issue of php[architect] features articles on PHP security, an API Handler library, and reflections on the php[tek] 2024 conference. It includes discussions on improving codebases, dependency injection, and preparing for Global Accessibility Awareness Day. The magazine aims to provide valuable insights and tools for PHP developers to enhance their skills and practices.

Uploaded by

marek.rode
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
88 views36 pages

phparchitect-2024-04

The April 2024 issue of php[architect] features articles on PHP security, an API Handler library, and reflections on the php[tek] 2024 conference. It includes discussions on improving codebases, dependency injection, and preparing for Global Accessibility Awareness Day. The magazine aims to provide valuable insights and tools for PHP developers to enhance their skills and practices.

Uploaded by

marek.rode
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

APRIL 2024

www.phparch.com Volume 23 - Issue 4

The Magazine for PHP Developers

DEEP DIVING
PHP SECURITY
API Handler
php[tek] 2024 in Review

Also Inside:
            ­ € 
           ‚   €ƒ  
a php[architect] print edition

Learn how a Grumpy Programmer approaches


improving his own codebase, including all of the
tools used and why.
The Complementary PHP Testing Tools Cookbook is
Chris Hartjes’ way to try and provide additional tools
to PHP programmers who already have experience
writing tests but want to improve. He believes that
by learning the skills (both technical and core)
surrounding testing you will be able to write tests
using almost any testing framework and almost any
PHP application.
Available in Print+Digital and Digital Editions.

Order Your Copy


https://phpa.me/grumpy-cookbook
APRIL 2024
Volume 23 - Issue 4

2 Bad News, Good News 19 Making the Difficult Choice


Eric Van Johnson 2500 Feet
Edward Barnard
3 Feature: API Handler
Tim Lytle
25 Go vs Rust: A Guide for
PHP Developers
9 My First php[tek] RADAR
Drew Halverston Matt Lantz
11 Definitely Not My First
php[tek] 27 Dependency Injection with
Beth Tucker Long PHP-DI
PHP Foundry
Oscar Merida
12 Security and Side Channels
Security Corner
Eric Mann
34 The Changing Face of
Networking
finally{}
16 Getting GAAD Ready Beth Tucker Long
Barrier-Free Bytes
Maxwell Ivey

Edited by invigorated post conference organizers Advertising Copyright © 2024—PHP Architect, LLC
php[architect] is published twelve times a year by: To learn about advertising and receive the full All Rights Reserved
PHP Architect, LLC prospectus, contact us at [email protected]
Although all possible care has been placed in
9245 Twin Trails Dr #720503 today!
assuring the accuracy of the contents of this
San Diego, CA 92129, USA magazine, including all associated source code,
Contact Information: listings and figures, the publisher assumes no
Subscriptions General mailbox: [email protected]
Print, digital, and corporate responsibilities with regards of use of the information
Editorial: [email protected] contained herein or in all associated material.
subscriptions are available. Visit
https://www.phparch.com/magazine to subscribe Print ISSN 1709-7169 php[architect], php[a], the php[architect] logo, PHP
or email [email protected] for more Digital ISSN 2375-3544 Architect, LLC and the PHP Architect, LLC logo are
information. trademarks of PHP Architect, LLC.
Editorial

Write For Us
Bad News, Good News If you would like to contribute,
contact us, and one of our editors
Eric Van Johnson will be happy to help you hone
your idea and turn it into a beauti-
Bad news first, php[tek] 2024 is over. Good news, you don’t have ful article for our magazine.
to listen to us talk about php[tek] 2024 as much anymore. php[tek] Visit https://phpa.me/write
or contact our editorial team
2025, however, is just around the corner, and there is no time like
at [email protected] and get
the present to put the bug in your supervisor’s ear about attending. started!

Just kidding, sort of. from a first timing and the other from a
In all seriousness, php[tek] 2024 was a seasoned veteran.
Stay in Touch
blast. I was blown away when I asked how Matt Lantz, in his Radar column ‘Go vs Don't miss out on conference,
many people was this their first php[tek] Rust: A Guide for PHP Developers,’ exam- book, and special announcments.
and saw half the room raise their hands. ines two other languages through the lens Make sure you're connected with
The conference itself was fantastic. There of a PHP developer. In contrast to the cine- us.
were so many great talks, both in the matic portrayal of hacking, real-world • Subscribe to our list:
tracks and in the hallways. The Uncon attacks often involve reconnaissance and https://phpa.me/sub-to-updates
track this year was next level. If you leverage side-channel tactics, focusing • Mastodon:
attended virtually or in person, the talks on gathering information and exploiting @[email protected]
will be available on PHPTek.tv (https:// vulnerabilities without direct system
• Twitter: @phparch
phptek.tv) in the next few weeks. If you compromise. These indirect approaches,
weren’t able to attend and didn’t purchase such as timing-based attacks, highlight • Facebook:
a virtual ticket, you can still do so at the subtleties of modern cybersecurity http://facebook.com/phparch
https://tek.phparch.com/register and threats. In Security Corner, Eric Mann
watch the talks once they are published takes a deeper dive into this topic with
along with the talks from php[tek] 2023, his‘Security and Side Channels’column.
which are available now. Unlike code, life is not always a 0 or 1,
php[tek] is truly a premiere event that a true or false, or a yes or no. Sometimes,
has changed the lives of many devel- choices must be made, and sometimes,
opers (including mine) over the years. they are complex. In the 2500 Feet
Throughout this issue, you will see a few column, ‘Making the Difficult Choice,’
articles from attendees of php[tek] 2024. Edward Bernard shares real stories about
There will also be plenty of images from when a difficult choice needed to be
the conference. If you attended, you made. Global Accessibility Awareness
might see yourself in one of them. If Day (GAAD) is coming up. Learn every-
you didn’t attend, you can see what you thing you need to know in Barrier-Free
missed out on. Bytes ‘Getting GAAD Ready’ by Maxwell
Now, let’s get into this month’s maga- Ivey.
zine. To kick things off, Tim Lytle (hey, In PHP Foundry, Oscar Merida covers
😊
he was a speaker at php[tek] 2024 ) ‘Dependency Injection with Pimple,’ where
writes about‘API Handlers,’what they are, he helps you get control over spaghetti
andhow to create them, use them, and code by leveraging dependency injec-
extend them. As a backend developer, I tion. And in finally{}, Beth Tucker Long
find these sorts of articles valuable. You returns to discuss ‘The Changing Face
will also get two additional comple- of Networking.’ She touches on how an
menting Feature contributions from Beth accelerated shift to remote work brought
Tucker Long and Drew Halverston on challenges for companies unprepared
their experiences at php[tek] 2024, one technologically and lacking remote lead-
ership skills.
Download the Code
Archive:
https://phpa.me/April2024_code
FEATURE

Feature: API Handler


Tim Lytle
PhoneBurner has released an open source API Handler library that provides a pattern for
building API handlers that is both flexible and easy to use. This article explains the motivation
behind the library, the pattern it implements, and how to use it.

In this specific case that means we built our first API


Why? handler for one internal API. And then for the next API, we
That’s a fair question. Do we need yet another API frame- copy / pasted that code and made what changes were needed.
work? And then we did it again, and again. Each time considering
First, it’s worth highlighting what API Handler really is. what could be solved generally, and what changes we were
This is as much a framework as the Pirate Code is a set of rules making that were specific to the current implementation.
- really, they’re more of what you’d call ‘guidelines’. At the core,
this is really a pattern to follow, with the core boilerplate code
already done for you - and completely replaceable if needed.
Three Basic Things
We’d already used the strangler pattern1 to wrap our legacy As we looked to generalize building an API RequestHandler,
MVC router into the fallback handler for a PSR-15 middle- three common components became obvious.
ware stack. We wanted to modernize our API code, and First what we’ll call a ‘resolver’, something that given a
leveraging middleware to move from our rather interesting ServerRequestInterface can return a thing. What thing? That
API ‘controller’ pattern to a handler-based pattern seemed depends on what API is being built, but it’s the thing that the
like the right path forward. API is about. If this is a user API, it’s going to be the user
It’s very likely we could have used an existing API frame- model, or entity, or projection, or aggregate root, or whatever
work; however, our codebase has been around since PHP 4 you use to represent a user. So the signature of a Resolver is:
(and is now on PHP 8), so chances are we’d run into some interface Resolver
interesting integration issues. We’d also need to work to see {
consistent adoption of both the framework as well as the public function resolve(
specific implementation choices we made for our context. ServerRequestInterface $request
None of those are blockers, but they gave us enough pause ): object;
}
to stumble across something very useful before we found an
existing library to use. Second we needed a Transformer. This also takes a Server-
RequestInterface as well as some other object - continuing
Goals the preceding example, whatever thing represents the user in
your code - and it returns a representation of the user. For us
We wanted this new handler code to be very generic and
this would be a HAL+JSON representation, but to make sure
reusable. We not only wanted to improve the code that
it’s as flexible as possible going forward, the signature is as
currently lived in what we called ‘controllers’, but we also
open as you can be.
wanted to introduce a pattern that was easy for developers to
implement and test. That’s just not the case when one handler interface Transformer
takes one set of dependencies and another handler takes a {
different set. This new handler concept could not be tightly public function transform(
object $object,
coupled to the rest of our code.
ServerRequestInterface $request
Let’s take a quick detour for a moment and talk about what ): mixed;
it takes to design a generic solution - what we were after in }
this case. Generally the generic solution will not be obvious at
the start, so it can be helpful to do the work a few times before Finally we need something to manage modifying the state
you start to see the general solution. Build things that are of our domain, and since naming things is hard we called
generalized, but don’t introduce interfaces. That allows your this a Hydrator. Given a ServerRequestObject and, depending
second and third versions to be as flexible as needed, and on the context, also the thing that needed to be updated it
then looking across those solutions the generalized version returns the updated thing…maybe. The interface shows that
should become apparent. the Hydrator has the greatest complexity, as would be expected.

1 https://martinfowler.com/bliki/StranglerFigApplication.html

www.phparch.com \ April 2024 \ 3


Feature: API Handler

interface Hydrator public function handle(


{ ServerRequestInterface $request
public function create( ): ResponseInterface {
ServerRequestInterface $request $resource = $this->resolver->resolve($request)
): object; $response = $this->transformer
->transform($resource, $request);
public function update( }
ServerRequestInterface $request,
object $object Which can be more succinctly expressed as this:
): object;
public function handle(
public function delete( ServerRequestInterface $request
ServerRequestInterface $request, ): ResponseInterface {
object $object return $this->transformer->transform(
): ?object; $this->resolver->resolve($request),
} $request,
);
}
Working Together As you’d imagine, anUpdateHandlerwould look like:
As we started playing around with these concepts in a
public function handle(
RequestHandler it became apparent that these three compo- ServerRequestInterface $request
nents worked together in a very consistent way regardless of ): ResponseInterface {
the API. return $this->transformer->transform(
If an API request is simply fetching some data, the Resolver $this->hydrator->update(
finds the resource which is then passed to the Transformer. $request,
$this->resolver->resolve($request),
If we’re updating a resource, the Resolver still finds the ),
resource, but now the resource and the request are passed to $request
the Hydrator to update internal state and return the updated );
resource. That updated resource is then passed to the Trans- }
former.
And if we’re making a resource, the request is passed to the The CreateHandler is similar
Hydrator which may or may not return a created resource. If public function handle(
a resource is returned, then that’s passed to the Transformer. ServerRequestInterface $request
We made a few concrete RequestHandler implementations ): ResponseInterface {
this pattern remained consistent, and we realized that what return $this->transformer->transform(
$this->hydrator->create($request),
we needed next was a generalized RequestHandler with a $request,
related factory that could instantiate it with the specific );
Resolver, Transformer, and Hydrator an API required. }

And the most complex of the set (which really isn’t all that
Compose Yourself complex), the DeleteHandler.
But before we could do that there was reason to change the
behavior of one aspect of an existing API, and a developer public function handle(
ServerRequestInterface $request
on the team implemented that change by creating a simple ): ResponseInterface {
RequestHandler that only handled that single operation. $resource = $this->hydrator->delete(
Until that point we’d been thinking of a future generalized $request,
RequestHandler for APIs that would orchestrate the behavior $this->resolver->resolve($request),
between the Resolver Transformer and Hydrator based on );
the HTTP methods used. But now that seemed a little too if ($resource === null) {
opinionated. If instead we created a set of operation specific return $this->getResponseFactory()
handlers, we’d still need a similar factory to create the handler, ->make(null, 204);
but we’d allow much greater flexibility in how the operation }
is selected.
return $this->transformer->transform(
So we could create a simple ReadHandler that orchestrated $resource,
the interaction between the Resolver and the Transformer. It $request
could look like this: );
}

4 \ April 2024 \ www.phparch.com


Feature: API Handler

Now you may be ready to point out some obvious hand- That decouples things and provides a very simple refactor
waving in these examples. These aren’t real handlers, as the path for our existing code. We can implement a Response-
transformer doesn’t return a ResponseInterface, it returns Factory that expects our HalResource and creates the same
pretty much anything. And we have a set of handlers, the concrete Response we’ve been using. Anyone else can create a
interfaces they require, but no factory to instantiate them. ResponseFactory that fits their own needs.
Good call on that, let’s talk about why. To make things easy to start with, we can implement a
SimpleResponseFactory that wrangles the wide open return of
Open Sourcing a Transformer into a reasonable Response. And we don’t have
to require any specific PSR-7 implementation thanks to the
Those examples are very similar but not exactly what we PSR-17 ResponseFactoryInterface.
created. Our Transformer interface has a very concrete return
type that works for us. Its a HalResource, which is a Json- DefaultFactory::setFactories(
Seralizable object. Because of that our handlers take the new JsonResponseFactory(new ResponseFactory()),
new StreamFactory(),
HalResource and use it to create a concrete Response. );
But that doesn’t work for an open source library. Our
goal is to open source something that is flexible enough to To leverage this factory, our handlers are just a little
be compatible with not just existing code but also a variety more than shown previously. For example, here’s ReadHan-
of requirements. Not everyone wants HAL responses. Not dler::handle():
everyone wants to use a Diactoros response.
In fact, we really don’t want to be tied to either ourselves. public function handle(
ServerRequestInterface $request
As part of the open source process we needed to remove that ): ResponseInterface {
tight coupling. We considered having the Transformer return return $this->getResponseFactory()->make(
a response, but that wasn’t true to the pattern we’d found so new TransformableResource(
successful. Our transformers aren’t aware of the response, our $this->resolver->resolve($request),
handlers are. Instead we can use a factory to create a Response $request,
$this->transformer,
from the return of the Transformer. ),
Not to be confused with the PSR ResponseFactory our focus 200,
is this very specific decoupling: );
}
interface ResponseFactory
{

Code Over Configuration


public function make(
?TransformableResource $resource = null,
int $code = 200
): ResponseInterface;
How does an inbound request get routed to a handler? I
} can tell you how we do it. We have middleware that takes a
request and finds a matching route. We use fast route, but
We’re also introducing a TransformableResource, which is have a route definition layer that enables us to add additional
just a value object that has the resource object, the Server- attributes to the matched route. That route is then attached to
RequestInterface and the Transformer. Everything needed to the server request as an attribute.
make the response, just not made yet. An API dispatch middleware (down the chain a bit) then
checks the request for a matching route, and if one is found
readonly class TransformableResource
{
checks if the route has an associated Resolver Transformer and
public function __construct( Hydrator. If it does, the middleware passes all that to a handler
public object $resource, factory. The factory checks the HTTP method constructs the
public ServerRequestInterface $request, matching handler.
public Transformer $transformer, All that is way very tightly coupled to our middleware imple-
) {
}
mentation, our routing implementation, and our perspective
on how APIs work. But at the core our API dispatch middle-
public function getContent(): mixed ware is asking doing two things: asking if a handler can be
{ created for a specific request, and when the answer is yes,
return $this->transformer->transform( creating that handler and passing the request to it.
$this->resource,
$this->request
To decouple that behavior for the library, a very similar
); middleware could rely on a HandlerFactory to do the same
} two things
}

www.phparch.com \ April 2024 \ 5


Feature: API Handler

class DispatchMiddleware implements MiddlewareInterface Your hydrator is going to accept whatever your resolver
{ returns and know how to update it.
public function __construct(
public readonly HandlerFactory $factory Implement the Handler Factory
) { }
The HandlerFactory does two things. Given a ServerRe-
public function process( questInterface identify if it should be handled. Implementing
ServerRequestInterface $request, this method likely inspects the request and match on path or
RequestHandlerInterface $handler some attributes that have been added to the request instance.
): ResponseInterface { Then given a ServerRequestInterface the HandlerFactory
if ($this->factory->canHandle($request)) {
return $this->factory can return the concrete operation handler that should handle
->makeForRequest($request) the request. Like canHandle the implementation of makeFor-
->handle($request); Request will likely also inspect the request and based on the
} path or some attributes identify the operation handler, and
construct it with the expected Resolver, Transformer, and
return $handler->handle($request);
} Hydrator. That means whatever your application uses for
} dependency injection will probably be injected into your
HandlerFactory as well.
This allows us to avoid complex configuration around asso-
ciating request paths with the matching Resolver Transformer Add Dispatch Middleware
and Hydrator, and instead replacing that configuration with Finally, construct DispatchMiddleware with your Handler-
code, an implementation of HandlerFactory: Factory implementation and add that to your middleware
stack.
interface HandlerFactory If you’re using the SimpleRequestFactory, you’ll also need
{
public function makeForRequest( to call DefaultFactory::setFactories() and set the PSR-17
ServerRequestInterface $request response and stream factories you want used to create the
): RequestHandlerInterface; response.
If you want your API responses to have specific headers, for
public function canHandle( example a Content-Type, creating a simple ResponseFactoryIn-
ServerRequestInterface $request
): bool; terface wrapper around your preferred PSR-7 library makes
} that possible.
class JsonResponseFactory
Perhaps you want a simple map driven association between implements ResponseFactoryInterface
{
an API path and a resource, and use attributes to map a public function __construct(
resource to a set of Resolver Transformer and Hydrator. Or private ResponseFactoryInterface $response_factory
perhaps like us you want the flexibility of uniquely composing ){}
Resolver Transformer and Hydrator for any request. With what
we have here, you can do whatever works for you. public function createResponse(
int $code = 200,
string $reasonPhrase = ''
Using API Handler ): ResponseInterface {
$response = $this->response_factory
Right now API Handler unsurprisingly plays best with an ->createResponse($code, $reasonPhrase);
existing PSR-15 middleware stack. If you already have that return $response
setup, here’s how you use API Handler. ->withHeader('Content-Type', 'application/json');
}
Implement Resolver / Transformer / Hydrator }
For each of your APIs you’ll need an implementation for
each of these. Most of the time you’ll have one of these for
each API. These are also (loosely) coupled to your domain as Lazy Responses
well, you’ll end up injecting dependencies from your applica- If you use SimpleRequestFactory you’ll notice that the
tion into those three implementations. returned response object is not the same type as what the
Your resolver is likely resolving the model / entity / whatever PSR-17 factory creates. In fact it’s a TransformableResponse
you call the thing that represents the data in your application. that wraps the response the PSR-17 factory will eventually
Your transformer is going to take that object and return the make.
shape of your API response. If you use the default SimpleRe- TransformableResponse contains a TransformableRe-
sponseFactory you can pass it almost anything and it’ll try to source. That’s the value object containing the resource,
wrangle it into a response.

6 \ April 2024 \ www.phparch.com


Feature: API Handler

ServerRequestInterface and Transformer all needed to make public function resolve(


a response. http-tortillia (one of our other open source ServerRequestInterface $request
): object {
libraries) is used to implement ResponseInterface in a way $resource = $this->resolver->resolve($request);
that waits until one of the PSR-7 methods is called to trans-
form the resource into a response. if(! $this->acl->isAllowed(
This allows other middleware to inspect the response, and $request->getAttribute('authed_user'),
potentially mutate the resource or the transformer before the $resource,
'read')
response is realized. That can be done by calling withTrans- )) {
formableResource() and passing a TransformableResource with throw new NotFound();
a different resource or transformer. }
This can be used to defer paging to common middleware
and not having to handle paging in every resolver. As long as return $resource;
}
the resolver returns a collection that is not yet realized and
can be paged a generic paging middleware can inspect the The Hydrator does essentially the same thing, asking the
request for paging parameters and set the matching paging ACL if the authenticated user is authorized to write to the
parameters on the collection. resource in question before calling the wrapped Hydrator.
This can also be used dynamically embed related resources. With these two wrapping implementations, we just
Instead of having individual transformers know what related needed to update our HandlerFactory to wrap the Resolver
resources should be embedded a embedded middleware can and Hydrator after creation, and then create the operation
inspect the response and compose a more complex trans- handlers with the wrapped versions.
former. This allows API paths to simply define the primary
transformer for the resource and leave complex transforma-
tion to a more general middleware. RC-1
API Handler is currently at release candidate 1, and while
Extending the documentation is a little light (you just read it), we’d love
some feedback on both what you think about the concept,
API Handler is intentionally simple. What we’ve found is and how easy it is to use if you feel like taking it for a spin.
from this simple base some very powerful things are possible. composer require phoneburner/api-handler
Not because of what the library does, but what it doesn’t
do. Extending happens in your own middleware, or in the Tim likes making things, is a lover of well
HandlerFactory you create, or in the concrete Resolver, Trans- built APIs, and hates the top reply. A long
former, and Hydrator you implement. time mercenary PHP developer, he’s a fan
For example we needed to make sure users couldn’t just of Laminas (you know, ZendFramework),
change the ID on a request and see another user’s data. That’s Doctrine (2 of course), TDD, and practical
not a surprise to anyone, as it’s a standard authorization design patterns. He’s now working on things
problem. But one that API handler doesn’t try to solve inten- at PhoneBurner, tweeting sporadically from@
tjlytle, and hanging out with his wife and
tionally.
kids. @tjlytle
In our case, we already use an ACL package where we can
pass a user and the resource, and ask if they can read or write
that resource. Given that context we created an ACL imple-
mentation of Resolver and Hydrator. Both expect to be passed
both the ACL and a specific Resolver or Hydrator to wrap.
Here’s a version that shows the basic behavior for the
Resolver:

www.phparch.com \ April 2024 \ 7


The essential tool for working
with dependencies in PHP
Private Packagist gives you fast, reliable, and secure access to
your internal and mirrored copies of third-party packages.

Reliable access Security monitoring

Copies of the open-source and other Receive alerts or weekly summaries when
third-party packages you use are stored, security vulnerabilities are reported for
so no need to worry about availability. Composer dependencies in your projects.

Review dependencies Quick setup

Automated comments on your pull Private Packagist integrates with GitHub,


requests allow you to quickly review any Bitbucket and GitLab and supports any
dependency changes. code hosting platform using git, svn or hg.

Use code ARCHITECT24


packagist.com for 10% off your first year.

Hundreds of companies trust Private Packagist.


FEATURE

My First php[tek]
Drew Halverston
This year was my first time attending a php[tek] conference. I am always appreciative of how friendly the PHP community is
and php[tek] was no exception. I met many new people and got to catch up with other folks that I hadn’t seen since before the
pandemic. Chatting with the sponsors was also a great experience.
The wide variety of topics being talked about was impressive. There were plenty of talks that appealed to me as a junior devel-
oper and many that allowed me to stretch out past my comfort level and learn new things. Make styling a breeze with Tailwind
CSS has me wanting to use Tailwind more in future projects and Consulting: Coding is Only Half the Work gave me a lot of
insight into how I could improve my consulting business. The daily keynote talks were great. I took a lot away from Finding
Your Perfect Place in Tech as I am relatively new to PHP development.
The venue was awesome. There was plenty of space to engage with other attendees without feeling crowded and the meals,
beverages and snacks were all delicious. I really appreciated the OSMI quiet room. It gave me time to soak in what I was expe-
riencing at the conference in a calm space.
Attending the conference was a decision that I am glad I made. Everything was phenomenal and I am looking forward to
attending again next year!

Drew Halverson recently graduated from the Front-End Web Developer program at Madison Area Technical College.
He is the owner and developer at Navlys Design and primarily works with PHP, WordPress, and Laravel. When he isn’t
working, he enjoys playing guitar, riding his motorcycle, and playing with his two Great Pyrenees.

www.phparch.com \ April 2024 \ 9


Thanks
FEATURE

Definitely Not My First php[tek]


Beth Tucker Long
I have been attending php[tek] for almost two decades. During that time, a lot has changed in the PHP community as well
as the tech industry at large. The one thing that has never changed is how much I get out of php[tek], and this year was no
exception.
Between learning more about super helpful, but less known features of the Standard PHP Library (SPL) and how to use
PHPStan to help update legacy code, I have so much I want to start implementing in my day-to-day. Diving into “PHP Applica-
tions at Scale” has my brain spinning with new ideas on ways to keep applications flexible and working during bursts of activity,
which is always helpful. “Composer Guide to Supply Chain Security” reminded me that there are many attack vectors which
are generally glossed over when dealing with outside packages and libraries. I can never learn enough about security.
Another wonderful part of this year’s php[tek] for me was that the talks were recorded. That way, I did not have to choose
between giving my own talk and attending “Who Goes There? Actively Detecting Intruders With Cyber Deception Tools” (just
kidding, mostly). This also allowed me to finish up intriguing conversations and debates, like an in-depth discussion about
whether or not unitialized array keys and undefined variables should really be forbidden or are just “not best practice”. I can’t
tell you how much I love having these types of conversations and hearing points of view from developers in different industries
and working at different levels of security and scale. We have such a diverse and amazing community!
All in all, I came home with a lot of new knowledge and a mind brimming with new ideas and inspiration. Thank you to
everyone I met and interacted with. You all are wonderful, and I can’t wait to meet up with you again next year!

Beth Tucker Long is a developer and owner at Treeline Design, a web development company, and runs Exploricon, a
gaming convention, along with her husband, Chris. She leads the Madison Web Design & Development and Full Stack
Madison user groups. You can find her on her blog (http://www.alittleofboth.com) or on Twitter @e3BethT

www.phparch.com \ April 2024 \ 11


Security Corner

Security and Side Channels


Eric Mann
Any modern film involving hacking will usually feature a scene of attackers breaking into a
machine, system, or service in some glorious fashion. They race against a clock featuring
animated characters to crack a password. CGI electrons race down a wire to illustrate malware
compromising a system. An operative dives out of an overhead vent to insert a device into the
mainframe.

In the real world, this kind of attack is quite rare. A spec-


Listing 1.
tacle worthy of media coverage, perhaps. A real and prescient
threat against which you should spend every waking hour 1. $query = 'SELECT * FROM users WHERE email = ?';
defending is unlikely. 2. $statement = $db->prepare($query);
Most attacks are instead started through some sort of earlier 3. $statement->execute([$_POST['email']);
reconnaissance. Rather than actively hacking the system, the 4.
attackers take time to study its behavior to find out as much 5. $user = statement->fetchObject();
6.
information about the target as possible. This information 7. if ($user) {
helps target attacks to avoid detection by operators, and 8. return password_verify(
it might also allow them to extract sensitive data from the 9. $_POST['password'],
system without directly compromising it in the first place! 10. $user->password_hash
Attacks that leverage indirect means of compromising a 11. );
system are called side-channel attacks. These are situations 12. }
where someone leverages ancillary information about an API 13.
14. return false;
or application operation to gain further knowledge about the
system or to attack/compromise it directly. One of the easiest
illustrations of a side-channel attack is one using timing In other words, an attacker can time requests to identify
information. whether or not a specific user (based on their email address)
has registered with your system.
Timing Attacks On my own machine, it takes an average of 0.05 micro-
seconds to verify a password hashed with default (explicitly
Assume that for all of this explanation, an attacker is able PASSWORD_DEFAULT) parameters. Your production machines
to successfully submit whatever data they want to your API might differ based on configuration, but assume for a moment
as many times as they choose. In that world, they can collect they’re similar. In that world, a request for a valid username
information about the amount of time it takes your server or email will always take 0.05 microseconds longer to return
to respond to a request. Depending on how the application an authentication error than a request for an invalid one.
handles the data coming in, the ability of an attacker to time If the attackers are trying to enumerate user accounts or
multiple requests will give them information about what’s even just validate whether or not Joe or Jill Victim use your
happening behind the scenes. service, they can spam your authentication endpoint and
If your application authenticates users, you likely have produce a list of valid user emails quite easily. Based on
a controller somewhere implementing logic similar to the timing the response time of your server alone!
following: (See Listing 1)
The logic is simple, and the password verification it contains
is sound. No passwords are stored in plaintext; you’re using Information Leakage
both strong hashing and a secure hash verification method. Any time your application gives information over to a
The problem is that you’re leaking information to the attacker! would-be attacker in an unauthenticated context, you’re
If you run this logic once, it’s pretty hard to see why there’s leaking that data. Depending on the nature of the informa-
a problem. A user with valid credentials is logged in; an tion, this leakage will be more or less critical. That said, it’s
authentication attempt with invalid credentials is rejected. a common weakness among even the most successful and
The problem, though, is that repeated requests will reveal a widely used systems today.
slight difference in how long it takes to return a result based WordPress, which powers over 40% of all websites1,
on whether a user exists in the system. suffers from both the above illustrated timing attack as well
1 https://phpa.me/wp-market

12 \ April 2024 \ www.phparch.com


Security Corner
Security and Side Channels

as an even more critical information leakage in its authen- avoid timing attacks entirely by making both successful and
tication system. An abbreviated form of the username/ unsuccessful requests do the same processing as follows:
password authentication function2 looks like the following:
(See Listing 2)
$query = 'SELECT * FROM users WHERE email = ?';
$statement = $db->prepare($query);
Not only can attackers use timing information to identify $statement->execute([$_POST['email']);
registered usernames (and email addresses thanks to a similar
wp_authenticate_email_password()), but WordPress itself will $fixed = password_hash('something random');
also return an entirely different error based on whether or not
the username exists! $user = statement->fetchObject();

if ($user) {
Defense-in-depth return password_verify(
$_POST['password'], $user->password_hash
When faced with these kinds of weaknesses in your );
application, the correct response is to fix them. Ensure that } else {
anonymous users cannot gain information about the oper- return password_verify($_POST['password'], $fixed)
&& false;
ation of your system through remote reconnaissance. For
}
example, the first illustration of user authentication can

Listing 2. Listing 2. (cont.)


1. function wp_authenticate_username_password( 38. 'invalid_username',
2. $user, 39. sprintf(
3. $username, 40. 'Error: The username %s not registered.',
4. $password 41. $username
5. ) { 42. )
6. if ( $user instanceof WP_User ) { 43. );
7. return $user; 44. }
8. } 45.
9. 46. $user = apply_filters(
10. if ( empty( $username ) || empty( $password ) ) { 47. 'wp_authenticate_user',
11. if ( is_wp_error( $user ) ) { 48. $user,
12. return $user; 49. $password
13. } 50. );
14. 51. if ( is_wp_error( $user ) ) {
15. $error = new WP_Error(); 52. return $user;
16. 53. }
17. if ( empty( $username ) ) { 54.
18. $error->add( 55. if ( ! wp_check_password(
19. 'empty_username', 56. $password, $user->user_pass, $user->ID
20. 'Error: The username field is empty.' 57. )) {
21. ); 58. return new WP_Error(
22. } 59. 'incorrect_password',
23. 60. sprintf(
24. if ( empty( $password ) ) { 61. /* translators: %s: User name. */
25. $error->add( 62. 'Error: The password you entered for'.
26. 'empty_password', 63. ' the username %s is incorrect.',
27. 'Error: The password field is empty.' 64. $username
28. ); 65. ).
29. } 66. '<a href="'.wp_lostpassword_url().'">' .
30. 67. 'Lost your password?' .
31. return $error; 68. '</a>'
32. } 69. );
33.
70. }
34. $user = get_user_by( 'login', $username ); 71.
35.
72. return $user;
36. if ( ! $user ) {
73. }
37. return new WP_Error(

2 https://phpa.me/wp-user-contents

www.phparch.com \ April 2024 \ 13


Security Corner
Security and Side Channels

The unsuccessful code path doesn’t actually have to validate Once logging is implemented, you can also begin to throttle
any real values and, thanks to the && false trailer, will always requests from remote clients. If a client at a specific IP address
return false anyway. The behavior of this function will be is submitting data to a login page more than once every now
identical to the earlier iteration except without the different and again, they’re likely abusive and should be limited in their
timing between valid credentials and invalid ones. ability to send further requests.
Similarly, projects like WordPress should take care not to Building secure code is always your first line of defense.
explicitly confirm the presence of usernames or emails during But there will come a time that a previously unrecognized
login flows—this was an intentional choice of the WordPress edge case could leak information through some unforeseen
team for the overall usability of the product but still one that side channel. To protect against that eventuality, log what
warrants revisiting. Whether a user exists in the system or not you can, check your logs regularly to identify patterns, and
should never be visible to an unauthenticated third party lest limit ingress into your system by clients exhibiting poor or
it make that user the target of a future attack. potentially abusive behavior. There is no one single bullet for
To truly defend your application, though, you should look internet security, but these approaches and awareness of the
into leveraging two complimentary @strategies: logging methods against which they defend will definitely help!
and throttling.
Every request should be logged in some way. This provides Eric is a seasoned web developer experi-
you insight into request patterns and the types of data being enced with multiple languages and platforms.
provided. But it also surfaces information about who is He’s been working with PHP for more than
making the requests (i.e. IP addresses, geolocation of clients, a decade and focuses his time on helping
user agent strings). You can leverage this information to developers get started and learn new skills
with their tech of choice. You can reach out
proactively block requests from bad actors and prevent
to him directly via Twitter: @EricMann
potential abuse of your system.

Secure your applications against


vulnerabilities exploited by attackers.
Security is an ongoing process not something to add right
before your app launches. In this book, you’ll learn how
to write secure PHP applications from first principles.
Why wait until your site is attacked or your data is
breached? Prevent your exposure by being aware of the
ways a malicious user might hijack your web site or API.

Order Your Copy


https://phpa.me/security-principles

14 \ April 2024 \ www.phparch.com


Connect. Build. Engage. Explore.

Get started at
developer.vonage.com
|
Barrier-Free Bytes

Getting GAAD Ready


Maxwell Ivey
This month, I want to start by thanking PHP Architect for their investment in accessibility and
inclusion. I also commend the team for adding me as a columnist.

Next, I want to thank Beth Tucker Long, a fellow colum- So, I have done what I can to explain the why. Now, let’s
nist, for being so personally invested in accessibility and discuss the how. Beth gave you some great ideas in her article,
her March 2024 finally{}1 article about Global Accessibility which I referenced previously. I haven’t used Waves, but I hear
Awareness Day (GAAD). This is not just because I am blind; good things about it. As Beth mentioned, Wave is a tool that
accessibility affects my life directly, along with the lives of will review your website for accessibility and provide a report
millions of people like me every day. These folks get it; they pointing you to areas that must be addressed to improve your
understand that failing to include accessibility is inefficient. accessibility. And another perk for someone who lives on a
I have been preaching this message for years: Accessibility budget, it is a free service. Personally, I also write posts for a
equals Efficiency. I call it the Accessibility Advantage. I truly company called Audio Eye. They offer a free website checker,
want business owners, content creators, site developers, etc., too. They will use it as a point of contact to try to sell you their
to understand that accessibility is in their best interest. They product, but I think most of us expect that these days.
should even see it as a competitive advantage as long as so I do want to give you some things to think about if you
little of the net is accessible. are going to use an automated checker or a site automation
However, it goes even further than Beth mentioned in her option. First, don’t trust any of them that promise 100%
previous article regarding GAAD. You see, it’s not just the one accessibility and remediation. Because even with generative
billion people with a disability, which is a market no one can AI, that promise just isn’t realistic. Second, you want to use
afford to ignore. But that doesn’t take into account the total an option that has been created with input from people repre-
reach of the disability community. Because we are a highly senting a wide variety of disabilities. And you want to see that
loyal consumer base, we will advocate for businesses and they are continuing to draw on experience from an actual live
brands that make an effort to make us feel welcome. I like testing group.
to say that designing to include disabled people is like hiring I love Beth’s suggestion of adding people with disabilities
influencers you won’t have to pay. When we find a company to your testing system. I would also encourage you to add
that provides a quality product or service in an accessible them to your design and customer service teams if you don’t
manner, we want everyone to know. We will talk to our already have them. This is because I have heard from coders
friends, family, coworkers, and social networks. Heck, we will about how challenging it is to turn out an accessible site when
even tell people we don’t like. the designers of the content aren’t familiar enough with the
But even that doesn’t completely cover the advantages of challenges of accessibility when creating images, audio, videos,
building in accessibility. Many of the problems that frustrate text documents, etc. Additionally, quite often, the first people
a user who is blind, hearing impaired, or physically disabled to know about an accessibility issue will be your customer
also cause problems for an able-bodied user. Many things support team. If they have at least a basic understanding of
that frustrate someone with a neurological or developmental accessibility, then they would know when and how to forward
disability make things harder for someone with no mental or these concerns so they get addressed.
emotional issues. In short, when you design inclusive prod- If your company has an internal learning and education
ucts, services, and content, you make your offerings better for program, consider inviting people with disabilities to become
everyone. part of your speaker team. Next, Beth mentioned sharing
These more enjoyable experiences will lead to loyalty about the accessibility improvements that you have already
whether the visitor is disabled or not. And this applies to made instead of talking about what you plan to do. I couldn’t
content as well. One way to grow the audience of a YouTube agree with this more. In my experience, companies are reluc-
channel, podcast, or live-streaming event is to make it inclusive. tant to talk about the work they have done and are doing. This
This would include offering things like alt text descriptions, is important because accessibility is one of those things you
closed captioning, text transcripts, and audio descriptions for have to continue to work at. I want to encourage you to write
both video and audio presentations. Heck, this inclusion can a blog post or record some other type of content chronicling
even lead to building stronger, more creative, resilient, and your investment in accessibility. You may even want to have
positive teams in the first place. someone from your team start appearing on podcasts that
reach the disability community to promote your efforts along
with your business and to solicit even more suggestions on
1 https://phpa.me/finally-03-2024

16 \ April 2024 \ www.phparch.com


Barrier-Free Bytes
Getting GAAD Ready

how to make your products, services, and content even more Once you have addressed a couple of items on the site eval-
inclusive. uation report, run it again. You will see how well your efforts
I’m currently looking for business leaders who have are working, and you can select a couple more items to work
embraced accessibility to come on my new podcast called The on. Many people like me understand just how challenging
Accessibility Advantage. And I look forward to discussing the accessibility is for a site owner or developer. As someone who
challenges they had to overcome internally and externally had to hand-code his site for several years before switching to
and how they managed to become more inclusive. I also look WordPress, I understand how hard this can be. And my sites
forward to hearing about how making the effort affected their were never accessible.
teams. For example, I have already heard from the owner of There are so many disabilities, and they use a wide variety
a small company who said that investing in accessibility and of adaptive technology. You also have to design for multiple
inclusion gave them a hiring advantage when competing for operating systems, platforms, and browsers. This is actually
younger, more socially motivated workers. part of why we give so much love to companies willing to
Wrapping up, I want to agree with Beth about taking make the effort. If there is some way I can help you get started,
action. She asked the community to do a couple of small, please reach out to me through the comments page or my
simple things. This is important because many people get website in my bio. Whether you need technical advice or
overwhelmed by what it takes to make their websites, apps, simply want some emotional reassurance, I want to be there
and content accessible. So, I want you to know that this will for you.
be a process. You won’t be able to implement everything at Because you see, The PHP Architect team does get it. They
once. And you shouldn’t. Most people, like me, appreciate brought me on to the team; I have a responsibility to do what
sincere, consistent effort over immediate compliance with I can to help each and every one of you implement accessi-
the minimum of the available arbitrary rule. Start by doing bility in whatever way and at whatever pace you are able to.
the hard things first. I like to say that it is easier for me to answer an awkward
I hope I have helped you realize the value of being inclusive. question than it is to make people guess. So, if you have a
So, step one for me is to accept that accessibility is a necessity, question about anything, please just ask. I want to see GAAD,
not a luxury. Then focus on the most important thing for your Global Accessibility Awareness Day, become a day of celebra-
business. If making a sale is the goal, then make it easy for us tion. Like Beth said, let’s make it a day where people share
to locate the relevant information and make a quick, smooth about the improvements they have made.
purchase with minimum distraction that gives us maximum
confidence in our buying decisions. Then, run a site review Maxwell Ivey is known around the world as
on Waves or a similar site. The Blind Blogger. He has gone from failed
Take a snapshot or screen share of where you stand right carnival owner to respected amusement
now. Then, select one or two things to start working on now. equipment broker to award-winning author,
motivational speaker, online media publicist,
A simple one to start with would be making sure that all
and host of the What’s Your Excuse podcast.
buttons and links are clearly labeled with text characters for
@maxwellivey
easy navigation by screen readers. Another might be adding
compelling, informative alt text to your images. Next, you
might modify the headings on your site so they can be easily
navigated. I will write more on that in a future article.

www.phparch.com \ April 2024 \ 17


2500 Feet

Making the Difficult Choice


Edward Barnard
“How we got here” can be an interesting and difficult question. Once again, I’m using Minnesota’s
“Miss Mitchell” as an example. We’ll trace the link from aviation pioneer Glenn L. Martin to North
American Aviation’s B-25 Mitchell aircraft.

We’ll be observing human nature by computers, but not so good when it


focusing on a specific person, Glenn comes to working with people–and The Barnstormer
Martin. We’ll see difficult choices software development is a “people” Wikipedia tells us that barnstormers2
made, for better or for worse. sport. Tracing “how we got here” back were pilots who flew throughout the
to Glenn Martin takes us through country to sell airplane rides and
several people, their choices, and their perform stunts. Minnesota’s Charles
I have discovered that people contra-
actions. Lindbergh began flying as a barn-
dict each other. I have also found that
This is another case of tracing “how we stormer, but Glenn Martin did not.
“how we got here” has a lot to do with
got here” and its outcome. Minnesota’s In the late summer of 1907, less than
whatever weirdness you’re trying to
“Miss Mitchell” was designed by people. four years after the Wright Brothers
turn into software at the moment. This
This process happened a century ago flew at Kitty Hawk, Glenn Martin
month, we’re following a chain of events
(plus or minus two decades), and thus, built an airplane because he had seen
back in time to a gentleman named
I wasn’t there to observe for myself. I one. He drew out some plans based on
Glenn Martin. He’s the “Martin” part
need to rely on writers–who are people. what he had seen. Roy Beall, one of the
of Lockheed Martin1, the well-known
What I found is this: people contradict mechanics at Martin’s garage, helped.
aerospace company.
each other. People have agendas. People A third person solved a diffi-
Our endpoint, once again, is Minne-
had limited access to information cult problem. Charles Day3 was an
sota’s “Miss Mitchell”, a B-25J Mitchell
because military aircraft were secret at instructor at the YMCA Auto School in
medium bomber manufactured in late
the time. Thus, contemporary accounts Los Angeles, California. Day carved an
1944 by North American Aviation. Our
and design descriptions will have gaps airplane propeller for Martin as a favor.
subject-matter expert (me) explains
or make wildly incorrect assumptions. Day created the idea of using laminated
that “how we got here” has a lot to do
Modern accounts can be shaded with wood, making the propeller stronger
with Glenn Martin. Martin, the person,
modern biases and revisionist history. and lighter. Day remains credited as the
is therefore our topic this month.
Thus, the situation, for me, is twice inventor of laminated wooden propel-
Tracing the significant events
removed. I’m learning about the people lers that are still used today.
explaining “how we got here” is a
who designed the airplane, but I also Charles Day later became Chief Engi-
crucial skill in bug-fixing (see “Related
need to learn about the peopletelling neer of the Glenn L. Martin company.
Reading”). In Glenn Martin’s case, I’ll
meabout the people who designed the In 1907, however, Martin’s home-built
introduce several other people who
airplane. aeroplane was in the cow pasture, ready
helped create this chain of events. I
Something odd happened. Month to fly. They got the engine started,
think it’s quite possible Glenn Martin
after month, book after book, one inter- and Martin taxied it around carefully.
was an incubator. That is, he provided
library loan after another, I learned Martin had never flown before. Nobody
the starting point for several people’s
things. Quite by accident, I began had taught him to fly an airplane nor
spectacular careers. We’ll see some
learning about people! By focusing on how to build one.
interesting observations and a difficult
something that is most definitely not The engine quit. This was 1907; it
choice. Martin was famous for two
about software, I focused on people, happens. But Martin was at the far end
separate reasons: two separate skill sets.
which brings me insight into software of the pasture and didn’t want to wait
He loved both. The time came when he
development because software develop- for Day and Beall to help get it running
had to choose between the two.
mentisa people sport. again. That’s when Martin learned that
That’s why I’m telling you about an when you’re in front of the aeroplane
People Sport old-time barnstormer. He had to make
I’m not a “people” person. I’m a choice. Perhaps you and I can relate. 2
pretty good at communicating with https://en.wikipedia.org/wiki/Barnstorming
3
1 https://phpa.me/lockheed https://www.earlyaviators.com/edaychar.htm

www.phparch.com \ April 2024 \ 19


2500 Feet
Making the Difficult Choice

spinning the propeller to start the county fairs. Would they like a barn- Mom (Minta). Not once does the book
engine, there won’t be enough time to stormer to perform? mention that Glenn had a sister, Della.
scramble back onto the plane and take This was 1910. Everybody wanted to That seems odd. Was the author so
control. see a flyer perform. Minta’s idea was focused on Glenn that he simply acted
The plane nosed over, ending Martin’s brilliant. as if the sister did not exist?
first experience with building an aero- As part of his barnstorming act, This question, in my view, is another
plane. Martin would stake a handkerchief key to debugging software. If there’s a
Roy, Charlie, and Glenn sat down in full view of everyone in front of symptom, event, or circumstance that
at the kitchen table to design another the grandstand. Martin then took off, doesn’t fit the solution or seems out of
aeroplane. Martin wrote to Wilbur flying the biplane he had built in his place, this might mean you’ve not yet
and Orville Wright, sending drawings trademark black flying suit and leather found the solution.
of what they wanted to build (an aero- helmet. The answer is sad. Caltech explains in
plane). He asked the Wrights if they had At the end of each performance, “Accelerating Mental Health Research”6:
any objection to building that aeroplane. he switched off his airplane engine,
The Wrights received many such indicating trouble. This was after any Della Martin was 28 years old
letters during that time period. They number of dives and swoops and when her family had her commit-
were embroiled in patent fights with various acrobatic performances. With ted to the California Department
Glenn Curtiss. But Orville wrote back, the engine off, he dead-sticked in for of State Hospitals for hearing the
saying they had no objection to Glenn his “emergency” landing. He carefully, voice of God… she was 73 when
Martin building an aeroplane similar delicately landed, safe on the ground– her brother, aviation pioneer
to the drawings. Martin was duly with one wheel resting squarely on the Glenn Martin, died and she was
impressed to hear back. handkerchief. It was all part of the act! freed from institutionalization.
In Los Angeles, circa 1909-1910, That’s how the Glenn L. Martin She received an inheritance from
they built themselves an aeroplane. company came to exist, run by one of Glenn’s estate and, in her last 17
Beall built an engine for it with a light- the original barnstormers. His aircraft years of life, lived with a compan-
er-weight crankcase. The only space plant manager was the original barn- ion in San Marino and traveled
they could find that was large enough stormer (and likely taught Martin that the world. Before passing away, she
was a church building–so they built the bit with the handkerchief). dedicated her wealth to the forma-
aeroplane inside the church. They had Glenn Martin taught lumber magnate tion of a foundation to support
to remove (and replace) the entire front William Boeing to fly and sold him a mental health research.
of the church to get the aeroplane out! Martin airplane to take back home to
That aeroplane flew. By this point, Seattle. Boeing eventually crashed that
Martin’s mother, Minta, was a key plane in Lake Union. Boeing decided
partner in the success. he’d rather build his own plane than The Boy Engineer
Martin was a brilliant self-taught repair Martin’s. One of the Boeing I’ve mentioned Charles Day, who
pilot. He earned Expert Aviator Certif- company histories has a picture of the invented the laminated-wood propeller,
icate number two. (Glenn Curtiss had crashed plane that led to the founding and William Boeing, who built
earned number one.) But he wanted to of Boeing Aircraft, but it does not airplanes. I’m about to mention several
build airplanes. (In U.S. English, “aero- mention Glenn Martin, who built that other luminaries connected with Glenn
plane” became “airplane” circa 19164.) plane. Martin. They help answer our investiga-
To build airplanes, Martin needed a That minor omission turned out to be tion of “how we got here”.
factory. He needed funding. He went a key revelation for me. Two different Silent-screen actress Mary Pickford’s
barnstorming. It was a way to pick up narratives about the same business foundation explains Martin’s adven-
a few dollars here and there. He deliv- process might not look the same at all. ture in the 1915 Pickford film A Girl of
ered newspapers (in a bundle) by air, Or one might omit a key piece of infor- Yesterday7:
strapped to the wing of his biplane. mation that you’d never know unless
In short, Martin needed startup you went to an extra source. The plot of A Girl of Yesterday
funding and lots of it. As another example, Glenn Martin’s included a scene where Mary is
biography To Ride the Wind describes
5 kidnapped and taken away by
airplane that was to be filmed in
Crowd Funding scenes as a young boy, including
building things in the kitchen with
Minta Martin had an idea. They 6 Advancement and Alumni Relations.
found contacts for many California “Accelerating Mental Health Research,” October
5 Still, Henry. To Ride the Wind: A Biog-
events, up and down the coast, such as raphy of Glenn L. Martin. New York: Julian 10, 2023. https://phpa.me/mental-health-re-
Messner, Inc., 1964. https://phpa.me/oclc-en- search.
4 https://phpa.me/airplane-spelling tity. 7 https://phpa.me/girl-of-yesterday

20 \ April 2024 \ www.phparch.com


2500 Feet
Making the Difficult Choice

Griffith Park… Mary stepped forward to be bound and Martin looked up, saying, “I am Mr. Glenn L. Martin. But I
gagged as the script called for, and she was placed in the haven’t time to talk right now, young fellow. Sorry, I’m waiting
small, four-seater plane… Mary’s pilot was a local aviator, for someone. Come out to my plant later on.”
Glenn Martin… Flying a plane was an everyday occur- I should note that this version of the story comes from
rence to Martin, but he balked at being in the film when the young man’s biography. The Martin biography tells a
he was told his role called for him to kiss a girl because somewhat different story. Both sources are printed books,
“My mother wouldn’t like it.” authoritative, peer-reviewed, and published during or shortly
after those persons’ lifetimes.
I found time and time again that I didn’t dare rely on a single
Figure 1 shows Glenn Martin (center) in front of his Model
source for anything, no matter how authoritative. When I dug
TT biplane, Mary Pickford on the right. The Model TT was
more deeply, I found surprises and contradictions. Always
a military trainer plane with two cockpits and dual controls.
consider the possible agendas, filtering, bias, and misunder-
Martin built three Model T and fourteen Model TT. The
standing. It happens.
earlier Martin T was delivered without engines, so the Army
The young man retreated hastily and took a seat across the
could put in whatever engine they wanted. The T/TT order
lobby. But he watched Martin, who was visibly anxious for his
was a huge order for Martin’s company.
man to arrive. Finally, the young man left his seat to approach
Martin again.
Figure 1.
“I don’t want to disturb you, Mr. Martin, but I think you’re
waiting for me.”
“You?” Martin laughed. “Son, if you must know, I’m
waiting for the Chief Engineer for my airplane factory, not
a high school boy.” Martin grinned. “Now, son, if your name
happened to be Donald Douglas–” Martin got no further as
his sentence was interrupted.
“My name is Donald W. Douglas.”
Martin stared in amazement and said, startled, “You. Why
you’re just a boy. A boy engineer!”
That’s how, according to Douglas company legend, Donald
Douglas became known at Glenn Martin’s aircraft factory as
The Boy Engineer. The famed Martin Bomber, the MB-1, was
actually designed by his Chief Engineer, Donald Douglas.
During World War II, Douglas Aircraft Company set up a
factory near Chicago to build a military transport airplane,
the C-54 Skymaster, at Orchard Place9. An airplane factory
needed a runway for departures, naturally, and the area
became known as Orchard Place / Douglas Field, ORD. After
the war, Chicago bought the property for one dollar and built
the airport known as O’Hare International (ORD).
When Donald Douglas was hired, the plant manager was
By 1915, World War I was raging, but the isolationist
Larry Bell. His Bell Aircraft Company10 later built the Bell X-1,
U.S.A. didn’t care. Glenn Martin’s chief engineer was Charles
the first airplane to break the sound barrier in level flight. You
F. Willard8, himself an aviation pioneer credited with several
might remember the Bell helicopters featured in the TV series
firsts. Willard left the Glenn L. Martin Company to join the
MASH.
U.S. Army Signal Corps. That was the part of the Army that
General Billy Mitchell, in the 1920s, famously sank some of
handled aviation (balloons and observing). This meant Martin
the U.S. Navy’s battleships as a demonstration. (A key feature
needed a new Chief Engineer. Martin considered carefully,
of Mitchell’s demos was that they worked.) General Mitchell
and there was someone interesting on the East Coast.
used the Martin Bomber for sinking naval vessels. The Martin
Martin invited this person (this was before email) to come
MB-2 Bomber was a Donald Douglas design with help from
out to Los Angeles and join Martin’s company. They arranged
someone named Dutch Kindelberger.
to meet in the Biltmore Hotel lobby in August 1915.
Dutch Kindelberger, when he later worked for Donald
As Martin sat in the lobby, a young man came in, approached,
Douglas at Douglas Aircraft, designed the earliest of the
and said, “You’re Mr. Glenn L. Martin?”
Douglas Commercial (“DC”) aircraft (DC-1, DC-2, DC-3,

9 https://www.phpa.me/orchard
8 https://www.earlyaviators.com/ewillard.htm 10 https://en.wikipedia.org/wiki/Bell_Aircraft

www.phparch.com \ April 2024 \ 21


2500 Feet
Making the Difficult Choice

DC-4, …, DC-10). The C-54 Skymaster, noted above with that Martin served as an incubator. That’s a strong influence
Chicago O’Hare, was the military version of the DC-4. to consider.
Dutch Kindelberger left Douglas Aircraft Company to However, there’s one more piece to the puzzle: Martin
become president of North American Aviation. He brought himself. He had a rough Monday morning in 1918.
with him Lee Atwood, later known as the Dean of Aero-
space. Kindelberger and Atwood designed the B-25 bomber
(including Minnesota’s “Miss Mitchell”). Kindelberger named
Glenn Martin’s Monday Morning
their B-25 design the “Mitchell” in honor of the late General Circa 1916, Glenn Martin merged with the Wright Brothers
Billy Mitchell. This was, I believe, the only U.S. aircraft series to form Wright-Martin Aircraft Corporation. That sounded
named after a person. great but was horrible. The company hired a cement maker
Figure 2 shows the North American Aviation11 logo on as plant manager. They didn’t build airplanes, just Wright
my back-seat rudder pedal while flying at 2500 feet. Figure Cyclone airplane engines.
3 looks down at the airport ramp (parking area) as we break Figure 4 (on the next page) shows one of Miss Mitchell’s
hard left to circle and land. At the upper right of the ramp, two Wright Cyclone engines. We had just backed the airplane
the larger silver plane, is our North American B-25 “Miss into the hangar after her first public flight of 2023 (and my
Mitchell”. At lower right, with yellow wings and blue fuselage, first ride in back). Our maintenance crew was awaiting our
is our Vultee Valiant, known as the “Vultee Vibrator” because return and took over immediately.
of what it did to the building windows when it flew by. Both I’m a text person rather than a visual person. However, it
the blue Valiant and the one I’m riding, our yellow North sure makes a difference when I can see and touch the subject
American SNJ-6 Texan, were used for pilot training during at hand. A picture or screenshot can make many things
World War II. clear. Watching a subject-matter expert at work can be most
We’ve now answered, “How we got here”. Much of the story enlightening.
revolves around Glenn Martin. This might mean aviation was Back in 1916, neither Martin nor the Wrights were happy.
a very small world at the time (it was), but it could also mean That company dissolved within ten months. It had been in
New York. Returning to his factory in Los Angeles, Martin
Figure 2. realized he needed to expand. He was disgusted with the year
wasted not building airplanes. (I can relate, but I’m certainly
not at Martin’s level!)
Martin spoke with the owner of the Cleveland Indians
baseball team, Alva Bradley. Bradley and Martin created a
combined interested in building aircraft for war, but there
was a catch. The combine wanted the airplanes to be built in
Cleveland.
This meant building a new factory. Martin saw this as
fantastic. Greenfield development (i.e., building a new
factory) would allow him to implement various manufac-
turing ideas he’d pioneered in Los Angeles.

Figure 3.

11 https://phpa.me/na-aviation

22 \ April 2024 \ www.phparch.com


2500 Feet
Making the Difficult Choice

Martin firmly stated that this conversation did not happen


Figure 4.
until tomorrow at the scheduled time. “Good day, Mr. Hart-
well.”
Glenn Martin informed his secretary that he’d be gone for
the rest of the day. He walked through the aircraft factory to
the runway on the other side. He put on his black flying outfit,
the same style made famous during his barnstorming adven-
tures. He climbed into his private Martin Model TT biplane
and turned it into the wind.
He flew far out over Lake Erie so as to be away from obser-
vant eyes. Much, much later, Martin returned to make a
smooth landing in his personal Model TT and walked away.
Glenn L. Martin never flew again. He had airplanes to build.

Summary
I told part of the Glenn L. Martin story. He influenced, hired,
or taught to fly several aviation pioneers. However, the reason
for the story is that it’s a “people” story. It’s worth studying
the choices made. Learning how people influence and make
choices provides useful insight for software development.
There’s no substitute for seeing, touching, and hearing. It
was enlightening to observe that when a Wright Cyclone
starts up (Figure 5), everyone looks. Seeing people react
provides insight you won’t have otherwise. Next time you do
a demo, watch for the reactions–or non-reactions.

Figure 5.

Donald Douglas, at that time, was working for the U.S.


Army Signal Corps. Martin asked him to rejoin his company
now in Cleveland. Douglas wound down his Washington,
DC, work quickly. The U.S., by this point, had entered World
War I.
One Monday morning in 1918, Martin arrived in his
Cleveland office and checked his calendar for the week. The
next day, Tuesday, held an appointment with a Mr. Hartwell.
Martin’s receptionist soon announced Hartwell was here to
see him a day early. Martin invited Mr. Hartwell in.
Hartwell hesitantly explained that Martin represented “a
valuable property to the investors in the company–$2,000,000,
to be precise.” Martin’s investors felt flying was too risky. The
32-year-old barnstormer’s proper place was in the office, not
the air. If Martin continued to fly, the investors would with-
draw. Ed Barnard had a front-row seat when
Glenn Martin thought about this for several minutes and the Morris Worm took down the Internet,
agreed. Hartwell quickly headed for the door, explaining that November 1988. He was teaching CRAY-1
he was only the messenger. supercomputer operating system internals to
Martin stopped him with a question. “You weren’t supposed analysts as they were being directly hit by the
Worm. It was a busy week! Ed continues to
to come here until tomorrow, is that right, Mr. Hartwell?”
indulge his interests in computer security and
Hartwell agreed, saying he happened to be in the vicinity teaching software concepts to others.
and dropped by on the chance Martin was available. This was
obviously a distasteful meeting that Martin wanted to put
behind him.

www.phparch.com \ April 2024 \ 23


Solving the
Hardest
Problems
in Enterprise
PHP

PHP Professional PHP Observability


Services, Including and Orchestration
PHP Migrations Tooling With ZendHQ

PHP Long-Term Free and


Support for PHP Instructor-Led
7.2, 7.3, 7.4, 8.0 PHP Training

Ready to See What Zend


Can Offer Your Team?
Scan the QR Code, or visit
zend.com, today to explore our
industry-leading PHP solutions.

© Perforce Software. All trademarks and registered trademarks are the property of their respective owners.
RADAR

Go vs Rust: A Guide for PHP Developers


Matt Lantz
Programming languages are evolving fast, and developers need to keep up with reason. I have
heard about many colleagues wanting to explore new languages, and the two most popular
were Rust and Golang. The two popular programming languages, Golang and Rust, have risen
to prominence beyond the PHP community. These two highly sought-after languages have some
comparable features and functionality. Hopefully, I can help some PHP developers make an
informed choice on which language to learn next.

Golang is fast, but Rust outperforms it due to its fine control over
Created by Google, the Go language (popularly known as system resources. Rust’s primary differentiator is its focus
Golang) is a statically typed, compiled language known for its on memory safety while maintaining high performance.
simplicity and effectiveness. Its primary appeal is its straight- Its built-in memory management technique makes it safer
forward syntax, which makes it relatively easier to learn and without relying on garbage collection, as Golang does.
use, even for beginners. Golang shines in enabling concur- Golang has a strong edge over Rust in terms of concurrency.
rent programming and managing extensive programs. It’s It simplifies concurrent programming through goroutines
widely used in system and network programming, big data, and channels, making deploying and scaling large distributed
and cloud services. systems easier. Golang is generally easier to learn and use,
especially for PHP developers. Its syntax and conventions are
Rust more intuitive and reminiscent of common languages like
Conversely, Rust1 is a multi-paradigm programming JavaScript and Python. Both languages offer powerful tools
language focused on performance and safety, specifically safe to ensure code is well-written and bug-free. However, Rust’s
concurrency. Developed by Mozilla, Rust eliminates common steep learning curve might slightly set PHP developers back.
programming errors by implementing a strong compile-time So, Which Language Should PHP Developers Learn?
checking system. Rust features zero-cost abstractions, guar- If you prioritize simplicity and ease of learning and need to
anteed memory safety, and a rich-type system renowned in handle heavy network tasks or microservices, Golang is your
system programming, particularly where high performance go-to language. Its similarity to PHP in terms of syntax can
and safety are required. make it more of a seamless transition.
Rust would be a better fit for developers interested in
PHP system-level programming and working environments that
Given the title of this magazine, I shouldn’t have to write focus on memory safety and performance. It might initially
this, but let’s make sure we cover some bases. PHP is a widely seem complex, but its safety guarantees are unparalleled.
used open-source scripting language suitable for web devel-
opment. PHP is reasonably versatile and compatible with Performance Comparison
most infrastructure systems used today. It was first released Rust and Golang are both compiled languages known for
in 1994 by Rasmus Lerdorf and has since evolved with many their outstanding performance. Rust’s zero-cost abstractions
version updates, focusing on improved performance, power system, ownership model, and strong static typing enable it to
and syntax enhancements, and some types. However, some excel in system-level programming tasks. On the other hand,
communities have tried to get PHP into a more platform-ver- Golang’s simplicity and efficient concurrency model make it
satile state, expecting it to become a standard tool for desktop ideal for creating highly scalable services.
applications or otherwise; it is not likely to gain much trac- For PHP developers, Rust provides an opportunity to build
tion in a JavaScript-dominant world. performance-critical sections of the application with signifi-
Because of a few areas of modern cloud infrastructure that cantly higher speed. Golang, with its inherent simplicity,
are not easily PHP compatible, many PHP developers look to permits the smooth running of programs with large amounts
different languages. Some will jump to Node because it feels of concurrent processing, a significant advantage in modern
as natural as writing more powerful JavaScript. But others are web development.
left pondering: Should I try Golang or Rust?
Both of these languages deliver excellent performance, but
they approach execution times differently. Golang’s runtime

1 https://rustforphp.dev

www.phparch.com \ April 2024 \ 25


RADAR
Go vs Rust: A Guide for PHP Developers

Memory Safety which language to learn next should align with your career
Memory management is a significant challenge in system path, project needs, and personal interests. Regardless of the
programming. Rust shines here with its zero-cost abstrac- choice, discovering a new language is an exciting journey that
tions, guaranteeing memory safety without using garbage broadens understanding, improves problem-solving, and
collection. It allows you to manage memory efficiently with opens new opportunities in terms of jobs and how you can
advanced features like ownership, borrowing, and lifetimes. impact the community.
On the other hand, Golang uses garbage collection, One more thing
relieving developers from worrying about memory manage-
Within the PHP community, some lovely courses are avail-
ment. While it does provide simplicity and convenience, it
able for those who wish to adopt a new language for more
lacks the fine control and zero-cost abstractions of Rust.
high-performance components. Ryan Chandler has a course
Ease of Use on Rust, and Mohammad Said has one on Golang2. Both of
Golang is often considered to be easier to pick up and use these developers highlight the point that Rust and Golang are
from a PHP developer’s perspective. It maintains a straight- more optimal as extensions of a PHP-based application rather
forward approach to programming with clear syntax, which than a complete replacement. Handing complex processes
resembles that of PHP. Rust, with its unique concepts like to a Rust or Golang micro-service can help optimize a PHP
ownership and lifetimes, may present a steeper learning application without overhauling the whole system.
curve for PHP developers. If you’re interested in courses that can help a PHP devel-
oper adopt either of these languages, I strongly recommend
Concurrency looking at them. Ryan and Mohammad have been strong
Concurrency in Golang is a first-class citizen. It’s built into figures in the Laravel community and are highly skilled at
the language via goroutines and channels, making it easy developing scalable systems and teaching complex topics in
for PHP developers to build incredibly fast applications that an easy-to-understand manner.
handle multiple processes at once.
Rust also supports concurrency but offers a different level Matt has been developing software for over
of convenience than Golang. The ergonomic syntax in Golang 13 years. He started his career as a PHP
makes concurrent programming easier and safer, providing developer for a small marketing firm but
another reason why PHP developers might want to consider has since worked with a few Fortune 500
companies, led a couple teams of developers,
this language.
and is currently a Cloud Architect for a
Documentation and Community Support significant Travel technology company. He’s
contributed to the open-source community
Both Rust and Golang have extensive documentation and on projects such as Cordova and Laravel.
are supported by active communities. However, Golang, He also made numerous packages and has
being older and backed by Google, has a larger ecosystem and helped maintain a few. He’s worked with
arguably better support for web development. start-ups and sub-teams of big teams within
Both Golang and Rust present extensive capabilities for large divisions of companies. He spends
PHP developers looking to expand their programming time with his wife and kids when he’s not
skills. As I’ve said, these languages are not meant to replace tinkering with code or learning new technol-
PHP but aim to offer alternative approaches to solving ogies. @Mattylantz
modern programming challenges. Ultimately, deciding

2 https://themsaid.com/courses/php-to-go

26 \ April 2024 \ www.phparch.com


PHP Foundry

Dependency Injection with PHP-DI


Oscar Merida
Any codebase can quickly devolve into a tangled mess of function calls and copy-pasted snippets,
which is difficult to adapt without some upfront discipline and tools that support some consistent
organization. If we want our programs to be reusable, concise, and testable without hassle, then
we should use dependency injection as a guiding principle to write loosely coupled components.
Each piece can focus on doing one thing well while ignoring implementation details that aren’t
concerned with it. We can also swap dependencies to control how code behaves in different
environments.

Spaghetti code gets its name from the tangled branching arguments to the method call. Does this sound like a lot of
paths, large lines of nearly identical, copy-pasted blocks, potentially needless extra work? It might be, but we can use
which make it nearly impossible to understand a program a dependency injection container to centralize and manage
flow. It’s a rich source for bugs, often due to small changes object creation.
having an unintended (or unpredictable) effect elsewhere. Of
course, this is not a problem specific to PHP code. It’s why
nearly all programming languages support user-defined func-
An Example
tions, classes, and objects. CPUs don’t need all that syntactic Consider a typical—though slightly contrived—example
sugar; machine code is good enough for them. We need our function that’s going to read one or more rows from a data-
programming languages to aid us so that we can reason about, base and output them to a webpage (see Listing 1). It gets
understand, and maintain the code. the job done, but it’s highly coupled. It needs to know how
PHP 8’s object model has matured tremendously since the to connect to the database and relies on three constants that
rudimentary object-oriented support introduced by PHP must be defined globally. The output is hardcoded to immedi-
4. Using classes with well-named methods and properties ately echo an HTML table, with one record per row.
goes a long way to making our code readable and limiting
how much we need to comment it. Dependency Injection is
another useful tool to master and add to our belt. Listing 1.
1. <?php
Dependency Injection 2.
3. function showRecords(int $max): void
Object-oriented programming aims to organize code into 4. {
reusable chunks of related code, but often, many classes 5. $pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [
are filled with long lines of procedural code hidden behind 6. PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ
method calls. We want to use small classes with limited 7. ]);
8.
scopes that we can put together when we need to add a new
9. $stmt = $pdo->query(
feature or fix a bug. To put it another way, any class should
10. 'SELECT title, author, genre, pages, year
be more than happy to delegate as much of its work to other 11. FROM `books` LIMIT ' . $max
specialized objects. 12. );
Dependency injection is easy to describe and harder to 13.
adhere to in practice. A dependency is an instance of a class 14. echo '<table>';
in which our program needs to function. It can be a class 15. while ($row = $stmt->fetch()) {
that handles access to the database layer, a client for making 16. echo sprintf(
calls to an external API, or something that takes one or more 17. '<tr><td>%s</td><td>%s</td><td>%s</td>'.
18. '<td>%d</td><td>%d</td></tr>',
inputs and renders them as HTML, CSV, or some other
19. $row->title,
output format. 20. $row->author,
The function or method that needs a dependency should 21. $row->genre,
not be created using the new keyword, which may require 22. $row->pages,
knowing how to create the dependency’s dependencies. They 23. $row->year
should already be available when we need them. To do so, 24. );
we inject the dependencies — which is just a fancy way of 25. }
saying we pass them either as constructor arguments or as 26. echo '</table>';
27. }

www.phparch.com \ April 2024 \ 27


PHP Foundry


Let’s consider the main tasks we’re asking our function to kind of “Renderer” we pass to showRecords(), as you can see
perform. First, we’re querying a database table named books on Listing 3. First, we defined an interface to specify how
to get some number up to $max results. Then, we create a any ResultRenderer must work. By doing so, no matter what
simple HTML table for each row returned. Those sound concrete implementation we pass as showRecords() as the
like two separate concerns: reading from the database and $output parameter, showRecords() knows how to use it. All
creating the HTML output, which we could inject when we showRecords() cares about is that there’s a render() method
call showRecords. that takes our database query results. The implementation
Listing 2 updates showRecords so that it takes two additional details of outputting a CSV file are handled by a new class,
arguments. This is how we inject our dependencies. The first
is the PDO connection object used to query the database, Listing 3.
followed by a new class, HTMLBookTableOutput, that renders
1. <?php
2.
Listing 2. 3. interface ResultRenderer {
4. public function render(PDOStatement $stmt): string;
1. <?php
5. }
2. class HTMLBookTableOutput 6.
3. { 7. class CSVBookOutput implements ResultRenderer
4. public function render(PDOStatement $stmt): string 8. {
5. { 9. private $fh;
6. $out = '<table>'; 10.
7. while ($row = $stmt->fetch()) { 11. public function __construct(public string $filepath)
8. $out .= sprintf( 12. {
9. '<tr><td>%s</td><td>%s</td><td>%s</td>' 13. $this->fh = fopen($filepath, 'w');
10. . '<td>%d</td><td>%d</td></tr>', 14. }
11. $row->title, 15. public function render(PDOStatement $stmt): string
12. $row->author, 16. {
13. $row->genre, 17. while ($row = $stmt->fetch()) {
14. $row->pages, 18. fputcsv($this->fh, (array) $row);
15. $row->year 19. }
16. ); 20.
17. } 21. fclose($this->fh);
18. 22.
19. $out .= '</table>'; 23. return file_get_contents($this->filepath);
20. return $out; 24. }
21. } 25. }
22. } 26.
23. 27. function showRecords(
24. function showRecords( 28. int $max, \PDO $pdo, ResultRenderer $output
25. int $max, \PDO $pdo, HTMLBookOutput $output 29. ): string {
26. ): string { 30. $stmt = $pdo->query(
27. $stmt = $pdo->query( 31. 'SELECT title, author, genre, pages, year
28. 'SELECT title, author, genre, pages, year 32. FROM `books` LIMIT ' . $max
29. FROM `books` LIMIT ' . $max 33. );
30. ); 34.
31. 35. return $output->render($stmt);
32. return $output->render($stmt); 36. }
33. } 37.
34. 38. // these constants still come from somewhere external
35. // these constants still come from somewhere external 39. $pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [
36. $pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [ 40. PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ
37. PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ 41. ]);
38. ]); 42.
39. showRecord(20, $pdo, new HTMLBookTableOutput()); 43. $output = new CSVBookOutput(__DIR__ .'/example.csv');
44. showRecord(20, $pdo, $output);

the HTML table. As a result, showRecords() is just 6 lines CSVBookOutput. Note that in both listings, showRecords doesn’t
compared to twenty. change at all.
What did that buy us? Consider a new request to show $max A further refinement to showRecords() could be to define an
number of books as a CSV file. We need only change what interface for classes that retrieve books from any number of

28 \ April 2024 \ www.phparch.com


PHP Foundry


sources—databases, document stores, external APIs, or local instances and use reflection or a custom call to instantiate any
files. We’d need to change how a renderer works, but it’s not object we need.
Since this is a common component across frameworks,
Listing 4. the PHP-FIG has defined PSR-11: Container Interface1 to
“standardize how frameworks and libraries make use of a
1. <?php
2.
container to obtain objects and parameters”. This gives us a
3. interface BookRepository { number of solutions we can use depending on our framework
4. /** and preferences:
5. * @return BookRecord[] • Symfony’s DependencyInjection Component2
6. */
7. public function retrieve(int $max): array; • The PHP League’s Container3
8. } • The succinctly named PHP-DI4
9.
10. interface ResultRenderer {
11. /** Storing Sensitive Information
12. * @return BookRecord[] $books
When we register our new account with SpaceTraders, the
13. */
14. public function render(array $books): string; response it returns includes a bearer token we must save and
15. } use to authenticate future requests. Since whoever knows that
16. token can act on our behalf, we want to save it somewhere
17. function showRecords( safe. Certainly, we should never commit secrets into a git
18. int $max, repository. While convenient, it is not a secure practice. Once
19. BookRepository $repo, you’ve committed a secret to your version control’s history,
20. ResultRenderer $output removing it may be difficult or impossible. If your code leaks
21. ): string {
or is public, then anyone has it.
22. $books = $repo->retrieve($max);
23. return $output->render($books); One more thing: if you need to use different secrets for
24. } testing, sandbox environments, or independent installs,
saving your database passwords or API tokens alongside
your code couples it to those environments. Think of it this
way—your code shouldn’t know that you have a local envi-
a huge leap from what we have so far. I’ve sketched out the ronment, a testing one, a staging one, or even a production
interfaces and typehints in Listing 4. environment. Your code just needs to know how to connect
to one of them via common configuration settings like URLs
and credentials.
Tips for Success Luckily for us, we have vlucas/phpdotenv5, which reads
Using dependency injection takes effort when you’re used secrets from .env files and makes them available via the $ENV
to making the objects you need where and when you need super global. Their readme walks through the basic steps for
them, but it pays dividends quickly. Keep these things in mind. adding it to a project:
• Use interfaces to define the injected dependencies. This 1. Install it: composer require vlucas/phpdotenv
decouples your code, though you can get away with 2. Create a .env file and add a line for it to .gitignore
specifying a concrete class if it will never change. 3. Create a .env.example file for sharing stubs so anyone
• Objects never create other objects (unless they’re using your project knows what secrets they will need.
a factory). Try to always pass in a dependency as a
constructor argument, preferably as a method argument. Our initial .env.example file is one line, but we’ll likely add
• Classes shouldn’t inspect global variables, superglo- more as the project grows.
bals, or other bits of external state to influence how they # token value from registering a spacetraders callsign
behave. SPACETRADERS_TOKEN=""

Containers
The example earlier has very few dependencies that I
manually tracked and created. In practice, we wouldn’t want 1 https://www.php-fig.org/psr/psr-11/
to create the same dependencies every time we need one 2 https://phpa.me/symfony-di
throughout our code. Dependency injection containers help 3 https://container.thephpleague.com
us manage this task by centralizing how we request object 4 https://php-di.org
5 https://github.com/vlucas/phpdotenv

www.phparch.com \ April 2024 \ 29


PHP Foundry


I then updated the spacetraders CLI app to load the values Once we have an account register, other commands should
from .env by adding these two lines after Composer’s auto- interact with the API through a $client instance. For example,
loader: to read information about our “Agent” in spacetraders, I
added another CLI command for it that calls a new method
require __DIR__ . '/vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__); Listing 6.


$dotenv->load();
1. <?php
2.
3. namespace Phparch\SpaceTraders;
4.
5. class Client
Listing 5. 6. {
7. private string $baseURI =
1. public function handle(): void 8. 'https://api.spacetraders.io/v2/';
2. { 9.
3. $ch = curl_init(); 10. public function __construct(
4. 11. private string $token = '',
5. $data = json_encode([ 12. private \GuzzleHttp\Client $guzzle,
6. 'symbol' => 'PHP_ARCHIE', 13. ) {}
7. 'faction' => 'COSMIC' 14.
8. ]); 15. public function register(
9. curl_setopt( 16. string $symbol, string $faction
10. $ch, \CURLOPT_URL, 17. ){
11. 'https://api.spacetraders.io/v2/register' 18. return $this->post('register',
12. ); 19. data: [
13. curl_setopt($ch, \CURLOPT_POST, true); 20. 'symbol' => $symbol,
14. curl_setopt($ch, CURLOPT_HTTPHEADER, [ 21. 'faction' => $faction
15. 'Content-Type: application/json' 22. ],
16. ]); 23. authenticate: false);
17. curl_setopt($ch, \CURLOPT_POSTFIELDS, $data); 24. }
18. curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true); 25.
19. 26. public function myAgent(): object
20. $response = curl_exec($ch); 27. {
21. $this->success($response); 28. $response = $this->get('my/agent');
22. } 29.
30. return $response;
31. }
32.
Moving Curl Dependencies 33. // see GitHub repository for implementations
34. private function get(string $url);
We used native curl_ calls in the initial implementation of
35. private function post(
our command to register a call sign. Recall that the command 36. string $url,
looked something like Listing 5. 37. array $data,
While powerful, PHP’s cURL library is cumbersome to 38. bool $authenticate = true
work with and not object-oriented. Luckily, we have many 39. );
wrappers that we can use. One popular solution is Guzzle6. 40. }

composer require guzzlehttp/guzzle

Instead of having all those cURL lines in our controller, we named myAgent(). Listing 6 outlines such a class. Notice that
want to have thin commands that call the service classes that our constructor specifies two dependencies that we need
do the real work. The command should take care of any input to function: a $token, if we have one, and any instance of a
validation and then call something like: guzzle client. We’re not hardcoding how Guzzle behaves, if it
follows redirects, how long it waits for timeouts, etc., within
$client = new Phparch\SpaceTraders\Client($token); the Spacetraders client. All the client cares about is, “Give me
$response = $client->register($symbol, $faction); a guzzle object that I know how to use”.

6 https://www.github.com/guzzle/guzzle

30 \ April 2024 \ www.phparch.com


PHP Foundry


Adding Php-di to Spacetraders Listing 7.


We’ve kept our dependencies out of Spacetraders client
1. <?php
class, but we have to instantiate it somehow in our command 2.
controller. How do we do that? Let’s look at setting up a 3. namespace Phparch\SpaceTraders;
PHP-DI as our dependency injection container. The first step 4.
is to bring it in via Composer 5. use DI;
6. use DI\Container;
composer require php-di/php-di 7.
8. class ServiceContainer
9. {
Mini-cli has a container. However, it is not PSR-11 10. private static Container $container;
compliant. That means we can’t reuse the container elsewhere, 11.
like a web-based front end, if we end up making one. Instead 12. public static function config(array $config)
of tying myself into its custom container, I decided to use one 13. {
14. if (!isset(self::$container)) {
that follows PSR-11. Since we won’t be using the provided
15. self::$container = new Container();
container, I’m following a pattern introduced to me by Chris 16. }
Tankersley and turning our container into a service locator. 17.
We can call this service locator statically whenever we need 18. foreach ($config as $name => $service) {
an instance of an object (see Listing 7). Ideally, we do this 19. self::$container->set($name, $service);
as close to the “edges” of our application, mainly controllers. 20. }
Avoid calling the service locator from within a service. If you 21. }
find yourself doing that, it’s a code smell for a dependency 22. /**
23. * Returns a service from the Service Container
that you can inject via your constructor.
24. *
Adding the ServiceLocator to the command line app 25. * Most services that are implemented as classes
requires two new lines after the lines for phpdotenv. 26. * should use the class name as the service name.
27. * For services that need additional setup or a
use \Phparch\SpaceTraders\ServiceContainer;
$services = require __DIR__ . '/config/services.php'; 28. * name that does not match a class name, register
29. * the name explicitly in this method.
ServiceContainer::config($services); 30. *
31. * @template T
PHP/DI uses reflection to instantiate objects with all their 32. * Name of the Service to return
required dependencies if it can. If we need to pass secrets or 33. * @param class-string<T> $serviceName
have a class with a complicated constructor, we can specify 34. * @return T
35. * @throws DI\DependencyException
how that happens in config/services.php. This configuration
36. * @throws DI\NotFoundException
file (Listing 9) is an associative array of service identifiers and 37. */
a callable function that returns that service. We can use any 38. public static function get(string $serviceName)
string for the key, though I find that using a class name makes 39. {
it easy to keep track of what’s in a specific service “slot”. That’s 40. if (!isset(self::$container)) {
why you see an entry for SpaceTraders\Client::class. This 41. self::$container = new Container();
entry’s callable executes when something requests that client. 42. }
43.
44. return self::$container->get($serviceName);
Listing 8. 45. }
46.
1. <?php 47. /**
2. 48. * For when you need a new instance of a class
3. use GuzzleHttp\Client; 49. *
4. use Phparch\SpaceTraders; 50. * @template T
5. use Phparch\SpaceTraders\ServiceContainer; 51. * Name of the Service to return
6.
52. * @param class-string<T> $serviceName
7. return [
53. * @return T
8. SpaceTraders\Client::class => function() {
54. * @throws DI\DependencyException
9. return new SpaceTraders\Client(
55. * @throws DI\NotFoundException
10. $_ENV['SPACETRADERS_TOKEN'],
56. */
11. ServiceContainer::get(Client::class));
57. public static function make(string $serviceName)
12. }
58. {
13. ];
59. return self::$container->make($serviceName);
60. }
61. }

www.phparch.com \ April 2024 \ 31


PHP Foundry


It knows how to get our authentication token to pass it with a


Listing 9.
Guzzle client instance.
You can see in Listing 8 how we use the ServiceContainer to 1. public function handle(): void
get our client instance. The command does very little besides 2. {
validating the command line arguments. It doesn’t need to 3. $client = ServiceContainer::get(Client::class);
know where to find a token or even that a Guzzle client is 4.
required. It starts by asking the ServiceContainer for the 5. try {
6. $symbol = $this->getParam('symbol');
client. If the inputs are valid, it calls register() and returns
7. $faction = $this->getParam('faction');
the response. 8.
Almost all the calls for an object instance via PHP’s new 9. if (!preg_match('/[[:alnum:]_]+/', $symbol)) {
keyword should happen here. Doing so keeps the rest of 10. throw new \InvalidArgumentException(
our code loosely coupled. Of course, if you’re working with 11. "Symbol can only incl letters, numbers, underscores"
a framework like Laravel or Symfony or a CMS like Drupal 12. );
that has native containers, you’re best off following its recom- 13. }
14.
mended practices. This statically-called “ServiceLocator”
15. if (!preg_match('/[[:alnum:]_]+/', $faction)) {
approach is useful with WordPress or spaghetti legacy code. 16. throw new \InvalidArgumentException(
17. "Faction can only incl letters, numbers, underscores"
The code for this project is also available at https://github. 18. );
com/phparch/spacetraders 19. }
20.
21. $response = $client->register($symbol, $faction);
22. $this->success(json_encode(
Conclusion 23. $response,
24. flags: JSON_PRETTY_PRINT
By understanding and sticking to Dependency Injection in 25. ));
your applications, you’ll write code that is flexible and more 26. } catch (\Throwable $e) {
composable. It should guide you in organizing your code into 27. $this->error($e->getMessage());
objects with few responsibilities that are willing to delegate 28. }
implementation details to other classes. This may seem like 29. }
“over-engineering” at first. It might look like your source code
is hard to understand and “keep in your head” when the
implementations are scattered across source files. You may be
tempted to write methods that do it all in one place. I urge
you to resist.
Dependency injection pays off in the long run. When
it comes time to writing unit tests, we can control how our
code behaves to recreate error conditions by swapping in test
doubles.

Oscar Merida has been working with


PHP since version 4 was released and is
constantly learning new things about it and
still remembers installing an early version
on Apache. When not coding or writing, he
enjoys RPGs, soccer, and drawing. @omerida

32 \ April 2024 \ www.phparch.com


php[architect]
[consulting]
Get
customized
solutions for
your business
needs
Leverage the
expertise of
experienced
PHP Create a
developers dedicated team
or augment
your existing
team

Improve the Building


performance cutting-edge
and scalability solutions using
of your web today's
applications development
patterns
and best
practices
[email protected]
finally{}

The Changing Face of Networking


Beth Tucker Long
The pandemic brought about a rapid shift in work environments for many businesses. Remote working was suddenly required
where it hadn’t even been allowed before. It was a bumpy ride for many companies that were unprepared technology-wise for
remote working, but it was also a truly painful transition for those who did not have leadership trained in managing remote
teams.
While things have been slowly returning to pre-pandemic work environments, studies are showing that we are not returning
unchanged. Nature.com published “The impact of COVID-19 on digital communication patterns”, a study showing that we
are spending less time in digital meetings but having more of them. Shorter, more focused meetings are good for productivity
but not for getting to know people. By the end of the day, the increase in the number of meetings can leave us exhausted and
unwilling to attend another meeting outside of work, even if it is beneficial for us.
Why should we care about professional networks? Harvard Business Review in “Research: We’re Losing Touch with Our
Networks” reports that not having a healthy professional network is very detrimental for employees: “It can make finding a job
more difficult. It can hinder career progress and make it harder to get promoted.”
Finding a job, progressing your career, and getting promoted are all critical steps for employees. Companies need to pay
attention, though, too. What does the Harvard Business Review article say about the impact of poor employee professional
networks on companies? “People with fewer connections at work have a decreased sense of belonging and are less likely to identify
with the organization, which puts them at higher risk of turnover and possibly even fraud and negligence.”
That is definitely not good, so where does this leave our professional networks? During the pandemic, many networking
events continued by going virtual. Virtual meetings, however, are much more prone to distraction, between being able to
hide by keeping your camera off, the relative ease with which you can do other activities during the meeting, and difficulties
in cohesive conversations due to lag, lack of body language cues, and delays in figuring out who is talking by having to read a
list rather than just identifying the speaker by visual location in your immediate area. This all adds up to major difficulties in
maintaining and growing your network. A recent study shows that professional networks decreased during the pandemic and
by a pretty significant amount at just under 27%:
“We find that the global estimated network size of respondents decreased from 1,503 people in June 2019 to 1,098 individuals in
June 2020…”
“Social Networks and Loneliness During the COVID-19 Pandemic” - https://phpa.me/sagepub
Professional networks are strong providers of recommendations, referrals, and business, which means major money is left
on the table.
We need to find a way to help people rebuild their professional networks. User groups are a great way to network, but it
is not going to be easy to convince people to attend. People are burned out from heightened stress over the last few years
and, according to “The impact of COVID-19 on digital communication patterns” study, are working ~8.2% longer work days
and sending ~8.3% more after-business-hours emails, which means people have less free time to spend at networking events.
Events need to be compact, easy to attend, and meaningful to convince people to attend; even that might not be enough to
overcome post-pandemic exhaustion.
Related URLs:
• Nature.com: The impact of COVID-19 on digital communication patterns - https://phpa.me/nature-comms
• Social Networks and Loneliness During the COVID-19 Pandemic - https://phpa.me/sagepub
• Research: We’re Losing Touch with Our Networks - https://phpa.me/losing-touch
• The Changing Face Of Post-Pandemic Business Networking - https://phpa.me/post-pandemic-business

Beth Tucker Long is a developer and owner at Treeline Design, a web development company, and runs Exploricon, a
gaming convention, along with her husband, Chris. She leads the Madison Web Design & Development and Full Stack
Madison user groups. You can find her on her blog (http://www.alittleofboth.com) or on Twitter @e3BethT

34 \ April 2024 \ www.phparch.com

You might also like