Categorias
Tropeçando

Tropeçando 113

Neon

Serverless PostgreSQL database with real zero-scaling. The fully managed serverless Postgres with a generous free tier. We separate storage and compute to offer autoscaling, branching, and bottomless storage.

Compute scales dynamically to ensure you're ready for peak hours. Compute scales to zero and cold storage offloads to S3 for cost efficiency. Create a fully managed serverless Postgres instance in seconds.

Make your app faster with PHP 8.3

PHP 8.3 is the latest version of PHP. It has exciting new features and major improvements in performance. By upgrading to 8.3, you can achieve a significant increase in speed. In this article, we dive into how PHP 8.3 can be a game changer. It can speed up your application's performance.

OWASP Top 10 Explained: SQL Injection

SQL Injection (SQLi) is a code injection technique that exploits a security vulnerability occurring in the database layer of an application.

The vulnerability is present when user inputs are either improperly filtered for string literal escape characters embedded in SQL statements or user input is not strongly typed and thereby unexpectedly executed.

This allows an attacker to manipulate SQL queries, enabling them to unauthorized access, modify, and delete data in the database. This can lead to significant breaches of confidentiality, integrity, and availability, ranging from unauthorized viewing of data to complete database compromise.

15 Quick Useful Tips for AWS CDK Engineers

In this short article, we will cover 15 useful tips with accompanying code snippets for AWS CDK users.

Implementing DTOs, Mappers & the Repository Pattern using the Sequelize ORM [with Examples] - DDD w/ TypeScript

There are several patterns that we can utilize in order to handle data access concerns in Domain-Driven Design. In this article, we talk about the role of DTOs, repositories & data mappers in DDD.

Categorias
Technology

Notes – ServerlessDays NZ 2024

Those are my notes for ServelessDays NZ - Auckland, at 24th May 2024.

Sheen Brisals - Think, Architect, and Build Serverless Applications as Set Pieces

During ServerlessDaysNZ Sheen Brisals gave the talk Think, Architect, Build, Sustain Serverless Application Set Pieces. It was full of important insights to Set Pieces and sustain Serverless Applications.

I particularly liked how he touched on the fact that legacy applications being rewritten to Serverless is a thing, as this is everywhere being part of lots of engineers' lives.

More than that, Brisals highlighted how patterns and pivotal for a maintainable and reliable application, despite the execution model:

  • Identify Domains so you can decouple a domain to rewrite it more effectively
  • Complexity is better abstracted, becoming simpler, when you know and apply good proven Patterns -- the exception is to invent a new one
  • Design Patterns, Architecture Patterns, Execution Model patterns, Software Design, etc, will improve the quality of your Application. As Serverless will likely push you to learn them, you have the opportunity to develop as an Architect
  • The Serverless should help you to think in the whole picture, as the settled pieces need communication between them, therefore optimising value to the end-user

Unfortunately, I was not selected to win the book Serverless Development on AWS, but for those who won, I wish they could learn a lot there. What a great indication of how good a fellow is Sheen. Giving away those books is a gigantic contribution to the community!

I am very pleased to know you in person, Sheen.

This presentation talked a lot with Michael Walmsley's. So nice.

Heitor Lessa - Let Them Retry: Idempotency for the Rest of Us

Despite being common to talk or to assess if a given application or infrastructure follows best practices and great architectural patterns, implementing this is a challenge for development teams for different reasons.

Heitor Lessa, in his talk "Let Them Retry: Idempotency for the Rest of Us", demonstrates how a tool that improves the Developer Experience bringing the implementation of the patterns close to the code is powerful to win adoption. PowerTools is a developer toolkit to accelerate development providing interfaces and abstractions to implement Serverless best practices.

Heitor used a sample code, emulating an existent codebase, from an application already working in Production. We had the opportunity to see the appeal of PowerTools. Usually, Idempotency (to handle duplicated transactions) is associated with a good amount of change in the code. Still, PowerTools was designed to introduce no or very few impacts to a code that is very dangerous to change. As building blocks, adding more complex functionalities, such as caching, payload tempering and failure mode.

