Phparchitect 2024 07
Phparchitect 2024 07
SEARCH FOR
GOOD CODE
Advanced Search For Laravel Applications
Also Inside:
'
a php[architect] guide
Write For Us
Search For Good Code If you would like to contribute,
contact us, and one of our editors
As PHP Architects, our relentless a comprehensive approach, delving will be happy to help you hone
pursuit of high-quality code is always at into unit testing, semantic versioning, your idea and turn it into a beauti-
the forefront of our work. We take pride method/function signature changes, and ful article for our magazine.
in assuring our clients that the applica- dependency updates, fostering a deeper Visit https://phpa.me/write
tions we build are not meant to confine understanding of this critical topic. or contact our editorial team
them. At the same time, we value the Edward Barnard’s captivating piece, at [email protected] and get
opportunity to cultivate long-standing ‘User Workflow Improvement with Hedy started!
partnerships with our clients, many of Lemarr,’ offers a fascinating glimpse from
whom we consider to be exceptional 2500 Feet into the world of program-
business collaborators. mers’ minds through the lens of another Stay in Touch
We also understand that some compa- language, providing an enriching and Don't miss out on conference,
nies want to hire a company to build the thought-provoking read. book, and special announcments.
initial proof of concept and then bring In the Security Corner, Eric Mann Make sure you're connected with
the development in-house. Good code continues to enlighten us with ‘PHP us.
and standard coding practices help us Under Attack’, dispelling misconceptions • Subscribe to our list:
achieve this. Don’t let yourself fall into about PHP’s security and highlighting its https://phpa.me/sub-to-updates
the trap that you code in a way to achieve significant advancements over the years, • Mastodon:
job security. I’ve seen too many instances nurturing a sense of confidence in the @[email protected]
where developers tried that, only to be language’s robustness.
• Twitter: @phparch
out of work because of how they acted Christopher Miller’s insightful explora-
towards the project. tion of Authentication and Authorization • Facebook:
Continuous education serves as the in the ongoing “Securing PHP Applica- http://facebook.com/phparch
cornerstone of upholding exemplary tions” series promises to further fortify
coding practices. Active engagement our understanding of essential security
within the community allows us to culti- measures.
vate a network of professionals from With an optimistic outlook, Matt Lantz
whom we can seek assistance when introduces us to the cutting-edge tech-
necessary. With these principles in mind, nology of WebAssembly in the RADAR
let’s approach this month’s challenges column, demonstrating its potential
with renewed determination and a focus through ‘PHP & WebAssembly: An Edge
on the two key concepts outlined. Case,’ staying true to his commitment to
Now, let’s embark on an exciting keeping us abreast of the latest advance-
journey into this month’s issue, embracing ments.
the wealth of knowledge awaiting us. Oscar Merida shares invaluable insights
The highlight of this edition is the on maintaining the robustness of our
compelling article penned by Cesar applications with ‘PHPStan’s Watchful
Augusto Mendez Vargas, titled ‘Advanced Eye,’ empowering us to proactively safe-
Search For Laravel Applications.’ Cesar guard our systems against potential bugs.
adeptly illustrates how to integrate Wendell Adriel’s comprehensive guide,
full-text search capabilities into your ‘Understanding and Implementing a
application, offering invaluable insights Dependency Injection Container—Part
and practical guidance. Delving into II,’ promises to elevate our expertise
segmentation, normalization, filters, and nurture our skills, enabling us to
conditions, and more, this article prom- construct a robust Dependency Injection
ises to equip you and your team with a system from the ground up.
powerful tool for enhancing application Lastly, Beth Tucker Long provides a
functionality. compelling perspective on the evolving
In the Education Station, Chris status of PHP in ‘Driving Abandonment,’
Tankersly passionately advocates for offering a thoughtful and insightful Download the Code
preserving ‘Backward Compatibility’, outlook on the language’s trajectory. Archive:
shedding light on its paramount signifi- This month’s lineup is a treasure trove
https://phpa.me/July2024_code
cance. Chris skillfully navigates through of knowledge and inspiration, inviting us
the intricacies, emphasizing the adverse all to explore, learn, and grow.
impacts of breaking userspace. He takes
FEATURE
As we can see, from now on, instead of looking for each Please note that the processing time to create/update a
token on each document, we can go to our inverted index, see filter will vary depending on your database size.
which documents have the tokens of our search, and return Now that you have created your filters, you can perform
them accordingly. searches on the data with more complex queries; this is done
After gaining some basic understanding of how full-text thanks to the following:
search works, let’s look at some useful functionalities. Each Conditions
topic can be a book on its own, but after covering the basics,
you will be able to integrate the most used parts of your app. Conditions are a filter’s basic building blocks. They are
written in theattribute OPERATOR valueformat, where:
author = "John Doe" To override these default settings, you can use theminWord-
author = 'John Doe' SizeForTyposobject.
title = 'AND'
$client->index('books')->updateTypoTolerance([
Otherwise, the parser might not be able to handle your 'minWordSizeForTypos' => [
query and throw an error. 'oneTypo' => 3,
'twoTypos' => 8
]
Facets ]);
Ever wondered how online stores automatically organize
Keep in mind that oneTypo has to be between 0 and
categories based on your search parameters? This is done
twoTypos ( 0 <= oneTypo <= twoTypos ), and twoTypos have to
using an approach called Faceted Search.
be less than 255 (twoTypos <= 255).
A faceted search is a method of searching where users can
trim the results by filtering groups of data—each group is Disabling Typo Tolerance in Certain Cases:
called a Facet. It is an intuitive way of displaying data and By default, typo tolerance is applied to every token, but
allowing users to navigate through content. In Meilisearch, what if you don’t want to allow a typo for certain words? For
this is done through filtering, as facets are a specific use case example, we might not want results for MySQL to be mixed
of filters. with NoSQL and vice versa.
Let’s say you have an online clothing store—users looking For this scenario, we have the disableOnWords setting:
for “Blue t-shirts” will have a huge list of results. Having this
amount of results can be overwhelming, but, if we implement $client->index('books')->updateTypoTolerance([
facets, they will be able to reduce the output by selecting 'disableOnWords' => ['nosql', 'mysql']
]);
things like Brand, size, material, etc… making the process
faster, easier, and less time-consuming. Let’s say we want exact matches for all book titles and not
Example of a faceted search interface3: (See Figure 3) only certain words; we can set the disableOnAttributes for
this particular case:
Figure 3.
$client->index('books')->updateTypoTolerance([
'disableOnAttributes' => ['title']
]);
Relevancy
When using a full-text search engine, we might end up
getting a lot of unrelated documents, or, better said, results
are not being displayed in the order that makes sense.
For this particular case, Meilisearch has several features for
fine-tuning the relevancy of search results, one of them being
Typo Tolerance ranking rules, which is a way to sort search responses based
on a consecutive set of rules.
One advantage of full-text search is that it has a higher level
Meilisearch has a predefined set of rules for all indexes. For
of tolerance of typos; for example, it can show results for “Red
each search, the results are sorted using bucket sort. The order
t-shirts” when users type “Red t-shorts”.
in which rules are applied matters, as only the first ranking
On Meilisearch, this feature is enabled by default, but we
rule is applied to all documents, and the subsequent rules are
can modify or disable it. Let’s take a look at a few settings
only applied if needed to break the tier.
customizations:
The default rules for search are (in order as of the time of
Number of Typos PER Token writing this article):
The default rule for typo tolerance on Meilisearch is: 1. Words Documents with more token matches from the
It will accept one typo for query terms containing five search string are ranked higher.
or more characters and up to two typos if the term is at
least nine characters long. 2. Typos Documents with the less amount of typos are
Example: If you have a document that contains the word ranked higher.
mysql, searching for misql will match mysql because it has 5 or 3. Proximity Documents with the tokens being closer
more characters. On the other hand, searching for PGP will not together and in the same order of the search string are
match PHP as it has less than 5 characters. ranked higher.
4. Attribute This is based on the Attribute ranking
3 https://phpa.me/faceted-search
order, tokens that are on more relevant attributes are Model Configuration
ranked higher. (On meilisearch you can define how You can tune several things inside your Laravel model; one
relevant an attribute is for search results, for example, of the most important is the data that will be fed to the search
the title might be more relevant than the author or engine. If not provided, Laravel Scout will simply dump the
category). toArray() response into the engine. This can result in unnec-
5. Sort When the sort ranking rule is in a higher posi- essary information being stored. To have better control over
tion, sorting is exhaustive: results will be less relevant the date, you can define the information you want to share as
but follow the user-defined sorting order more closely. follows:
When the sort is in a lower position, sorting is rele-
vant: results will be very relevant but might not always public function toSearchableArray() {
return [
follow the order defined by the user. 'id' => $this->id,
6. Exactness The more similar the tokens of the search 'title' => $this->title,
string and the document are, the higher the docu- // ...
ment’s ranking. ];
}
Note: you can change the order of the rules and even create
your own rules Index Settings
As part of your deployment process, you should run the
How to Integrate a Laravel Project following command to have the latest index settings:
Laravel has a first-party library for full-text search called php artisan scout:sync-index-settings
Laravel Scout4. I always recommend sticking to official
Laravel packages, as they play well together and allow for
better migration in case you need to. According to their How Scout Works Under the Hood
description, “Laravel Scout provides a simple, driver-based
solution for adding full-text search to your Eloquent models.” Laravel Scout works by running your searches directly on
Laravel Scout has official support for Algolia, Meiliseach, the search engine instead of the database.
Typesense, and a database driver (MySQL and PostgreSQL). After it gets the results, it fetches the objects from the data-
Also, you can easily extend it by creating a custom driver for base based on the defined key and value configuration and
your search implementations. hydrates eloquent.
Basically, it runs the following query on your database:
Installation
SELECT * FROM books WHERE id IN (12, 33, 45, 67)
Add the Dependency:
This being said, there are a couple of important things to
composer require laravel/scout take into consideration:
• The search engine and your eloquent database are inde-
Publish Configuration: pendent of each other, so things like query scopes and
This is important because you can set the index settings on Eloquent queries won’t be supported. They will need to
the config without the need to interact with the client: be applied manually on Scout.
For a better understanding, please refer to the following
php artisan vendor:publish graph: (See Figure 4)
--provider="Laravel\Scout\ScoutServiceProvider"
Figure 4.
Set Up the Model
To make a model searchable with Laravel Scout, you need
to use the Searchable trait:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Book extends Model {
use Searchable;
}
4 https://laravel.com/docs/11.x/scout
// book data
Complementing Your Queries with
$book->save();
Your Operational Database
We recommend using a queue, by setting 'queue' => true
After Scout has delivered all the results, you can make use of
on your config/scout.php file and setting up a queue on your
the query method to do further customization, this time, on
Laravel installation if you haven’t. This way, you can offload
your operational database side. Keep in mind that you should
this operation from the main request and process it in the
not use this for filtering; instead, use the where statements
background.
described in this article.
Note: Whether you set the queue to false or not, Meilisearch
will perform the process asynchronously, and changes might Book::search('Concurrency in PHP')
not reflect immediately. ->query(
Finally, to set meilisearch as your scout search engine: fn(Builder $query) => $query->with('reviews')
)->get();
First, install the Meilisearch PHP SDK package:
composer require meilisearch/meilisearch-php \
http-interop/http-factory-guzzle Final Words
Lastly, set the following .env variables: Full text search is a powerful tool that will allow you to
improve your search by allowing more complex queries with
SCOUT_DRIVER=meilisearch faster responses and less resource usage. After reading this
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey
article, you already know the basics and can integrate it into
your project easily, no matter the engine; as long as it has
Laravel Scout support, you are good to go.
Not that you have set up everything, but you will be able to
run queries using eloquent; here are some examples: César is a Systems engineer with more than
Run simple searches: 8 years of working with many PHP tools
To run a search all you need to do is use the search method, such as Laravel, Phalcon, Yii, and WordPress.
which is defined in the Searchable trait. Fan of optimizing things, adventurer &
curious. When not programming you can
Book::search('Concurrency in PHP')->get(); find him doing road trips, hiking, cooking,
and creating new Dragon Fruit breeds.
Run searches with filters: Laravel Scout query builder has @CamvDev
3 simple implementations of the where clause: where, whereIn,
and whereNotIn. These clauses only support scalar values
such as integers and strings, limiting when you need to imple-
ment things like nested queries, less or greater operators, etc…
Let’s say you want to query books within a category; you
just need to run:
Book::search('Concurrency in PHP')
->where('category', 'programming')->get();
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.
5 https://phpa.me/php-usage-0321
Sometimes, I prefer to delay writing Now, in 2024, Hedy Lamarr is better among the richest in the world. He
an essay for php[architect] when the known as The Mother of WiFi2 than as made his fortune manufacturing and
code or feature isn’t actually working Delilah in the 1949 film Samson and selling arms and ammunition to both
as of yet. Right now is one of those Delilah3. What else was remarkable sides of numerous conflicts.7
times. Emergencies happen. It’s quite besides how she used a player piano
fortunate that I got lucky. A thoughtful roll to describe a frequency-hopping As Madame Mandl, I presided at
php[architect] reader, Karina, baited a algorithm that was, decades later, re-in- parties honoring all kinds of nota-
hook for me and tossed it into the open: vented for WiFi, Bluetooth, and CDMA bles, from stage and screen stars to
cell phone transmissions? Hedy Lamarr heads of state. There were fine men,
Today I learned that Hedy (a co-invented a “secret communication and there were bounders. Posturing
localizable textual programming system” with composer George Antheil. Adolf Hitler kissed my hand, and
language for the classroom) is That invention describes frequen- on another occasion pompous little
named after Hedy Lamarr. cy-hopping transmissions now used Mussolini held my chair. Enter-
for WiFi, Bluetooth, GPS, etc. Antheil’s taining was Mandl’s way of doing
younger brother Henry Antheil Jr. was business.
I asked myself, “Hedy as a program-
ming language?” I didn’t see the an American diplomat shot down
connection. In keeping with this by Soviet bomber aircraft on June 14, Hedy, as showpiece Madame Mandl,
column’s theme, let’s first look for a 1940. That downed aircraft was recently was literally a prisoner. After various
1940s connection, perhaps to mili- discovered, more than 80 years later failed escape attempts, she laid plans
tary aviation. When first looking at a https://phpa.me/finland-missing-plane4. as she interviewed women as a replace-
problem, it’s often necessary to do a bit We know that during that time period ment maid. One, Laura, was as tall as
of digging to turn up useful details–or (the 1940s), actress Ann Sheridan5 she was and with similar coloring and
to define the boundaries of the problem. was the second-most-favorite actress looks. Hedy practiced Laura’s walk and
No less a personage than William F. among the soldiers. Hedy (rhymes with talk.
Friedman, founding cryptanalyst of the “lady”) was first. That doesn’t sound like
National Security Agency, told us this is it leads to a programming language. It had been his game to keep me a
the case:1 Hedy Lamarr was born in Vienna, prisoner. It had been my game to
within the Austria-Hungarian Empire, escape. He had lost.8
Often, indeed, the student will not on November 9, 1914, though her
even know whether he is on the autobiography says 1915.6 She was
Have we learned anything relevant to
right track until he has performed a the teenage bride of Fritz Mandl, an
the problem at hand? Yes, perhaps we
large amount of preliminary “spade extremely dangerous man who was
have. One goal was to define the bound-
work” involving many hours of
aries of the problem. Hedy Mandl was
labor. 2 https://phpa.me/mother-of-wifi able to escape in that most dangerous
3 https://www.imdb.com/title/tt0041838/ of games.
1 William F. Friedman. Military
4 https://phpa.me/finland-missing-plane
Cryptanalysis, Part I, Monoalphabetic
Substitution System; 4th Edition, Fried- 5
man, Revised And Enlarged By Callimahos. https://en.wikipedia.org/wiki/Ann_Sheridan
Accessed June 9, 2024. http://archive.org/ 6 Lamarr, Hedy. Ecstasy and Me: My Life
details/41748389078762, Section I page 2, pdf as a Woman. New York: Bartholomew House, 7 Lamarr Ecstasy, page 21.
page 32. 1966, page 16. 8 Lamarr, Ecstasy, page 34.
Hedy Lamarr, in her autobiography, does not mention Programming syntax errors, by their opaqueness, present
any of her inventions. That, obviously, was not what people early barriers to success. I immediately flashed back to
wanted to hear.9 that worst and most fearsome horror from my distant past:
IEY013I SYNTAX spewed within my IBM System/360 Fortran
The first important actresses I met were Joan Crawford IV (G Extended) program listings. Figure 1 (next page) shows
and Joan Bennett. It was at the Turf Club of Santa Anita a program listing with errors. Figure 2 (next page) shows the
racetrack. (Joan Crawford was to be named actress who opaque explanation. Figure 3 (next page) shows how IBM
acted most like a Hollywood star. Joan Bennett had the thought of software developers in the early 1970s.12
greatest dignity. She was from a highly regarded theatrical Note that IBM specifically explains: “If a statement cannot
family. And I was often to be called Hollywood’s most be identified, this error message is used.” This horror-in-
beautiful star.) ducing message appears when you have made a mistake and
likely have no idea what is wrong, and neither does IBM’s
One further connection should not be a surprise: Several Fortran compiler. It’s no wonder so many would-be student
World War II aircraft were named after her–or, specifically, programmers make the War Games computer’s choice, “The
named “Tondelayo”10 after her role in a 1942 movie. Alberto only winning move is not to play”, and choose a different area
Vargas created publicity (pin-up) art with Hedy Lamarr as of study.
Tondelayo. One airworthy B-25J Mitchell currently flies as
Tondelayo. User Workflow Improvement
Professor Hermans’s paper is well worth reading, and
Reset here’s why. Through user experience research (students
We’ve now gathered some facts, most of them scandalous. writing Hedy code), she makes it easier and easier to learn
Let’s see what we can find out about the programming the programming language. She created levels, each level
language Hedy. Professor Felienne Hermans, the language being more advanced until the final level is programming in
creator, presented an ACM paper.11 Python. That’s not the point I’m making.
Is this a dead end as well? Hedy is a programming language Walk through her paper as she describes her thought
aimed at children aged 11-12. On the contrary, Hermans process for improving Hedy based on programming-error
explains: statistics. She’s smoothing out the students’ workflows and
improving the student experience based on the classes of
With computer technology being a tool of rapidly growing errors they are making.
importance in nearly all aspects of life, the world also My users, in particular, are famous for not following the
needs programmers. In fact, the world needs ever more webpage workflow I designed. Users find multiple ways to
programmers; the number of software developer jobs is surprise me, sometimes at the rate of hundreds of surprises
expected to grow with 17% by 2024, much faster than the per second. This approach is different from a sales funnel
average rate among other professions. aimed at getting the user to the end goal (finalize a sale).
Hermans’s approach aims to make unfamiliar tasks easier
to perform. In her case, it’s about writing code as you’re just
We’re in the industry; we all know this to be true. However,
learning to code, but I see this as a valuable way of improving
Hermans, with Hedy, is on to something. She explains that
any user workflow.
young learners often struggle with the syntax of a program-
ming language in addition to learning what computer
programming is all about, what an algorithm is, and so on. The Programmer’s Brain
She notes that when very-young students are first learning Professor Hermans wrote a book a few years ago; it’s on
to write, they write lower-case words without punctuation. my shelf, The Programmer’s Brain: What Every Programmer
Studies have shown, she says, that correctly placing punctua- Needs to Know About Cognition.13 I never finished the book
tion is cognitively more difficult and more advanced. because everything seemed obvious. It’s time I took another
look at the book! Also, on the Manning website, there’s a free
9 Lamarr, Ecstasy, page 56. excerpt, “The Act of Writing Code.”14
10 PacificWrecks.com. “Pacific Wrecks - B-25D-20 ‘Tondelayo / Chow
Hound’ Serial Number 41-30669.” Accessed June 9, 2024. https://phpa.
me/pacificwrecks. 12 Ibm:: 360:: Fortran:: GY28-6638-2 OS360 FORTRAN IV G Com-
piler Program Logic Manual 1972-12. https://phpa.me/bitsavers.
11 Hermans, on the first page, grants permission to quote from this
paper. Hermans, Felienne. “Hedy: A Gradual Language for Program- 13 Hermans, Felienne, and Jon Skeet. The Programmer’s Brain: What
ming Education.” In Proceedings of the 2020 ACM Conference on Every Programmer Needs to Know about Cognition. Shelter Island:
International Computing Education Research, 259–70. ICER ’20. New Manning, 2021.
York, NY, USA: Association for Computing Machinery, 2020. https:// 14 “The Act of Writing Code.” Manning (blog), January 26, 2022.
doi.org/10.1145/3372782.3406262. https://phpa.me/act-of-writing-code.
However, that’s not the full answer. It’s clear from that
interview that presenting a woman as a role model is crucial
in this context. Hermans continued:
Additional Reading
I highly recommend Hedy’s Folly: The life and breakthrough
inventions of Hedy Lamarr, the most beautiful woman in the
world by Richard Rhodes. It’s well-researched, and Rhodes
Figure 3. has a great way of connecting different threads into an
understandable picture. Lamarr’s autobiography is quite
enlightening about how things were in the 1930s and 1940s,
particularly as affected by the Hollywood glamor machine.
Backward Compatibility
Chris Tankersly
“WE DO NOT BREAK USERSPACE!”—Linus Torvalds, https://lkml.org
Method/Function Signature Changes The user will have to change their code to pass in the
The most common way to break backward compatibility is Guzzle client and will have to know how to work with the
to change the incoming signature of a method or function or new MyAPIResponse object. The user must make a change to
change its output. Consider the following example: their code, so a MAJOR change has been made.
I would prefer that the client is passed in first, and things like Breaking Backward Compatibility
the $id or other restrictions passed in after that.
You will either want to migrate the old code to the new Lowers Adoption
structure, let’s say a ThirdPartyApi class, and have the old I have quoted him a few times during this article, but I
method call the new wrapper or have the new code call the would highly recommend watching Linus Torvald’s Q&A
old code. With the above code, we could structure our new session from DebConf 2014, specifically his answer on “Why
class to work with the old signature. (See Listing 5) desktop Linux sucks”14. While he is talking about the Linux
We call the new code from the old getUserData() method kernel, much of his rant is valuable to developers at all levels.
WordPress thrives in a world where they stick to a strict
Listing 5.
adherence to backward compatibility. Many developers,
1. use GuzzleHttp\Client; myself included, love to make fun of them for it, but at the
2. end of the day, developers use WordPress not because of the
3. class ThirdPartyApi { good things it does but because people know that the code
4. public function __construct( they write today will last for a long time. That is valuable to
5. private Client $client client shops and contract developers.
6. ){}
7.
Frameworks like Symfony do a great job of providing
8. public function getUserData(int $id) { forward-compatible code for new releases. It is faster moving
9. return json_decode( than WordPress, but Symfony generally does a good job
10. $this->client->request('GET', "/user/{$id}") alerting developers to deprecations, how to handle them, and,
11. ->getBody(), most importantly, leaving deprecations around until a major
12. true release happens.
13. ); I think we could all learn a lot from this idea:
14. }
15. } “We have one rule in the kernel, there is one rule: ‘We don’t
16.
break user-space.’ Everything else is kind of a guideline.”—
17. /**
Linus Torvalds, Debconf 2014
18. * @deprecated
19. */
20. function getUserData(int $id, Client $client): array { Maybe more libraries should take this to heart and make it
21. trigger_error( their number one rule.
22. __FUNCTION__ . ' is deprecated, use ' .
23. 'ThirdPartyApi::getUserData() instead',
24. E_USER_DEPRECATED Chris Tankersley is a husband, father, author,
25. ); speaker, podcast host, and PHP developer.
26. Chris has worked with many different frame-
27. return (new ThirdPartyApi($client)) works and languages throughout his twelve
28. ->getUserData($id); years of programming but spends most of
29. } his day working in PHP and Python. He
is the author of Docker for Developers and
works with companies and developers for
and start to move people to the new code, just like we did integrating containers into their workflows.
when we tightened up the code before. Since we are depre-
cating the entire method, I also added a @deprecated block
so that IDEs will notice the deprecation when doing code
completion. Editors like PHPStorm and VSCode will strike
through the function name and show the deprecation to the
developer while they edit code.
14 https://youtu.be/Pzl1B7nB9Kc?t=172
Now, let’s add this block to our get method: Identifiers Already Registered
$this->set(id: $id); Now, we are going to tackle three more scenarios in which
we already have the identifier registered in our DI Container.
return $this->build($id);
For the fourth scenario, we are going to implement the
And we will also create some helper methods that we need. solution when the registered identifier definition is pointing
to a valid class by adding the following to our GET method.
public function set(
string $id, $definition = $this->definitions[$id];
mixed $value = null, if (
bool $singleton = false is_string($definition->concrete) &&
): void { class_exists($definition->concrete)
$this->remove($id); ) {
if (is_null($value)) { $instance = $this->build($definition->concrete);
$value = $id; if ($definition->singleton) {
} $this->addInstance(id: $id, value: $instance);
}
$this->definitions[$id] =
new ContainerItemDefinition( return $instance;
concrete: $value, singleton: $singleton }
);
} We also need to include our addInstance method:
Listing 3. Listing 4.
1. public function get(string $id): mixed 1. <?php
2. { 2.
3. if ($this->hasInstance($id)) { 3. declare(strict_types=1);
4. return $this->instances[$id]; 4.
5. } 5. namespace SimpleContainer\Container\Exceptions;
6. 6.
7. if (! $this->has($id)) { 7. use Exception;
8. if (! class_exists($id)) { 8. use Psr\Container\ContainerExceptionInterface;
9. throw new NotFoundException( 9.
10. "'{$id}' is not a class name and " . 10. final class ContainerException extends Exception
11. "is not set in the container" 11. implements ContainerExceptionInterface
12. ); 12. {
13. } 13. public function __construct(
14. 14. string $message,
15. $this->set(id: $id); 15. ?Exception $previous = null
16. 16. ){
17. return $this->build($id); 17. parent::__construct(
18. } 18. message: $message, previous: $previous
19. 19. );
20. $definition = $this->definitions[$id]; 20. }
21. if ( 21. }
22. is_string($definition->concrete) &&
23. class_exists($definition->concrete)
24. ){
25. $instance = $this->build($definition->concrete); first thing we need to check in our build method is that we
26. if ($definition->singleton) {
can create a reflection object for the requested class, and if
27. $this->addInstance(id: $id, value: $instance);
not, we should throw an exception.
28. }
29.
private function build(string $id): mixed
30. return $instance; {
31. } try {
32. $reflector = new ReflectionClass($id);
33. if ($definition->concrete instanceof Closure) { } catch (ReflectionException $exception) {
34. $result = call_user_func( throw new ContainerException(
35. $definition->concrete, $this "Failed to create object for '{$id}'",
36. ); $exception
37. if ($definition->singleton) { );
38. $this->addInstance(id: $id, value: $result); }
39. } }
40.
41. return $result; If we succeed in creating the reflection, we should now
42. } check if the class is instantiable. We can do this using a handy
43. method provided by the ReflectionClass class.
44. return $definition->concrete;
45. } if (! $reflector->isInstantiable()) {
throw new ContainerException(
"'{$id}' is not instantiable"
);
}
Implementing the Build Method
The final part of our DI Container is the build method. This At this point, we already know that our class can be instan-
is where we implement the “factory” logic of our container, tiated, so the next thing we need to check is whether the class
so this is the backbone of it. Before we start implementing has a constructor defined. If not, we can already instantiate it
it, we are going to use the second exception that the PSR-11 and return the instance.
provides to us. Inside the Exceptions folder, create a file
named ContainerException.php. (See Listing 4)
Let’s start implementing our build method then. If you read
my last article, “An Introduction to Reflection in PHP,” you
will know that one of the use cases for Reflection is creating
a DI Container, and here’s where we are going to use it. The
$constructor = $reflector->getConstructor(); The second type of parameter that we can resolve by its type
if (is_null($constructor)) { is if the parameter is not a built-in type and if that type (class)
try {
return $reflector->newInstance();
exists.
} catch (ReflectionException $exception) { if (! $parameterType->isBuiltin() &&
throw new ContainerException( class_exists($typeName)
"Failed to create object for '{$id}'", ) {
$exception $classArguments[] = $this->get($typeName);
); continue;
} }
}
If we were not able to get the value of the parameter by its
If the class has a constructor, we should then check each
default value or by its type, we can still check if we have the
parameter for it and get the needed values for each of the
definition for that parameter in our DI Container. So we
parameters, meaning that if our class needs instances of other
check by the parameter name, and if it’s registered in our DI
classes, we should then build instances for these classes to be
Container, we get its value from there.
able to build an instance of our class.
If we’re still not able to get the value for the parameter after
The first thing we are going to check is if the parameter has
all these tries, we will just set the value to null.
no type declared and has no default value; we should throw
After getting the values for all the parameters of the
an exception because we’re not able to resolve the param-
constructor, we will try to create an instance of the class with
eter. And for doing that, we are also going to rely on useful
them and return it. With that, we have the full implementa-
methods provided by the ReflectionParameter class.
tion of our build method. (See Listing 5 on the previous page)
$classArguments = [];
$params = $constructor->getParameters();
foreach ($params as $parameter) {
Conclusion
$parameterType = $parameter->getType(); We did it! We implemented a simple, but fully functional
if (is_null($parameterType) && DI Container from scratch, and we can use this container for
! $parameter->isDefaultValueAvailable()
) { simpler use cases without any issues. Along the way, we also
throw new ContainerException( learned how Dependency Injection and a DI Container help
"Failed to create object for '{$id}'" . us to improve the quality of our applications and how we can
" - Constructor parameter " . intelligently use the Reflection API to help us with our DI
"'{$parameter->getName()}' can't be " . Container.
"resolved");
} If you want to check the full implementation of the container
} with a few extras and also see an example of how to use the
container in an application, check this repository I created for
The second thing we are going to check is if the parameter this article: https://phpa.me/github-WendellAdriel1.
has a default value, let’s use it. Do you want to discuss this article, Dependency Injection,
or any other PHP-related topic? Follow me on X: https://x.
if ($parameter->isDefaultValueAvailable()) {
$classArguments[] = $parameter->getDefaultValue(); com/wendell_adriel
continue;
} Wendell Adriel is a conference speaker, tech-
nical writer, open-source enthusiast, amateur
If the parameter has no default value, we are going to check photographer and cat lover. Currently works
if it has a type defined and try to resolve it with that infor- at TrackStreet as a Software Architect and
mation. However, we are able to resolve only two types of in his free time, contributes and maintains
parameters this way. The first one is if the parameter type is some open-source projects and writes
an array because we can then use an empty array for its value. technical content for his blog. Reach him out
on X at @wendell_adriel
if (! is_null($parameterType)) {
$typeName = $parameterType->getName();
if ($parameterType->isBuiltin() &&
$typeName === 'array'
) {
$classArguments[] = [];
continue;
}
}
1 https://phpa.me/github-WendellAdriel
Listing 1. Listing 2.
1. const PHP = require('php-wasm/PhpWeb').PhpWeb; 1. #!/usr/bin/env node
2. const php = new PHP; 2. import { PhpNode } from 'php-wasm/PhpNode.mjs';
3. 3.
4. // When the engine is ready this event is triggered 4. const php = new PhpNode({
5. php.addEventListener('ready', () => { 5. docroot: '/persist/www',
6. php.run('<?php echo "Hello, world!";'); 6. persist: [
7. }); 7. {
8. 8. mountPath: '/persist',
9. // Handles the script output 9. localPath: './persist'
10. php.addEventListener('output', (event) => { 10. },
11. console.log(event.detail); 11. {
12. }); 12. mountPath: '/config' ,
13. 13. localPath: './config'
14. // Alternative for collecting return Value 14. }
15. php.addEventListener('ready', () => { 15. ]
16. php.run('<?php echo "Hello, world!";') 16. });
17. .then(retVal => { 17.
18. console.log(retVal) 18. // Listen to STDOUT
19. }); 19. php.addEventListener('output', (event) => {
20. }); 20. console.log(event.detail);
21. });
and at first glance, this code seems more complex than neces- 22.
sary.Beyondthat,our PHP “engine” has limited powers and 23. // Listen to STDERR
needs to be processed by JavaScript. The only real gain is 24. php.addEventListener('error', (event) => {
that we do not need PHP to run on the end-user’s computer. 25. console.log(event.detail);
26. });
Much of this exampleis takenfrom Sean Morris’ repository: 27.
https://github.com/seanmorris/php-wasm3.Though I did get 28. await php.run('<?php include("persist/index.php");');
it to function in a Laravel test app on my machine, it was not
fast and felt considerably clunky. (See Listing 1) me with more coding powers and less maintenance, I wish
To me,this ultimately limits the browser-driven use case them all the best in their journey.
to playgrounds and demo spaces for running PHP code via Though I did have a challenging time getting any of these
WebAssembly. systems to run and wascompletelyunable to get the compilers
However, if you wanted to run some PHP-based code on a to process PHP builds as per the documentation, I was able
serverless instance that was running NodeJS, then you could to put together some tiny proofs-of-concept for each of the
utilize something like this: (See Listing 2) leading cases for using PHP in WebAssembly.First, I was able
The above code also uses Sean Morris’ repository, which to process PHP via the browser,and second, I usedNode on a
enables you to load PHP code from an external file and server level, albeitonly on a CLI level.My main takeaway from
have Node process its output.Your PHPcodeis, ofcourse,still all of this was that its use cases are few and far between; what
limited by connectivity and various other quirks, whichI’mnot I mean by this is, of course, I see few use cases where thistype
going to dig into right now, but you can successfully have of deploymentisreally useful.The way in which Sean Morris
Node run PHP if youwantto.However, I diddoa simple was able to initiate a Laravel instance and have it running is
test using file_get_contents to load an RSS feed. Given the pretty neat overall. Still, it will be a while before I consider
sandboxing, it simply spits out an error statement. But, Sean integrating WebAssembly into my coding practices.
does demonstrate running Laravel 11 via Node on his demo
sitehttps://seanmorris.github.io/php-wasm/, which left me Matt has been developing software for over 13
thinking Laravel must have file-writing powers, but instead, it years. He started his career as a PHP developer for
connects to SQLite, and everything is database-driven. Sean a small marketing firm but has since worked with
a few Fortune 500 companies, led a couple teams
also wrote the SQLite driver package. of developers, and is currently a Cloud Architect
PHP CGI on WebAssembly is the larger bet that Wasmeris for a significant Travel technology company.
focusedon as per their documentation and the idea of running He’s contributed to the open-source community
PHP code on any device. Though theideaof running PHP on on projects such as Cordova and Laravel. He
also made numerous packages and has helped
embedded devices does interest me, I cannot help butthink, maintain a few. He’s worked with start-ups and
“Is thisreallythe right technology for this job?”. Thatbeingsaid, sub-teams of big teams within large divisions of
if people want to spend time integrating systems that provide companies. He spends time with his wife and kids
when he’s not tinkering with code or learning new
technologies. @Mattylantz
3 https://github.com/seanmorris/php-wasm
The Spacetraders client is rapidly growing, but I’ve only a mess of legacy spaghetti code from the before times or only
added under half of the API endpoints documented. Since maintain your current application to be concise and readable.
my focus has been on adding functionality, I have been quite In that case, you should adopt PHPStan into your develop-
lax in the structure of my code, which led to the first redesign ment workflow. Doing so on a previous project helped my
discussed last month. We might start by writing our archi- team immensely while we untangled a convoluted WordPress
tectural approaches and other coding standards and sharing site. Instead of copy-pasted procedural code throughout the
them. However, expecting contributors to stick to an archi- theme files, we ended up with encapsulated classes to inte-
tecture or design by decree isn’t a workable solution. Some grate with external APIs, render pages, and configure site
folks will forget or outright ignore the practices you want to features. It didn’t happen overnight, but PHPStan2 was a cata-
establish within a project. Keeping that documentation accu- lyst for our efforts.
rate and up to date also requires investing time and effort. We
can use a number of tools to help us, such as a static analyzer.
What is Static Code Analysis?
Static code analysis examines source code without running
Behold the Argonath, the Pillars of the Kings! ’ cried it. I may be simplifying things, but you can think that it uses
Aragorn. We shall pass them soon. Keep the boats in [nikic/php-parser] and other libraries3 to trace how your
line, and as far apart as you can! Hold the middle of the code handles potential values of expressions, passes argu-
stream!’ ments to functions, uses return values, and more. Although
you can help PHPStan’s analysis with typehints in doc blocks,
… Great power and majesty they still wore, the silent its analysis is based on the source lines it can figure out the
wardens of a long-vanished kingdom. Awe and fear fell actual types used. It can thus help you find many potential
upon Frodo, and he cowered down, shutting his eyes bugs without writing unit tests or getting your application to
and not daring to look up as the boat drew near. Even run on the server. You can catch many logical errors in your
Boromir bowed his head as the boats whirled by frail and implementation by checking PHPStan’s output early (at least
fleeting as little leaves, under the enduring shadow of the before a commit or push). Some useful ones in my experience
sentinels of Númenor. So they passed into the dark chasm include:
of the Gates.
• Missing argument or return type hints
It can be the guardian at your applications’ gates, but you • Type hints in doc blocks that don’t match the function
don’t need to fear what PHPStan reveals. signature or return type
• Returning null values in cases where the return type is
Static Analysis not nullable
• If a method or function returns null, not handling a
Ondrej Mirtes, creator of PHPStan, wrote about Testing
null return in your calling code.
Strategy With the Help of Static Analysis1 in the April 2018
issue of this magazine. It’s a shame it took me almost two • If you’re not using strict mode, finding method calls
years after that to fully embrace it in my work. that don’t pass the correct argument types.
Using typehints has been a running theme throughout
recent articles in this column. Suppose you need to modernize
2 https://phpstan.org
1 https://phpa.me/phparch-stan 3 https://phpa.me/phpstan-composer
For an intricate look at how this works, watch Jan Tvrdík: levels can find bugs that hide in seldom used code paths, calls
PHPStan Under the Hood – phpCE 2018 to undefined functions, doc blocks that don’t match a meth-
od’s arguments, and inconsistent return values. You may have
dozens to hundreds of such errors with any decently sized
application, but fixing these tidies up your codebase and
Getting Started signals that you’re no longer going to tolerate sloppy code.
Add PHPStan to your project’s dev dependencies and then It also helps familiarize your team with running phpstan,
create a phpstan.neon file to hold configuration settings. understanding its output, and fixing the issues it finds.
For my first run, I started at level 2, where I found 7 issues
compser require --dev phpstan/phpstan that I needed to fix ( See Figure 1). These consisted of places
where I had used @var instead of @param for type hinting
Listing 1 shows a starting configuration file for an initial strings in Value classes, another value object I’d forgotten to
run. You can do so much with this file4. At the moment, I’m define altogether, and a child class that was missing the return
asking it to scan my src/ and cli/ directories at level 1. You type required for one of its methods.
should only analyze the code you write. If PHPStan complains
Figure 1.
that it doesn’t know or recognize classes (“symbols”) in one
of your dependencies, use the scanDirectories or scanFiles
sections. If the dependency is popular, there may be a stubs5
package you can use instead (more on this later).
Listing 1.
1. includes:
2. - vendor/phpstan/phpstan/conf/bleedingEdge.neon
3.
4. parameters:
5. # PHP 8.3
6. phpVersion: 80300 I didn’t get more PHPStan errors until I turned it up to level
7. 5, where it complained about method calls that did not have
8. level: 2 the expected types. I had to fix 19 of these, but most were due
9. to one Trait used in a lot of CLI commands to output values
10. paths: to the screen. (See Figure 2)
11. - src
12. - cli
Figure 2.
Listing 3.
1. /**
2. * @template T of Response\Base
3. * @param class-string<T> $responseClass
4. * @return T
5. */
6. protected function convertResponse(
7. \GuzzleHttp\Psr7\Response $response,
8. string $responseClass
9. )
10. {
7 https://phpa.me/phpstan-doc-types
How High? a string that is not listed. Potentially, this catches a bug that
would be difficult to trace in production. (See Listing 5)
I’ve ended with a phpstan.neon field set to scan my applica-
tion at Level 8 with zero errors. At that level, my work was to Listing 5.
ensure my methods gracefully handled null values from user
input. Granted, at the moment, it’s a relatively small codebase, 1. /**
but that’s only one away from PHPStan’s maximum setting. I 2. * @param ISO3166::KEY_ALPHA3|ISO3166::KEY_ALPHA2 $key
can catch bugs earlier by regularly running it and fixing the 3. * @return array{'city':string, 'region':string,
errors it highlights. We also need to practice using its doctype 4. 'country':string}
5. */
extensions to describe method arguments and return values
6. public static function sanitizeCityRegionCountry(
and avoid using mixed as a shortcut for making errors go away. 7. ?string $city,
I might want to jump to level 9 soon since it is limited using 8. ?string $region,
mixed. 9. ?string $country,
10. string $key = ISO3166::KEY_ALPHA3
Array Shapes 11. ): array {
Educating Users On Password Security Best By combining these strategies, PHP developers can create a
Practices robust framework for password security, protecting users and
applications from unauthorized access and potential breaches.
While enforcing strong password policies is crucial,
Implementing strong password policies and educating users
educating users on password security best practices is equally
about best practices are fundamental steps in fortifying the
vital. PHP developers can integrate guidance within the PHP
first line of defense in web application security.
application to help users understand the importance of secure
passwords. This can include advice on using unique pass- Multi-Factor Authentication (mfa) for PHP
words, avoiding common patterns, and regularly updating Security: Elevating the Authentication Citadel
credentials.
Multi-factor authentication (MFA) serves as a formidable
// PHP Password Security Best Practices Guidance defense against unauthorized access by adding an extra layer
echo 'For enhanced security, use a unique password, ' . of verification. It ensures that even if an attacker gains access
'avoid common patterns such as "123456" or ' . to a user’s password, they still need an additional factor to
'"password", and regularly update your' .
breach the account. PHP developers can enable MFA features
'credentials. Consider using a password manager ' .
'to generate and store strong passwords securely.'; within their applications, prompting users to set up and use
additional authentication methods. This guide will demon-
Providing this guidance within the application can help strate how to implement MFA in a PHP application using the
users make informed decisions about their password choices, Google Authenticator library.
further enhancing the overall security of the application.
Setting Up Mfa
Comprehensive Password Policy Implementation To integrate MFA into your PHP application, you first
A comprehensive password policy implementation in a need to generate a secret key for each user and provide a
PHP application should also consider the following best prac- way for them to set up their MFA device, typically via a QR
tices: code. Here’s an example of how to generate a QR code using
1. Preventing Password Reuse: Ensure users cannot the Google Authenticator library and QuickChart API. (See
reuse their previous passwords. This can be achieved Listing 4)
by storing a hash of old passwords and comparing
them when a user tries to change their password. Listing 4.
2. Enforcing Regular Password Changes: Require users 1. // PHP Multi-Factor Authentication (MFA) Setup Example
to change their passwords periodically to reduce the 2. // Include Google Authenticator library
risk of long-term exposure in case of a breach. 3. require 'vendor/autoload.php';
4.
3. Account Lockout Mechanism: Implement an account 5. use PHPGangsta_GoogleAuthenticator;
lockout mechanism after a certain number of failed 6.
login attempts to prevent brute force attacks. 7. $authenticator = new PHPGangsta_GoogleAuthenticator();
4. Multi-Factor Authentication (MFA): Enhance 8. // Generate a unique secret key for the user
security by integrating multi-factor authentication, 9. $secretKey = $authenticator->createSecret();
10. $user = 'User123'; // Example user identifier
requiring users to provide additional verification 11.
besides the password. (See Listing 3) 12. // Generate QR code URL for MFA setup
13. $qrCodeUrl = 'https://quickchart.io/qr?text=' .
Listing 3. 14. urlencode(
15. "otpauth://totp/PHPApp:$user?secret=$secretKey"
1. // Example of Account Lockout Mechanism 16. );
2. 17.
3. // This should be tracked per user and stored in a db 18. echo '<img src="' . $qrCodeUrl .
4. $failedAttempts = 0; 19. '" alt="QR Code for MFA setup">';
5.
20. echo '<p>Scan this QR code with your MFA app.</p>';
6. if ($failedAttempts >= 5) {
7. echo 'Your account has been locked due to ' .
8. 'too many failed login attempts. Please ' .
9. 'try again later or contact support.'; Verifying Mfa During Login
10. // Implement logic to lock account and notify user Once the user has set up MFA, they will enter a time-based
11. } else { one-time password (TOTP) generated by their authenticator
12. // Proceed with authentication and increment app during login. Your PHP application needs to verify this
13. // $failedAttempts if the login fails TOTP against the secret key stored for the user.
14. } (See Listing 5)
Listing 5. Listing 6.
1. // PHP (MFA) Verification Example 1. // Full Example: Setting Up and Verifying MFA
2. // Include Google Authenticator library 2. // Include Google Authenticator library
3. require 'vendor/autoload.php'; 3. require 'vendor/autoload.php';
4. 4.
5. use PHPGangsta_GoogleAuthenticator; 5. use PHPGangsta_GoogleAuthenticator;
6. 6.
7. $authenticator = new PHPGangsta_GoogleAuthenticator(); 7. // Step 1: Generate and Store Secret Key
8. // Retrieve the stored secret key for the user 8. $authenticator = new PHPGangsta_GoogleAuthenticator();
9. $secretKey = 'stored_secret_key_for_user'; 9. // Generate a unique secret key for the user
10. 10. $secretKey = $authenticator->createSecret();
11. // Assume the user has submitted the MFA code 11. $user = 'User123'; // Example user identifier
12. // via a POST request 12. // Store $secretKey securely in the
13. // User-entered MFA code 13. // database associated with $user
14. $enteredCode = $_POST['mfa_code']; 14.
15. 15. // Step 2: Display QR Code for Setup
16. // Verify the entered code 16. $qrCodeUrl = 'https://quickchart.io/qr?text=' .
17. $isCodeValid = $authenticator->verifyCode( 17. urlencode(
18. $secretKey, $enteredCode, 2 18. "otpauth://totp/PHPApp:$user?secret=$secretKey"
19. ); // 2 = 2*30sec clock tolerance 19. );
20. 20. echo '<img src="' . $qrCodeUrl . '"
21. if ($isCodeValid) { 21. alt="QR Code for MFA setup">';
22. // MFA code is correct, proceed with authentication 22. echo '<p>Scan this QR code with your MFA app.</p>';
23. echo 'MFA code is correct. Access granted.'; 23.
24. // ... 24. // Step 3: Verify MFA Code During Login
25. } else { 25. // Assume the user has submitted the MFA code
26. // MFA code is incorrect, deny access 26. // via a POST request
27. echo 'Invalid MFA code. Access denied.'; 27. // User-entered MFA code
28. } 28. $enteredCode = $_POST['mfa_code'];
29.
Comprehensive Mfa Implementation 30. // Retrieve the stored secret key for the user
31. // $secretKey = getStoredSecretKeyForUser($user);
Implementing MFA in your PHP application involves 32.
several key steps to ensure robust security: 33. $isCodeValid = $authenticator->verifyCode(
1. Generate and Store Secret Keys Securely: Each user 34. $secretKey, $enteredCode, 2
35. ); // 2 = 2*30sec clock tolerance
should have a unique secret key generated and stored 36.
securely in the database. Ensure this key is kept confi- 37. if ($isCodeValid) {
dential and protected. 38. // MFA code is correct, proceed with authentication
2. Display QR Code for Setup: Provide users with a QR 39. echo 'MFA code is correct. Access granted.';
code that they can scan using an authenticator app, 40. // ...
such as Google Authenticator, to set up their MFA. 41. } else {
42. // MFA code is incorrect, deny access
3. Verify Codes During Login: During the login 43. echo 'Invalid MFA code. Access denied.';
process, prompt users to enter the TOTP generated 44. }
by their authenticator app and verify it against the
stored secret key. By integrating MFA into your PHP application, you elevate
4. Handle Backup Codes: Offer users backup codes that the security of your authentication process, creating a more
they can use if they lose access to their authenticator formidable defense against unauthorized access. Combining
app. Ensure these codes are also securely generated MFA with strong password policies and user education
and stored. ensures a robust, multi-layered security approach.
5. Educate Users: Provide clear instructions on how to Role-Based Access Control (rbac) to Restrict
set up and use MFA, highlighting its importance for
Unauthorized PHP Access: Crafting Digital
enhancing account security.
Permissions
6. Graceful Error Handling: Implement graceful error
handling and user feedback to guide users through Role-Based Access Control (RBAC) empowers PHP devel-
the MFA process, especially when codes are incorrect opers to design granular access control models within their
or expired. (See Listing 6) applications, ensuring users have access only to the resources
necessary for their roles. By defining roles and permissions,
Listing 13.
1. // Example: Updating permissions for a promoted user
2. function promote_user_to_manager($username) {
3. // Update user role and permissions in the db
4. echo "Promoting user '$username' to manager...\n";
5. }
6.
7. // Assume a user 'alice' is promoted to a manager role
8. $username = 'alice';
9. promote_user_to_manager($username);
10. update_permissions_based_on_changes();