For the vast majority of my professional career, I’ve been working with mostly just PHP, and felt pretty confident with it. However, the longer I worked with just PHP, the more afraid I became of branching out into new things. I watched as the backend ecosystem started changing – it seemed like every one was picking up Node or Go or Ruby – but what if I couldn’t learn those? What if my skills didn’t transfer? What if I wasn’t as good as I am at PHP – or even worse, good enough to even get anything done?
Last fall, my team was tasked with building a new service, and a coworker suggested it was a great opportunity to create a serverless microservice using Node JS. Over the course of a few months I had to throw myself into learning the AWS ecosystem, functional programming, learn Serverless, start writing JS – and I realized I’d been holding myself back for no reason.
That project started me down a path of learning TONS of new technologies – and after 20 years of writing software, I finally got over my reluctance to learn how any of the hardware side worked – which was based in the same fear of not being good enough. I bought a kit and started learning about microcontrollers and circuits. Powering an LED for the first time and controlling which LED light up with a simple toggle switch was as rewarding as those first “Hello World” scripts. I jumped into python and C++, built IoT devices for my home by sending signals over WiFi and long range radio, learned how to solder – and then taught my 8 year old daughter how to!
I asked my daughter to come up with an idea for a project for us to build together, and with her (admittedly vague) product requirements, we started working on her “cube shaped light controlled by drawing on an iPad app”. I created a React app for the frontend, which not only needed to support touch events so it would work on the iPad, but even multi-touch for better interactions. The backend is functional JS running on a lambda, which communicates with a third-party service to push events to the microcontroller – which is running python to control a bunch of individually addressable LEDs. The final step was designing a case for the whole thing, which went through a bunch of iterations where we tried to use acrylic cut on the CNC machine, before I finally caved and bought a 3D printer.
If you had told me a year ago that by this time next year, I’d be comfortable with any ONE of these ideas: writing python, react – or any JavaScript – working in AWS, building physical circuits from scratch, designing 3D models – I’d have laughed and said you had the wrong person.
I wish I hadn’t held myself back for so long. Don’t be afraid to step outside of your comfort zone, the discomfort is where we grow. There is an entire world of communities just like our PHP community, out there waiting for more newbies.
Knitting is computing. Bear with me, I’ll explain.
Computing is using certain hardware, following a set of instructions to manipulate input and produce a desired result.
Knitting is using certain hardware, following a set of instructions to manipulate input and produce a desired result.
See, it’s the same! Knitting ‘hardware’ is needles, our ‘input’ is yarn, and our result isβ¦ a scarf, perhaps.
Knitting patterns are exactly like computer programs – they’re a set of instructions. Yes, that would make knitting pattern writers programmers.
As a woman in tech, I’ve witnessed many surprised faces belonging to men who have just learned that I am a programmer. None have ever expressed surprise that I am able to knit!
Unravelling the Mystery
Let’s take a look at a much simpler knitting pattern that will create a slightly textured fabric.
worked over even number of sts
row 1: [yo, k2tog] rep
row 2 & 4: k all sts
row 3: [ssk, yo] rep
rep rows 1-4 for pattern
This pattern consists entirely of familiar concepts; variables, conditionals, iterators, and functions.
Let’s look at the variables. These are sts (stitches) and row (row). Our knitting needles holds an array of stitches which are knit over a number of rows. So, after we knit (process) each of our sts in turn, our row increments.
Our pattern contains conditionals. Which row we are on determines which instructions we follow. If row == 1, we [yo, k2tog] rep, if row in (2, 4), we k all sts, and if row == 3 we [ssk, yo] rep.
Each of the instructions for the rows ends with rep, our iterator. rep means to repeat the previous instruction. In this case, we continue to repeat the instruction, processing stitches, until we complete the row.
We also have functions. These are the actual yarn manipulations – we see them in the pattern as yo, k2tog, k, and ssk. Each of these is a different way to process a stitch in a row. As a knitter, you must learn how to perform each manipulation.
yo is a manipulation that increases the number of sts in a row by 1. k2tog and ssk manipulate two stitches at the same time, decreasing the number of sts in a row by 1, and k means to knit – leaving the number of sts as it is.
Examining the pattern using this knowledge, we can determine that given any even value for sts, sts.length will remain unchanged at the end of each row. If we were to keep repeating rows 1-4, we would end up with a rectangular piece of fabric which would get longer with each iteration. That’s our scarf!
A History Intertwined
Computing and textile manufacture have a shared history. Back in the 18th century, the textile industry was undergoing a revolution. A silk fabric inspector, Jacques de Vaucanson, turned his efforts to mechanising the cloth-making processes. He succeeded in creating a water-powered automatic loom. Unfortunately, this invention was largely ignored for several years – until it was picked up by Joseph-Marie Jacquard. Jacquard improved upon this automatic loom and patented the invention in 1804.
This Jacuqard machine could be attached to a loom, and, given some input, could create woven fabric according to any given pattern. This was a breakthrough in textile manufacture. Due to the way in which it employed punched cards, exceedingly complex patterns could be fed to the machine and easily re-used.
You may know that early computers also used punched cards for reusable input. This came directly as a result of the mechanisation of textile manufacture. Charles Babbage was heavily inspired by Jacquard, and used much of the same technology when he designed his Analytical Engine – the world’s first computer.
Punched cards are very familiar in computing as they represent binary data – either the hole is open, or it is closed. 1 or 0. This fact defined human-machine interaction. Ada Lovelace–the world’s first programmer and accomplished mathematician, who just so happened to be a woman–surmised that these punched cards could represent not just numbers and mathematical operations, but arbitrary data.
We may say most aptly that the Analytical Engine weaves algebraical patterns just as the Jacquard loom weaves flowers and leaves.
– Ada Lovelace
Weaving Things Together
There is not currently any one ‘universal’ notation for knitting patterns. Even different cultures have differing names for stitches; for example, an English double crochet stitch is an American single crochet stitch. There are also different ways to signify the size of needle required for a pattern, and different names for the thickness of yarn.
When we write our code, we usually use a specific language which is formally defined. This ensures that our code can be executed, and understood by other engineers. Could a ‘universal language’ be developed for knitting, and could we write tools to parse and validate our written patterns, and identify mistakes?
Each kitting pattern is generally written to be performed on specified hardware. Could we develop tools that allow knitting patterns to be transcompiled for use with differing hardware? For example – increasing or decreasing the number of rows or stitches to achieve a desired size, given any combination of needles and yarn.
Furthermore, is it possible that our knitting language could actually be used as a general purpose language for performing computations? In order for this to be true, our knitting language must be Turing Complete. In her Masters thesis “Algorithmic Complexity in Textile Patterns”, Heidi Metzler investigates whether or not knitting can be used to simulate a universal Turing machine. If it is possible, then knitting patterns must be Turing Complete!
Casting Off
In conclusion, knitting and computing are more similar than you might think! I hope that this article has given you food for thought, and perhaps some new perspective. If it weren’t for the textile industry and the contributions of women, perhaps computational theory wouldn’t be where it is today.
Lately, I’ve been working on migrating Doctrine ORM to PHP 8 syntax. To that end, I’ve been using Rector, an automated refactoring tool. It comes with a set of rules called LevelSetList::UP_TO_PHP_81 which makes sure you use the most modern syntax to do something as long as it is supported on PHP 8.1. UP_TO_PHP_81 is equivalent to UP_TO_PHP_80 + PHP-8.1-specific rules. UP_TO_PHP_80 is equivalent to UP_TO_PHP_74 + PHP-8.0-specific rules. UP_TO_PHP_74 is equivalent toβ¦ you get the idea, and maybe also marvel at how satisfying this looks. π€
I’m doing that work on the 3.0.x branch because we don’t currently plan to drop support for PHP 7.1 on the 2.* branches. While I’m at it, I’m taking this as an opportunity to add type declarations where not already present. Adding type declarations is very often a breaking change, especially when working on methods that are not private, and classes that are not final,which explains why that was not already done everywhere on lower branches.
Thankfully, we have plenty of phpdoc comments that Rector can use to infer the correct type declarations to add. Here is how such changes might look:
- /**
- * @param string $foo
- *
- * @return int
- */
- public function doStuff($foo);
+ public function doStuff(string $foo): int;
doctrine/orm is a lot of code though, so I’m trying to work in bite-sized pull requests. First, because it would be awful to review all these changes at once, but also because the phpdoc we have can be imprecise, or plain wrong (we still have to work through our PHPStan and Psalm baselines). Having inaccurate phpdoc might be fine from PHPUnit‘s point of view, but having inaccurate type declarations isn’t, so I need to fix these by hand afterwards.
I like to repeat that we’ve never been closer to releasing doctrine/orm 3.0 than we are today, an information that you can share widely because it’s always true. While that’s a fact you can hardly deny, it is still good to backport any of the fixes and improvements to the 2.x series: it makes difference between branches smaller, which in turn makes merging up from 2.x to 3.x easier, but also lets the users benefit from those fixes and improvements earlier.
Usually, what I do is pick a class or namespace, apply Rector on it, then review the changes. If I spot phpdoc that is wrong, I fix that on the patch branch (currently 2.13.x). If I spot phpdoc that is correct but a bit vague, I make it more precise on the minor branch (currently 2.14.x). After the PR is merged, I merge 2.13.x up into 2.14.x, and 2.14.x up in 3.0.x, and I try running Rector again, this time with correct phpdoc.
That migration is a good use case for the git subcommand I want to introduce to you today, because I need to change branches often. It’s already not unusal to have to do so when maintaining a library, but it’s exacerbated here. Sorry for the Tom Jones earworm.
In this particular case, here is the problem I am facing: imagine you have 10 fixes or improvements to backport from one branch to the others, and that you discover them progressively. How would you proceed? Would you stash changes, switch branches, run composer update for good measure, make your change, commit, then switch back every time? Or would you maybe try to remember several things you need to do, and try to do them all at once? Either solution sounds pretty bad.
The subcommand that can save you from this is git worktree. It allows you to have β¨severalβ¨ worktrees at once for a single repository.
Creating a throwaway worktree with branch 2.14.x can be done like so:
$ git worktree add /tmp/throwaway 2.14.x
The operation is instant, and in the case of doctrine/orm, there are only 2 steps to be ready to work on that new branch:
$ cd /tmp/throwaway
$ composer update
But I do not want to create throwaway worktrees⦠I find the idea of having permanent worktrees very appealing: they are like starting points for new branches I want to create. Each one has its own vendor directory, with the right dependencies.
Also, I prefer to have them neatly grouped in a single directory. I could have a normal repository, and then add worktrees inside, but then git would consider the worktrees themselves as new directories that need to be put under version control. To avoid having that main worktree, you can use a bare repository:
You will end up with a directory called doctrine-orm.git, and the contents of that directory will be what you usually find in the .git directory If you use git log, you will see the history of the default branch, which the current HEAD points to (2.13.x in our case).
Doctrine uses a consistent branching model on all of its repositories:
π bugfixes go to the patch branch;
π‘ new features, deprecations, improvement go to the minor branch;
π₯ breaking changes go to the major branch.
At first, I named directories after branches, but when 2.12.x went unmaintained, I no longer had a use for the corresponding directory, and realized I should have one directory per branch type instead. Here is how to create that workforest π³π³π³:
After that, you should end up with something like this
doctrine-orm
βββ major # a full 3.0.x doctrine/orm is inside π₯
βββ minor # a full 2.14.x doctrine/orm is inside π‘
βββ patch # a full 2.13.x doctrine/orm is inside π
doctrine-orm.git # Looks just like a regular .git directory
βββ config
βββ HEAD
βββ hooks
βββ objects
βββ packed-refs
βββ refs
βββ worktrees # contains administrative files for your worktrees
Note that I could have created the worktrees directly inside doctrine-orm.git, but I don’t want to, I find that messy.
When inside doctrine-orm/*, git still knows where the repository is stored thanks to a .gitfile in each worktree. Yes, in this case it’s just a one-line file, with a pointer to a directory that holds administrative data for that worktree.
When trying this at first, it broke custom git hooks I had. That’s because when using worktrees, Git will store the administrative data that is common to all three worktrees in the usual directory, but will put worktree-specific administrative data in another directory (here: /path/to/doctrine-orm.git/worktrees/minor). Making the distinction between the git directory specific to a worktree and the git directory shared by all worktrees helped me fix my hooks. It is possible to figure them out from inside a worktree:
When directly inside doctrine-orm, nothing special happens, and you will get the usual error message when trying to issue a git command.
$ git status
fatal: not a git repository (or any parent up to mount point /)
Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).
When inside a worktree, you can know the status of all worktrees:
Switching branches becomes as easy as switching directories. No need to commit or stash anything.
I do not need to run composer update all the time when switching from one worktree to another.
If I want to, I can even compare and edit the same file on two different branches at once. π€―
I like that git has such hidden gems, like git worktree or git bisect, that are little known and rarely needed, but are still killer features when you do need them.
In 2016 I decided that I didn’t want to be an IT-coordinator anymore. At the time I couldn’t deal with the politics involved in that function. You know, co-workers protesting the changes that you have to make happen and stuff like that. After some talks with job – and career – advisors I decided to become a developer. Probably a front-end one.
Thanks to one of the advisors I was offered a position at a non-profit that enables people with physical and mental disabilities to find and keep a job. That company wanted to make use of my front-end and webmaster experience and offered me a traineeship for back-end development. Yet, the problem was, they wanted me to learn it the auto-didactic way. The way that my direct co-worker and so many others in the industry followed. It didnΒ΄t seem as a problem at first, mind you. I already considered myself the prototype of a self-educated employee. And besides: how difficult could PHP be? You know, being a scripting language invented to cough up HTML? That much I already knew. So – believe it or not – they gave me the book Head First PHP & MySQL (O’Reilly, 2008) and after finishing asked me to help maintain the Document Management System and build an Intranet-side based on WordPress. They must have thought I was ready.
How little did we all knew? It took me three years to feel comfortable with my role. If I wasnΒ΄t so determined to become a developer, I would have quitted already. Meanwhile, the patience of the product owner fluctuated. That didn’t help either. As a matter of fact, we had a good talk only yesterday (2022) and he still took the opportunity to express his concerns about my productivity. Totally overlooking the fact that I not only introduced and implemented such an essential process as lifecycle management through Git but also helped secure the ISO-27001 audit by dressing up a tree of security policies for the DMS. On days I spend more time on processes than on coding.
It’s been six years ago that I started to become a PHP developer. I still have a lot to learn. If you find yourself in a similar position, I can assure you that there’s not much time to learn new things when production goals are set. Unless perhaps you’re nineteen, started programming twenty years ago, arenΒ΄t responsible for any other livelihood, and can work 24 hours a day. (not complaining about my own circumstances here though.)
What all this learning and determination thought me, besides knowing the perks of something being null, is that most tutorials and books can teach you programming to a random extent but they don’t teach you to be a developer. On hind sight I have to admit that to become a professional developer, it’s maybe better to get proper training and education. Because business demands more skills than logic and tooling. If you’re one of those dreaded full-stack developers like I am, or even ‘just’ a back- or front-end developer, there’s so much more to learn than code. And good tutors know this.
I was lucky to be determined and be able to compensate for my lack of knowledge and experience in PHP programming with the knowledge and experience that I gained in other jobs over the years. But one isn’t always that lucky.
Let’s set aside all practical concerns for a moment β it’s Christmas, after all. If you could choose β freely choose: what would you change about PHP?
Would you want generics or the pipe operator? Maybe you’d like to see consistent function signatures or get rid of the dollar sign. Type aliases, scalar objects, autoloading for namespaced functions, improved performance, less breaking changes, more breaking changes β the list goes on.
But what if I told you, you had to pick one, and only one. What would it be?
My number one feature isn’t in this list. Even worse: my number one wish for PHP will probably never happen. But like I said at the beginning: let’s set aside all practical concerns. Let’s dream for a moment, not because we believe all dreams come true; but because dreams, in themselves, are valuable and give hope β it is Christmas, after all.
Let me tell you the story of another programming language. A language that, just like PHP, had been the underdog for decades. It didn’t have sexy syntax, it was a poor performer, it had messy methods and confusing classes. It wasβ¦ JavaScript.
It was a language that β legend says β was written in two weeks. Yet it grew to be the most popular programming language people had ever seen, almost by accident.
Indeed, for years, developers were stuck with JavaScript: it was the only option to do any kind of frontend programming at all. And despite everyone mocking poor JavaScript, there simply were no other alternatives, so they had to use it.
That is: until a bunch of smart people started thinking out of the box. The idea was simple: what if we don’t write JavaScript anymore, but instead write a program in another language, and convert that code to JavaScript? That way our programs could still work in the browser, but we didn’t have to put up with JavaScript’s ugliness and quirkiness.
And so history was written: we compiled languages like C to a subset of JavaScript β an optimised subset that was much more performant. It was called asm.js. It allowed us to run existing game engines in the browser. We compiled JavaScript to JavaScript with Babel: newer syntax that wasn’t available in browsers was “transpiled” to older JavaScript versions. Supersets like CoffeeScript came into existence. They added better and more convenient syntax.
More and more, the JavaScript community transformed into a host of languages, with JavaScript simply being a compilation target.
Sure, compiling JavaScript meant adding a build step, but it seemed like developers got used to it. First and foremost: build steps were optimised for performance, and second: you could suddenly add a lot of static functionality to your language: tasks that were performed only when compiling your program but not while running it. TypeScript was created, and static type checking became a thing. It turned out: computers were awfully good at detecting mistakes, as long as we provide them with just enough information β types.
And everyone marvelled at JavaScript: it grew from a small frontend scripting language to the number 1 programming language in the world.
Let’s talk about PHP. I’ve been writing it for more than a decade now. I love the PHP community, I love the ecosystem, I love how the language has managed to evolve over the years. At the same time I believe there’s room for PHP to grow β lots of room. And so I dream.
I dream of a world where PHP follows in JavaScript footsteps. Where it becomes more than just PHP. I dream of a TypeScript for PHP: a language that’s still 100% compatible with all PHP code out there, but one that adds generics and proper static type checking β without having to worry about runtime performance costs. I dream of a language that has all (or most) modern-day language features, that are compiled to plain, old, boring β but working β PHP.
But dreams seldom come true.
Someone once said: “For JavaScript to become as popular as it is today, there had to be two things: it had to suck, and it had to be the only viable option available”. And here’s the thing: unlike JavaScript, PHP isn’t the only option available for backend programming. I also dare to say that it’s way better than JavaScript back in the day. These two main components that were needed for JavaScript to grow into something more than itself, aren’t equally present in PHP.
And that’s ok. It means my dream is probably unrealistic, but it also means something much more important.
My realisation recently is that PHP is already awesome. People are already building great things with it. Sure, maybe PHP is boring compared to the latest and greatest programming languages, and sure you might need to use another language if you’re building something for those 0.1% of edge cases that need insane performance. My dream of a superset of PHP might be one of many approaches, but it sure isn’t the only viable path forward.
Even without that dream of mine: PHP is doing great. It’s not because of how its designed, it’s not because of its syntax. It’s not because of its top-notch performance and it’s not because of its incredible type system. It’s because people like you are building amazing things with it. Whether you’re using PHP 8.2 or not; whether your running it serverless or asynchronous or not; whether you’re writing OOP, FP, DDD, ES, CQRS, Serverless or whatever term you want to throw at it β you are building awesome things. It turns out a language is rarely the bottleneck, because it’s merely a tool. But “PHP” is so much more than just a language, it’s so much more than just a tool. That’s because of you.
Thank you for being part of what makes PHP great. Happy holidays.