The existence of tools like PowerTools reinforces how implementing good and proven software (and architectural) patterns is pivotal for a scalable and reliable application. The Serverless execution mode can mislead to relaxed code, but that would weaken the performance and stability of an application. The lesson is that working smarter is applying known solutions for specific problems.

PowerTools provides a wide range of functionalities, not surprisingly being able to match Well-Architected frameworks in their implementation: Secrets/System Manager Parameters, Event Source Data Classes, Validation, Feature Flag, Idempotency, Data Masking, Streaming, Middleware, JMESPath, Batch processing, Metrics, Tracing. We avoid writing boilerplates, repeated code and even the need to create a shared lib of constructs ourselves. The community is improving it.

PowerTools is a helpful tool to implement these features. This is an opportunity to learn and deep dive into best practices and designs. It also enhances how you observe and monitor your application. It is a serious tool to consider if you intend to leverage how your code is executed, deployed, monitored and performed.

In his talk, Heitor implemented, live in the meeting, Idempotency into a legacy code. He enriched it with failure modes, caching, payload tampering and order tolerance. So, PowerTools is also very easy and quick to use.

Best practices for everyone

  • Heitor Lessa

Michael Walmsley - Unleashing Serverless Scalability on AWS: Practical Strategies and Proven Patterns

Some started Michael Walmsley introduction saying "A fantastic human being...". And I will start from there as well because I have experienced that myself.

I bumped into Michael while walking to the conference venue. I first heard about it from a great friend, Joshua Katz, who was impressed with Michael. It was a very pleasant walk while sharing quick impressions of being AWS Community Builders and excitement about the conference.

It happens that Michael is now an AWS Hero with many years of experience to share. One of the first things he said in his talk was replaying Suzana Melo Moraes (you should listen to this girl - so inspiring), who has three years in tech, when she was saying that, mostly every day, she struggles with something usually starting from having no idea how to fix a particular problem she was assigned to solve. Michael sympathised, saying that, even after 30 years, there are days that things happen to him the same way. This happens in everyone involved in this field and it was so humbling coming from him.

As usual, Michael doesn't keep secrets by himself but shares insightful tips. His presentation was about Unleashing Serverless Scalability on AWS:

  • Start the design with the needed scalability in mind (can you see that links to Sheen Brassals talk?)
  • Master and understand well the limits, they are there for a reason and as early you design your application to work with them, better design your application and scalable-ready it is
  • Events, Messages, and Commands are the way of communication for Serverless and a must-know subject
  • Do not ignore Flow Control
  • Break your application limits before someone else does -- use performance tests in your favour
  • Study and use proven patterns (check https://serverlessland.com)

Brad Jacques - Delivering at pace while evolving a Serverless architecture

Brad Jacques delivered a talk titled "Delivering at pace while evolving a Serverless architecture" at ServerlessDays NZ. Brad covered a challenging project where file manipulation use cases were an important feature.

"Complexity is everywhere". Brad could not help it advise that a successful delivery starts from breaking the complexity into pieces, to plan ahead of time and to do the simple things first. He mentioned that the deadline was short, affirming it was the right strategy to evolve the architecture.

He also stressed the use of established patterns for success, such as breaking down complexity, identifying domains and context boundaries, and understanding limits and messaging.

It was also important how the work was planned with the team. Having a small committed team, fast feedback loops and continuous measurement were key to proving the solution was correct.

The summary is so great that I will copy it here entirely:

  • Do the simple thing first
  • Small teams with a fast feedback loop (showcase often)
  • Identify risk early, shift left, and spike
  • Continuously measure performance, and stress test
  • Isolate context boundaries
  • The solution must prove itself correct

Brad's insights were based on his experience with a new project for a major client at a consultancy company. However, it was clear that the principles and strategies he shared apply to any application, in any industry, and of any size.

His parting advice was to "evolve your architecture, measure, and make decisions throughout the process."

Categorias
Tropeçando

Tropeçando 111

Don't do this: creating useless indexes

This is why, when I’m called for a performance problem (or for an audit), my first take is to look at the size of the data compared to the size of the indexes. If you store more indexes than data for a transactional workload, that’s bad. The worst I’ve seen was a database with 12 times more indexes stored on disk than data! Of course, it was a transactional workload… Would you buy a cooking book with 10 pages of recipes and 120 pages of indexes at the end of the book?

The problem with indexes is that each time you write (insert, update, delete), you will have to write to the indexes too! That can become very costly in resources and time.

PXP

PXP is a superset of the PHP programming language. It is heavily inspired by the goals of the TypeScript project and aims to improve and enhance PHP with transpilation.

Functional Classes

A place for everything, and everything in its place.

What is a class? According to the dictionary a class is:

A set, collection, group, or configuration containing members regarded as having certain attributes or traits in common; a kind or category.

The Simple Class

I work in many legacy code bases, and in fact, I’ve made it a big part of my career. I love diving into big monoliths that have grown out of proportion and tidying them up. One of the best parts of that work is rewriting a God class into a collection of small reusable classes. Let’s take a look at what makes a simple class great.

The economics of clean code

Code smarter. Code balanced. That is OK to have some debt. But pay them off quickly.

Categorias
Tropeçando

Tropeçando 110

Enabling the Optimal Serverless Platform Team — CDK and Team Topologies

Serverless, and related technologies, have enabled teams to move faster, reduce total cost of ownership and overall empowered developers to have greater ownership of the systems they build. However, Serverless is not a silver bullet — there is an organisational side that’s key to unlock the full benefits of Cloud.

Restructuring a Laravel Controller using Services, Events, Jobs, Actions, and more

A simple but nice walk-though about code decoupling.

The Serverless Server

I'm Will Jordan, and I work on SRE at Fly.io. We transmogrify Docker containers into lightweight micro-VMs and run them on our own hardware in racks around the world, so your apps can run close to your users. Check it out—your app can be up and running in minutes. This is a post about how services like ours are structured, and, in particular, what the term "serverless" has come to mean to me.

Keep Cognitive Complexity Low with PHPStan

What is cognitive complexity? It's the amount of information we have to hold in our heads simultaneously to understand the code. The more indents, continue, break, nested foreach, and if/else branches, the harder is code to read.

You can use PHPStan rules to decrease the cognitive complexity of your codebase. This brings matuiry to your application and a more maintainable code.

How to release PHP 8.1 and 7.2 package in the Same Repository

Some steps to release a package in more than one version, to allow compatibility for different PHP runtimes.

Categorias
Tropeçando

Tropeçando 109

How to Measure Your Type Coverage

Type coverage check for PHP with PHPStan.

Event Sourcing in Laravel

Granular interfaces

After refactoring to a granular interface, our system became more flexible and composable. Small interfaces communicate intent more clearly, making it easier to understand the flow of a system.

Serverless Laravel applications with AWS Lambda and PlanetScale

The Tighten Test: 12 Steps to a Better Team

Working in a good team turn your life entirely different. Tighten published this post with their heavily opinionated, based on their shared values, and sourced from their experience as web and app developers who regularly work with a variety of different software organizations. This list is based on Joel's 12 steps for better code.

Categorias
Tropeçando

Tropeçando 108

Why I Will Never Use Alpine Linux Ever Again

Alpine image is heavily use as a base image for all sort of applications. Some applications, usually running in Kubernetes, are facing issues due to Alpine implementation of musl. This article describes how those issues can cause a great amount of grief.

3 years of lift-and-shift into AWS Lambda

Let’s set the scene. We’re looking for scaling a PHP application. Googling around take us to find out that AWS Lambda is the most scalable service out there. It doesn’t support PHP natively, but we got https://bref.sh. Not only that, we also have Serverless Visually Explained which walk us through what we need to know to get PHP up and running on AWS Lambda. But we have a 8 year old project that was not designed from the ground up to be serverless. It’s not legacy. Not really. It works well, has some decent test coverage, a handful of engineers working on it and it’s been a success so far. It just has not been designed for horizontal scaling. What now?

Different beliefs about software quality

Good advices on how to deal with an environment where you have conflicts about your beliefs and how the environment work.

Increase code coverage successively

I often come across legacy projects that have a very low code coverage (or none at all). Getting such a project up to a high code coverage can be very frustrating as you will have a poor code coverage for a very long time.

So instead of generating an overall code coverage report with every pull request I tend to create a so called patch coverage report that checks how much of the patch is actually covered by tests.

Conway's Law

Pretty much all the practitioners I favor in Software Architecture are deeply suspicious of any kind of general law in the field. Good software architecture is very context-specific, analyzing trade-offs that resolve differently across a wide range of environments. But if there is one thing they all agree on, it's the importance and power of Conway's Law. Important enough to affect every system I've come across, and powerful enough that you're doomed to defeat if you try to fight it.

Is it a DTO or a Value Object?

A common misunderstanding in my workshops (well, whose fault is it then? ;)), is about the distinction between a DTO and a value object. And so I've been looking for a way to categorize these objects without mistake.

Categorias
Tropeçando

Tropeçando 107

Why Writing Is Important for Engineers

Learning by teaching has been an important pedagogical approach for a long time. As engineers, most of us are probably familiar with the story of the rubber duck, made popular in The Pragmatic Programmer, where explaining your problem to an inanimate object helps you understand it better yourself.

A Comprehensive Guide to Undoing Changes In Git

Git is a powerful version control system for tracking source code changes, for small and large projects alike. Sometimes you’ll encounter situations that require you to undo changes you’ve made to a Git repository.

Git changes cannot be undone with a simple back button. This is intended to protect the integrity of the codebase. Instead, you’ll need to learn the proper Git commands and the appropriate situations for using each command.

How the PHP Middleware Pattern works and can easily be applied

In this post we'll be looking at Middleware in PHP. This pattern is most common in the handling of requests and responses. But the Middleware Pattern can also be applied in various other places. We'll look into what middleware is, how middleware works, when middleware can be useful and what an alternative to middleware might be.

Tuning PostgreSQL Auto-vacuum

In PostgreSQL, rows that are deleted or modified are not completely removed. Rather they are marked as dead tuples. In other words, PostgreSQL does not physically remove these rows but places a marker on them to prevent future queries from returning or accessing dead tuples.

PostgreSQL provides the VACUUM command and VACUUM ANALYZE commands to get rid of dead rows or tuples. However, both commands differ in how they operate.

Learn Vim Progressively

tl;dr: You want to teach yourself vim (the best text editor known to human kind) in the fastest way possible. This is my way of doing it. You start by learning the minimal to survive, then you integrate all the tricks slowly.

Vim the Six Billion Dollar editor

Better, Stronger, Faster.

Categorias
Tropeçando

Tropeçando 105

CQRS and Event Sourcing implementation in PHP

A walk-through of using CQRS along with Event Sourcering using PHP.

Is my autovacuum configured properly?

Some tips to identify if you need to tune your autovacuum configurations. A proper house cleaning can improve your database health and performance.

Learn how to migrate to the PHP framework Symfony

SensioLabs and Smile released a joint white paper “PHP framework migration: from legacy to Symfony” explaining how to migrate to modern PHP frameworks like Symfony. Find a selection of the key information in this infographic design by SensioLabs.

trufflehog

Find leaked credentials. Search on your repos, source-code, etc.

Why we don’t use a staging environment

Squeaky deploys their code directly from laptops to production environments. The blog posts details their strategies, such as a good suite of tests, clear branch strategy and use of feature flags.

Scaling containers on AWS in 2022

Benchmarking for different types of workloads and scales capabilities on AWS services in 2022: lambda, EKS, ECS, Fargate...

Building well-architected serverless applications: Introduction

Multi-part series addressing each of the questions within the Serverless Lens of the Well-Architected Tool.

Comparing Workflows

Comparision of different types of git flows: centralized, feature branch, gitflow and fork flow. Simple comparision, but easy to get the sense of their use cases.

Construct Hub

Find libraries for AWS Cloud Development Kit (AWS CDK), which generates AWS CloudFormation templates, CDK for Terraform (CDKtf), which generates HashiCorp Terraform configuration files, and CDK for Kubernetes (CDK8s), which generates Kubernetes manifests.

Too much magic?

A good thinking about the "magic" under some awesomeness that are provided by frameworks or libraries. Although they are good for quicker development, there is good to think a little bit more about how and when use it when we have a software that we aim to last longer and get to the phase of greater maintainability.

Categorias
Tropeçando

Tropeçando 2 – Republish

dBpoweramp: CD Ripper & Audio Converter. Secure ripping to mp3, FLAC, m4a, Apple Lossless & WMA

CD extractor and multi converter from different audio and video codecs. Lots of advanced options for different formats.

A última de bluetooth - rede entre dois GNU/Linux

No trabalho você tem um pc ligado a internet via wifi, ethernet, ou similares. Você leva seu notebook que gostaria que estivesse conectado também, como fica? Você está no aeroporto com mais uma pessoa, os dois de notebook, só um modem 3G, como fica? E se vocês tiverem só um login da Vex, prestadora de acesso wifi, como fica? Você faz uma rede bluetooth entre os dois e compartilha a conexão, ora. (com exceção do primeiro, todos os comandos abaixo são como root)

Paje Online: Como Converter Vídeos no Linux?

Converter arquivos de vídeos e som no Linux, abrangendo os mais variados formatos e codecs, pode ser uma tarefa razoavelmente simples, bastando conhecer o programa certo. Nesta dica vamos apresentar o programa ffmpeg.

Ubuntu Basic Commands

An extensive list of essential Linux commands that every Ubuntu user will find helpful in their Linux journey.

Instructables - Make, How To, and DIY

Aprenda a fazer tudo

PHP é à quinta-feira - Gerar uma password | Peopleware

Um conjunto de funções que os ajudarão a gerar uma password (ou qualquer outra string de caracteres aleatórios).

Color Hunter

crie e encontre paleta de cores a partir de imagens

Sua Língua » Arquivo » Não compre o novo VOLP! — 1ª parte

Blank/erase a DVD-RW | commandlinefu.com

Apagando um DVD-RW na linha de comando

Se eu soubesse que web 2.0 era isso… » CrisDias weblog

nth-child | Boas práticas de Desenvolvimento com Padrões Web

9 Interesting Facts To Know About a Website | Tools

Os top cinco erros não técnicos cometidos por desenvolvedores | Pacote201.com.br

Blog do Márcio d’Ávila » Cuidado - A fraude evoluiu

Dicas para evitar fraudes da internet.

Categorias
PHP

PHP 8.1: more on new in initializers

I could not agree more with Brent when he says concerning the "new in initializers"[1] feature:

PHP 8.1 adds a feature that might seem like a small detail, but one that I think will have a significant day-by-day impact on many people.

When I see this new feature, lots of places that use Dependency Injection[3] come to my focus as candidates to be impacted, such as application or infrastructure service classes. As a result, we will write a much cleaner and leaner code without giving up on good practices to write modular, maintainable and testable software.

The Dependency Inversion Principle[4] gives us decoupling powers. But we know that many classes will receive the same concrete implementation most of (if not all) the time.

So this is very common to see some variation of this code:

$someDependencyToBeInjected = FactoryClass::create();
$someService = new SomeServiceClass($someDependencyToBeInjected);

Important note: I will ignore for now Service Containers and frameworks features that deal with service instantiation, auto wiring, etc.

Think of a database query service class: you depend on a connection object. Every time you need to instantiate your database service class, you need to prepare the connection dependency and inject it at the service class. Database connections are a great example when you use the same concrete implementation more than 90% of the time.

The same applies to a Service class that you use to handle business logic and depends on QueryService and CommandHandler interfaces to do its job.

Before PHP 8.1 we have code like this:

// service class to apply business logic
// most standard
class DefaultLeadRecordService implements LeadRecordService
{
    public function __construct(
        private LeadQueryService $queryService,
        private LeadCommandHandler $commandHandler
    ) {
    }
}

// infrastructure class to match the interface -- a DBAL concrete class
// sneakily allowing a "default" value, but also open to Dependency Injection
// but not that great
class DbalLeadQueryService implements LeadQueryService
{
    public function __construct(private ?Connection $connection = null)
    {
        if (!$this->connection) {
            $this->connection = Core::getConnection();
        }
    }
}

// instantiation would be something like -- given you have $connection already instantiated
$connection =  \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config);

$service = new \Blog\Application\DefaultLeadRecordService(
    new \Blog\Infrastructure\DbalLeadQueryService($connection),
    new \Blog\Infrastructure\DbalLeadCommandHandler($connection),
);

// if we allow construct get default value
$service = new \Blog\Application\DefaultLeadRecordService(
    new \Blog\Infrastructure\DbalLeadQueryService(),
    new \Blog\Infrastructure\DbalLeadCommandHandler(),
);

While on PHP 8.1, you will be able to write it like so:

class DefaultLeadRecordService implements LeadRecordService
{
    public function __construct(
        private LeadQueryService $queryService = new DbalLeadQueryService(),
        private LeadCommandHandler $commandHandler = new DbalLeadCommandHandler()
    ) {
    }
}

// we see there is still room for new features here
// still not that great
class DbalLeadQueryService implements LeadQueryService
{
    public function __construct(private ?Connection $connection = null)
    {
        // waiting when new initializers feature allows static function as default parameters
        if (!$this->connection) {
            $this->connection = Core::getConnection();
        }
    }
}

$service = new \Blog\Application\DefaultLeadRecordService();

One liner! That saves a lot of typing, and the code remains very well structured. This is the type of significant impact we will have on our day-to-day work. We will write a more straightforward, robust and meaningful code, and we will ship features faster with high-quality code.

If you want to see a full implementation of this, check the code at https://github.com/rafaelbernard/blog-php-81-new-initializers

Test, our faithful friend

Writing tests is a must-have for any repository where quality is a requirement. However, the "New in initializers" feature does not force us to give up on a complete suite of tests. We still have all powers of unit or integration tests.

For application code, we would write unit tests and all the expectations for concrete dependencies:

<?php

namespace Test\Unit\Blog\Application;

use Blog\Application\DefaultLeadRecordService;
use Blog\Domain\LeadCommandHandler;
use Blog\Domain\LeadQueryService;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;

class DefaultLeadRecordServiceTest extends TestCase
{
    private const EMAIL = '[email protected]';

    private LeadQueryService|MockObject $leadQueryServiceMock;
    private LeadCommandHandler|MockObject $leadCommandHandlerMock;

    private DefaultLeadRecordService $service;

    protected function setUp(): void
    {
        parent::setUp();

        $this->leadQueryServiceMock = $this->getMockBuilder(LeadQueryService::class)->getMock();
        $this->leadCommandHandlerMock = $this->getMockBuilder(LeadCommandHandler::class)->getMock();

        $this->service = new DefaultLeadRecordService($this->leadQueryServiceMock, $this->leadCommandHandlerMock);
    }

    public function testCanAdd()
    {
        $this->leadQueryServiceMock
            ->expects(self::once())
            ->method('getByEmail')
            ->with(self::EMAIL)
            ->willReturn(false);

        $this->leadCommandHandlerMock
            ->expects(self::once())
            ->method('add')
            ->with(self::EMAIL)
            ->willReturn(1);

        $result = $this->service->add(self::EMAIL);

        self::assertEquals(1, $result);
    }

    public function testAddExistentReturnsFalse()
    {
        $this->leadQueryServiceMock
            ->expects(self::once())
            ->method('getByEmail')
            ->with(self::EMAIL)
            ->willReturn(['email' => self::EMAIL]);

        $this->leadCommandHandlerMock
            ->expects(self::never())
            ->method('add');

        $result = $this->service->add(self::EMAIL);

        self::assertFalse($result);
    }

    public function testCanGetAll()
    {
        $unsorted = [
            ['email' => '[email protected]'],
            ['email' => '[email protected]'],
            ['email' => '[email protected]'],
        ];

        $this->leadQueryServiceMock
            ->expects(self::once())
            ->method('getAll')
            ->willReturn($unsorted);

        $fetched = $this->service->getAll();

        $expected = $unsorted;
        asort($expected);

        self::assertEquals($expected, $fetched);
    }
}

Integration tests can be written for infrastructure code. For instance, we can use an SQLite database file to assert the logic for database operations.

Be aware that I am creating an SQLite temp database file on-demand for each test execution with $this->databaseFilePath = '/tmp/test-' . time(); and, thanks to the Dbal library, we can be confident that operations could work for any database.

-> It is highly recommended that, as an alternative, create a container with a seeded database that is compatible with your production database system.

<?php

namespace Test\Integration\Blog\Infrastructure;

use Blog\Infrastructure\DbalLeadQueryService;
use Doctrine\DBAL\Connection;
use Faker\Factory;
use Faker\Generator;
use Test\TestCase;

class DbalLeadQueryServiceTest extends TestCase
{
    private string $databaseFilePath;

    private Generator $faker;
    private Connection $connection;

    private DbalLeadQueryService $service;

    public function testCanGetAll()
    {
        $this->addEmail($email1 = $this->faker->email());
        $this->addEmail($email2 = $this->faker->email());
        $this->addEmail($email3 = $this->faker->email());

        $expected = [
            ['email' => $email1],
            ['email' => $email2],
            ['email' => $email3],
        ];

        $fetched = $this->service->getAll();

        self::assertEquals($expected, $fetched);
    }

    protected function setUp(): void
    {
        parent::setUp();

        $this->faker = Factory::create();

        $this->createLeadTable();

        $this->service = new DbalLeadQueryService($this->connection());
    }

    protected function tearDown(): void
    {
        parent::tearDown();

        $this->dropDatabase();
    }

    private function connection(): Connection
    {
        if (!isset($this->connection)) {
            $this->databaseFilePath = '/tmp/test-' . time();

            $config = new \Doctrine\DBAL\Configuration();
            $connectionParams = [
                'url' => "sqlite:///{$this->databaseFilePath}",
            ];

            $this->connection = DriverManager::getConnection($connectionParams, $config);
        }

        return $this->connection;
    }

    private function dropDatabase()
    {
        @unlink($this->databaseFilePath);
    }

    private function createLeadTable(): void
    {
        $this->connection()->executeQuery('CREATE TABLE IF NOT EXISTS leads ( email VARCHAR )');
    }

    private function addEmail(string $email): int
    {
        return $this->connection()->insert('leads', ['email' => $email]);
    }
}

Conclusion

PHP is evolving very quickly, with new features that enable more quality software, help developers and is even more committed to the fact that most of the web run flavours of PHP code. New features improve readability, software architecture, test coverage and performance. Those are all proof of a mature and live language.

Upgrade to PHP 8.1 and use "new in initializers" as soon as possible. You will not regret it.

If there is something you want to discuss more, let me know in the comments.

Links:

  1. New in initializers RFC
  2. Road to PHP 8.1
  3. Dependency Injection
  4. Dependency inversion principle
  5. Interface segregation principle
  6. Solid relevance
  7. SOLID principles