Fellipe Sanches' website

Author: F. Sanches

  • && instead of and, || instead of or?

    You can use AND/OR, but…

    Let’s get straight to the short answer: PHP supports AND/OR instead of &&/||, but because they have lower precedence, they can cause incorrect wait behavior.

    What is Lower precedence?

    Lower precedence is the same as “lower priority”; in this context, we are talking about the order in which an operator is evaluated after other operators in the same expression.

    In other words:
    When PHP reads a line of code with multiple operators, it decides which parts to calculate first based on operator precedence.

    Let’s look at some examples below to clarify the concept.

    Using && / ||

    $a = true;
    $b = false;
    $c = false;
    
    //waited expression
    $res = ($a && $b) || $c;
    //typed expression (it's the same above)
    $res = $a && $b || $c;
    
    
    //practical example
    if($res) {
      echo 'it\'s true';
    }
    else {
      echo 'it\'s false';
    }
    
    //output: it's false

    That makes sense, since express says: if variables a and b are true, or if variable c is true, show “true”, otherwise show “false”.

    Using and / or

    $a = true;
    $b = false;
    $c = false;
    
    //waited expression
    $res = ($a and $b) or $c;
    //typed expression (it isn't the same above)
    $res = $a and $b or $c;
    
    
    //practical example
    if($res) {
      echo 'it\'s true';
    }
    else {
      echo 'it\'s false';
    }
    
    //output: it's true

    Due to and has very low  precedence in PHP, lower than the assignment operator =, so the expression is interpreted as:

    ($res = $a) and $b or $c;

    The rest of the expression, and $b or $c, is then evaluated and discarded because it is not assigned to anything. This logical operation uses the value of $a as its first operand (true).

  • The easiest way to install xdebug with Ubuntu

    Here’s a clean, American-blog-style rewrite:

    Setting up Xdebug isn’t always straightforward. Depending on your setup, you might need to wire it into your IDE or get it running inside a Docker container.

    So in this tutorial, we’ll walk through a simple, foolproof way to set up Xdebug on Ubuntu Linux.

    Instalation

    First of all, check your PHP version with php -v.

    php -v
    PHP 8.4.15 (cli) (built: Nov 20 2025 17:43:25) (NTS)
    Copyright (c) The PHP Group
    Built by Debian
    Zend Engine v4.4.15, Copyright (c) Zend Technologies
        with Zend OPcache v8.4.15, Copyright (c), by Zend Technologies
        with Xdebug v3.4.7, Copyright (c) 2002-2025, by Derick Rethans

    Ondřej Surý’s PPA

    Now, if you installed PHP from Ondřej Surý’s repository, which is always the most up-to-date, you can install xdebug with.

    Note that you need to change your xdebug version to match your PHP version.

    sudo apt-get install php8.4-xdebug

    Default PPA

    But, if you are using the standard version of PHP included with Ubuntu, which is usually outdated, simply run:

    sudo apt-get install php-xdebug

    Run the php -v again and you should see the Xdebug information on the last line:

    php -v
    PHP 8.4.15 (cli) (built: Nov 20 2025 17:43:25) (NTS)
    Copyright (c) The PHP Group
    Built by Debian
    Zend Engine v4.4.15, Copyright (c) Zend Technologies
        with Zend OPcache v8.4.15, Copyright (c), by Zend Technologies
        with Xdebug v3.4.7, Copyright (c) 2002-2025, by Derick Rethans

    Then, run the built-in web server and you will also see a warning:

    php -S 127.0.0.1:8000
    [Sat Nov 22 12:00:22 2025] PHP Warning:  JIT is incompatible with third party extensions that override zend_execute_ex(). JIT disabled. in Unknown on line 0
    [Sat Nov 22 12:00:22 2025] PHP 8.4.15 Development Server (http://127.0.0.1:8000) started

    This warning is normal when you’re using Xdebug on PHP 8.1+ (including 8.4).
    Nothing is broken… So, what this warning means?

    Xdebug overrides the internal zend_execute_ex()function, which the PHP Just-In-Time compiler (JIT) also needs, since both can’t run at the same time, PHP automatically disables JIT.

    This scenario, with no JIT and using Xdebug, is generally satisfactory for a development environment.

    Usage

    Step Debugging

    Xdebug’s step debugger allows you to interactively walk through your code to debug control flow and examine data structures.

    Configuring Step Debugging

    On the linux shell, run the command below to find the current php.ini file.

    php -i | grep "Loaded Configuration File"

    Output: Loaded Configuration File => /etc/php/8.4/cli/php.ini

    Then, change (or include) the setting to xdebug.mode=debug, in case of inclusion, you can put it at the bottom of the file.

    In setups where PHP/Xdebug and your IDE all run on the same host, this is all you need to configure on the PHP and Xdebug side. 

    Command Line Debug Client

    The command line debug client allows you to debug PHP scripts without having to set up an IDE. This might be useful if you don’t want to tie your xdebug to a specific IDE.

    A binary for Linux, macOS, and Windows is available on the Xdebug downloads page. Here I will use Linux (x86_64).

    In the project root folder:

    wget https://xdebug.org/files/binaries/dbgpClient
    chmod +x dbgpClient
    ./dbgpClient 
    #OUTPUT
    Xdebug Simple DBGp client (0.6.1)
    Copyright 2019-2024 by Derick Rethans
    
    Waiting for debug server to connect on port 9003.

    So if you haven’t already, start the built-in PHP server in a new command prompt:

    php -S 127.0.0.1:8000

    The Xdebug output will change to:

    #OUTPUT
    Connect from 127.0.0.1:37386
    DBGp/1.0: Xdebug 3.4.7  For PHP 8.4.15
    Debugging file:///home/fellipe/projects/symfony/my-clone-of-symfony-gs/index.php (ID: 294841/YOUR-NAME)
    (cmd)  

    If PHP/Xdebug run on a different machine, virtual host, or in a Docker container, you need to tell Xdebug where to make the debugging connection to.

    Configuriing Xdebug to connect with a PHPStorm

    ./dbgpClient
    Xdebug Simple DBGp client (0.6.1)
    Copyright 2019-2024 by Derick Rethans

    Waiting for debug server to connect on port 9003.

  • Installing Composer the right way

    If you search online for ways to install Composer on Ubuntu, you’ll probably find suggestions like:

    sudo apt install composer

    It looks simple… but it’s actually one of the worst ways to install Composer.
    Here’s why:

    • It almost always installs an outdated version of Composer.
    • Composer adapts quickly to new PHP versions — Ubuntu packages don’t.
    • apt installs Composer system-wide, which requires sudo even though Composer doesn’t need root privileges.
    • You lose access to composer self-update, because you must wait until Ubuntu updates the package (which often never happens).

    So… let’s install Composer the right way.

    Install Composer in Your Project Root

    In the root of your project run:

    php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
    php -r "if (hash_file('sha384', 'composer-setup.php') === 'c8b085408188070d5f52bcfe4ecfbee5f727afa458b2573b8eaaf77b3419b0bf2768dc67c86944da1544f06fa544fd47') { echo 'Installer verified'.PHP_EOL; } else { echo 'Installer corrupt'.PHP_EOL; unlink('composer-setup.php'); exit(1); }"
    php composer-setup.php
    php -r "unlink('composer-setup.php');"

    This installs Composer directly from the official source.

    At this point, Composer works, but only if you run: php composer.phar.

    Pretty annoying, right?

    Let’s make it easier.

    Create a Local Composer Command (Project Only)

    So, let’s improve this, putting a symlink to your project uses its own composer command.

    In the same folder where composer.phar is located, create a blank file named composer, and edit it:

    touch composer
    vim composer

    Add this content and save it.

    #!/usr/bin/env bash
    php "$(dirname "$0")/composer.phar" "$@"
    

    Back to the shell, make it executable:

    chmod +x composer

    Now your project has its own composer executable…

    s$ ls -l
    total 3416
    -rw-r--r--  1 fellipe fellipe     841 Nov 10 23:01 README.md
    -rwxr-xr-x  1 fellipe fellipe      61 Nov 21 11:38 composer
    -rw-r--r--  1 fellipe fellipe     315 Nov 10 20:27 composer.json
    -rw-r--r--  1 fellipe fellipe  165048 Nov 10 20:27 composer.lock
    -rwxr-xr-x  1 fellipe fellipe 3281618 Nov 20 21:42 composer.phar
    drwxr-xr-x 10 fellipe fellipe    4096 Nov 20 19:46 vendor

    …and you can just run ./composer

    $ ./composer --version
    Composer version 2.9.2 2025-11-19 21:57:25
    PHP version 8.4.15 (/usr/bin/php8.4)

    Summary

    • You don’t put anything in $HOME/bin.
    • You don’t modify your global PATH.
    • You don’t need sudo.
    • You create a small wrapper script that works only inside this project.
    • You gain the flexibility to use different Composer versions per project, and you avoid any security risk from installing system-wide packages as root.

    Nice, right?

  • How to copy files between WSL Ubuntu instances

    It’s not always possible to update your Ubuntu version with just one command. In these cases, we need to set up a new server and migrate the files between them.

    In this tutorial we will see how to copy your projects from Ubuntu 20.04 to Ubuntu 24.04 under WSL.

    Listing your current WSL distros

    PowerShell
    wsl -l -v

    You will see something like this:

    PowerShell
    Ubuntu-20.04    Running   2

    Create the new Ubuntu distro

    In the Windows Command Prompt ou Powershell run the command below, and you will create a new Ubuntu WSL instance, without replace the current.

    PowerShell
    wsl --update
    wsl --install -d Ubuntu-24.04

    List the current WSL distros again with wsl -l -v , you should see both the newest instance now.

    PowerShell
    Ubuntu-20.04    Running   2
    Ubuntu-24.04    Stopped   2

    Copy files between instances

    Someone could copy the files between the WSL instances using a Windows command like robocopy, but this method would be extremely slow.

    Others might try copying the files from within the Linux instances, which could be more work than expected.

    Instead, we will copy the files directly inside the Windows Explorer, the simpliest way.

    In Windows Explorer, find the folder of both WSL, and just copy the files that you need from origin folder to destiny folder:

    Easy, no? But…

    The copies will be made. However, in the destination directory, we will have the Zone.Identifier files included; It is an NTFS Alternate Data Stream (ADS) automatically created by Windows.

    We don’t need them on our Linux system, so let’s remove them. Enter in the newest Linux created typint this in cmd or PowerShell:

    PowerShell
    wsl -d Ubuntu-24.04

    Go to the project directory:

    Bash
    cd ~/projects/
    sudo find . -type f -name '*:Zone.Identifier' -exec rm -f {} \;

    Okay, now we only have the same files from the old Ubuntu in the new Ubuntu.

    Finally, we now just need to adjust the permissions that are set for the root user to work on the files contained in /home/fellipe/projects which currently are granted to the root user. For it type:

    Bash
    sudo chown -R fellipe:fellipe /home/fellipe/projects

    Okay…

    Now you have your files on a new server, ready to be worked on.

    Remember, since you’re working from a new Ubuntu, the entire work environment will also need to be configured; things like the web server and PHP will need to be installed.

  • XDebug alternatives for debugging PHP

    Sometimes xdebug can be tricky to install and configure, especially in scenarios where it wasn’t designed to work natively, such as running your project in WSL or Docker.

    Today I will show you two simpler alternatives for debugging PHP, which will help you debug your project without wasting time on long installations.

    Pre-installation

    What we will use in this tutorial:

    • PHP 8.4
    • Composer 2.8

    Note: Since all versions of PHP 7 and Composer 1 are no longer supported, avoid using them.

    You can find a tutorial on how to update your PHP 7 to PHP 8 here, and a tutorial on how to install composer the right way here.

    To update your composer, you can simply run:

    ./composer self-update

    Kint – Advanced PHP dumper

    🐈‍⬛ https://github.com/kint-php/kint

    The first debug tool is the Kint. Think of Kint as a smart evolution of a var_dump(), but much more complete and interactive.

    Instalation

    ./composer require kint-php/kint --dev

    Usage with Composer

    <?php
    
    include 'vendor/autoload.php';
    
    d('Dumped with Kint');
    
    $time = new DateTime();
    $data = [
        'id' => 42,
        'status' => 'active',
        'features' => ['debug', 'logging', 'profiling'],
        'settings' => [
            'theme' => 'dark',
            'notifications' => true
        ]
    ];
    $object = new stdClass();
    $object->title = "My Symfony Clone";
    $object->author = "Fellipe";
    $object->version = 1.0;
    
    d($time, $data, $object);

    Resources

    • Live search – This feature let you to fint values through complex data with ease.
    • Dump Data – you can unfold and explore instantly arrays, jsons, php objects, etc.
    • Generates code – Kint generates the exact code you need to access specific fields.

    Kind have a lot of other nice features, you can see all them here: https://kint-php.github.io/kint/

    PHP Debug Bar

    🐈‍⬛ https://github.com/php-debugbar/php-debugbar

    The next tool is the PHP Debug Bar, that displays a debug bar in the browser with information from PHP.

    Installation

    ./composer require --dev php-debugbar/php-debugbar

    Usage with Composer

    <?php
    
    require 'vendor/autoload.php';
    use DebugBar\StandardDebugBar;
    
    $time = new DateTime();
    $data = [
            'id' => 42,
            'status' => 'active',
            'features' => ['debug', 'logging', 'profiling'],
            'settings' => [
                    'theme' => 'dark',
                    'notifications' => true
            ]
    ];
    
    $dataJson = json_encode($data);
    
    $object = new stdClass();
    $object->title = "My Symfony Clone";
    $object->author = "Fellipe";
    $object->version = 1.0;
    
    $debugbar = new StandardDebugBar();
    $debugbarRenderer = $debugbar->getJavascriptRenderer();
    
    // --- MESSAGES ---
    $debugbar["messages"]->addMessage("Hello world in debugbar!");
    $debugbar["messages"]->addMessage("Current time: " . $time->format("Y-m-d H:i:s"));
    $debugbar["messages"]->addMessage($data);
    $debugbar["messages"]->addMessage("JSON data: " . $dataJson);
    $debugbar["messages"]->addMessage($object);
    $debugbar["time"]->addMeasure("Script started", 0, microtime(true));
    
    ?>
    <html>
    <head>
        <?php echo $debugbarRenderer->renderHead(); ?>
    </head>
    <body>
    
    <h1>DebugBar Test</h1>
    
    <?php echo $debugbarRenderer->render(); ?>
    
    </body>
    </html>
    

    Resources

    • Search – This feature let you to fint values through complex data with ease.
    • Dump Data – you can unfold and explore instantly arrays, jsons, php objects, etc.

    PHP Debug Bar have a lot of other nice features, you can see all them here: https://php-debugbar.com/

    Conclusion

    Both are useful tools for debugging. While Kint generates the appropriate code to access data, the PHP Debug Bar seems to make debugging easier with its toolbar.

    Keep in mind that both have more advanced features that weren’t covered here.
    So, which one do you prefer? Try them out and decide for yourself!

  • Interface Segregation Principle

    Interfaces let you write code that depends on abstractions, not on concrete classes.
    When you programming this way, your code becomes more flexible, easier to test, easier to replace, and less dependent on specific implementations.

    Let’s see it in some exemples below:

    //Bad :-( 
    //Programming to a concrete class with tightly coupled code
    class MySqlDatabase {
        public function connect() {
            echo "Connected to MySQL";
        }
    }
    
    class UserService {
        private MySqlDatabase $db;
    
        public function __construct() {
            $this->db = new MySqlDatabase(); // tightly coupled
        }
    
        public function loadUser() {
            $this->db->connect();
        }
    }
    
    //Good :-)
    //Define an interface and have decoupled code, like Lego bricks!
    interface DatabaseConnection {
        public function connect();
    }
    
    //Create multiple implementations
    class MySqlDatabase implements DatabaseConnection {
        public function connect() {
            echo "Connected to MySQL";
        }
    }
    
    class PostgresDatabase implements DatabaseConnection {
        public function connect() {
            echo "Connected to PostgreSQL";
        }
    }
    
    //Use only the interface inside your main class
    class UserService {
        private DatabaseConnection $db;
    
        // Any database that implements the interface can be injected
        public function __construct(DatabaseConnection $db) {
            $this->db = $db;
        }
    
        public function loadUser() {
            $this->db->connect();
        }
    }
    
    //Choose the implementation at runtime
    $service = new UserService(new PostgresDatabase()); //Postgres: I Choose You!
    $service->loadUser();
    

    Sometimes it isn’t always clear how to properly segregate your interface or to predict future changes;

    So, if in the future your interface becomes too bloated, don’t hesitate to divide it into smaller and more focused sections, each representing a single responsibility.

    When designing interfaces, you should always strive to be as precise as possible; since they describe what the parts of your system are capable of doing, the clearer that description is, the easier it will be to build, update, and maintain your software.

  • Composing Objects Principle

    When a subclass inherits from a super class, the subclass will gain knowledge and access to all of the super classes attributes and methods as long as their access modifiers are not private. 

    This means that if you have multiple levels of inheritance, a subclass at the bottom of the inheritance tree can potentially have a way to access attributes and behaviors of all the super classes. 

    It’s “bad” not because inheritance is wrong, but because deep or excessive inheritance creates several structural problems in software design.

    So, what can we do to avoid this? 

    Composing Objects Principle

    You can use the composing objects principle to gain a high amount of code reuse without using inheritance. This principle states that classes should achieve code reuse through aggregation rather than inheritance. 

    Design patterns, such as the composite design pattern and decorator design pattern use this design principle. 

    Composing objects does not force you to try and find commonalities between two classes, and couple them together like with inheritance. Instead, you’re able to design classes that can work together without having to share anything between them. This flexibility will also help you if the system requirements change.

    To clarify, let’s look at a code snippet below, comparing both modes.

    <?php
    //Inheritance way
    class Logger {
        public function log(string $message): void {
            echo "[LOG] " . $message . "<br>";
        }
    }
    
    class UserService extends Logger {
        public function createUser(string $name): void {
            $this->log("Creating user: {$name}");
            echo "User {$name} created.<br>";
        }
    }
    
    <?php
    //Composite way
    class Logger {
        public function log(string $message): void {
            echo "[LOG] " . $message . "<br>";
        }
    }
    
    class UserService {
        private Logger $logger;
    
        public function __construct(Logger $logger) {
            $this->logger = $logger;
        }
    
        public function createUser(string $name): void {
            $this->logger->log("Creating user: {$name}");
            echo "User {$name} created.<br>";
        }
    }
    

    Dynamic Behavior

    In addition to less coupling, composition does allow dynamic behavior at runtime, because you can inject different objects into a class:

    $userService = new UserService(new FileLogger());
    // later…
    $userService->setLogger(new DatabaseLogger());
    

    What you can’t do with inheritance, once this relationship is fixed in the source code:

    class UserService extends Logger {}

    Now, it’s not to say that inheritance should never be used. Composing objects in inheritance both have their place. Composing will give you better flexibility and less coupling while maintaining reusability, but that doesn’t mean it should always be used over inheritance. 

    You need to examine the needs of your system in order to determine which design principle is appropriate. 

    • Do you have a set of related classes or unrelated classes? 
    • What is a common behavior between them? 
    • Do you need specialized classes to handle specific cases or do you simply need a different implementation of the same behavior? 

    This are the different kinds of questions that you’ll need to ask yourself when designing your software systems.

  • Code smell: how to fix Long Methods

    Long Method is our code smell type today.

    What is a code smell?

    A code smell is a sign that something in your code might be poorly designed and could cause problems later on.

    In this series of posts I will talk about the most common code smells problems and then how fix each one.

    Problem:

    A method with many lines of code, or not in the best-case, but which is still difficult to understand, reuse, and maintain.

    Ways to avoid it:

    Divide your confusing method into well-defined part

    You have a block of code that logically belongs together:

    //Bad :-(
    //Everything mixed together
    function showProfile() {
        $this->showHeader();
    
        // All profile details printed here
        echo "User: " . $this->username;
        echo "Email: " . $this->email;
    }

    Extract it into a new method and replace the original code with a call to that method:

    //Good :-)
    //Details moved to another function
    function showProfile() {
        $this->showHeader();
        $this->printUserInfo();
    }
    
    function printUserInfo() {
        echo "User: " . $this->username;
        echo "Email: " . $this->email;
    }

    If you change the user info, you only change one function, and you can reuse it.

    Change temporary variables by a query

    Sometimes, you create a variable just to store a value temporarily so you can use it later in your code. This makes the code harder to reuse, harder to clean up, and harder to extract into smaller methods:

    //Bad :-(
    //Using a temp variable
    $totalCost = $this->cups * $this->pricePerCup;
    
    if ($totalCost > 20) {
        return "You get a free cookie!";
    } else {
        return "No free cookie this time.";
    }
    

    Create a small function that can calculate the value anytime you ask:

    //Good :-)
    //Replace temp var with query
    if ($this->totalCost() > 20) {
        return "You get a free cookie!";
    } else {
        return "No free cookie this time.";
    }
    
    function totalCost() {
        return $this->cups * $this->pricePerCup;
    }
    

    The total cost is calculated in one single place, where you can change the price or rule.

    Change parameters that are repeated by an object

    Your methods contain a repeating group of parameters:

    class Customer
    {
        public function amountInvoicedIn(DateTime $start, DateTime $end) {
            // ...
        }
    
        public function amountReceivedIn(DateTime $start, DateTime $end) {
            // ...
        }
    
        public function amountOverdueIn(DateTime $start, DateTime $end) {
            // ...
        }
    }

    Replace these parameters with an object:

    class Customer
    {
        public function amountInvoicedIn(DateRange $range) {
            // ...
        }
    
        public function amountReceivedIn(DateRange $range) {
            // ...
        }
    
        public function amountOverdueIn(DateRange $range) {
            // ...
        }
    }
    

    Less repetition, cleaner code, and the ability to make changes within the object in a centralized way.

    Preserve whole object

    You write a method that needs several pieces of data from the same object, you pass three or four values taken from the same object for instance just to call another method:

    $startDatetime = $today->getStartDatetime();
    $endDatetime = $today->getEndDatetime();
    $todaysTime = $time->timeDiff($startDatetime, $endDatetime);

    Instead of doing that, you can simply pass the entire object and let the method get whatever it needs:

    $todaysTime = $time->timeDiff($today);
  • Dependency Inversion Principle

    A common problem that you’ll need to address when designing your systems is dependency.

    Software dependency is basically about how much one part of your system relies on another.

    When your software is highly coupled, trying to substitute one class or resource for something else is not easily done, or sometimes even possible. To address this issue, we use the Dependency Inversion Principle (DIP), its premise is:

    “Depend on abstractions, not on concretions.”

    The idea is simple, interfaces and abstract classes are considered high level resources and shouldn’t depend on concrete classes that are considered low-level modules.

    An interface or abstract class defines a general set of behaviors, and the concrete classes provide the implementation for these behaviors.

    Let’s take a look at some exemples below.

    Direct dependency, without DIP:

    <?php
    
    class Fan
    {
        public bool $isOn = false;
    
        public function turnOn(): void
        {
            $this->isOn = true;
            // ...
        }
    
        public function turnOff(): void
        {
            $this->isOn = false;
            // ...
        }
    }
    
    class SwitchButton
    {
        private Fan $fan;
    
        public function __construct(Fan $fan)
        {
            $this->fan = $fan;
        }
    
        public function toggle(): void
        {
            if (!$this->fan->isOn) {
                $this->fan->turnOn();
            } else {
                $this->fan->turnOff();
            }
        }
    }

    Now using Dependency Inversion Principle, DIP:

    <?php
    
    //Interface
    interface Device
    {
        public function turnOn(): void;
        public function turnOff(): void;
        public function isOn(): bool;
    }
    
    //Concrete Implementation Device 1 (Fan)
    class Fan implements Device
    {
        private bool $on = false;
    
        public function turnOn(): void
        {
            $this->on = true;
        }
    
        public function turnOff(): void
        {
            $this->on = false;
        }
    
        public function isOn(): bool
        {
            return $this->on;
        }
    }
    
    
    //Concrete Implementation Device 2 (Lamp)
    class Lamp implements Device
    {
        private bool $on = false;
    
        public function turnOn(): void
        {
            $this->on = true;
        }
    
        public function turnOff(): void
        {
            $this->on = false;
        }
    
        public function isOn(): bool
        {
            return $this->on;
        }
    }
    
    //High-Level Class (SwitchButton) depending on the interface
    class SwitchButton
    {
        private Device $device;
    
        public function __construct(Device $device)
        {
            $this->device = $device;
        }
    
        public function toggle(): void
        {
            if (!$this->device->isOn()) {
                $this->device->turnOn();
            } else {
                $this->device->turnOff();
            }
        }
    }

    Since building a flexible, reusable and maintainable system will prolong the life of your software, applying this design principle will help you achieve those goals.

  • Facade & Dependency Injection (DI)

    Facade is a structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes.

    This design pattern uses a number of different design principles, to provide ease of access to complex classes, what we call subsystem. This is done by encapsulating the subsystem classes into a Facade class, and then hiding them from the client classes so that the clients do not know about the details of the subsystem.

    When use it: Use the Facade pattern when you need to have a limited but straightforward interface to a complex subsystem.

    <?php
    // --- Subsystems --- //
    
    class InventoryService {
        public function checkStock(string $productId): bool {
            echo "Checking stock for product: $productId\n";
            // Simulated logic
            return true;
        }
    
        public function reserveItem(string $productId): void {
            echo "Reserving item: $productId\n";
        }
    }
    
    class PaymentService {
        public function processPayment(float $amount, string $method): bool {
            echo "Processing payment of $$amount via $method\n";
            // Simulated logic
            return true;
        }
    }
    
    class ShippingService {
        public function ship(string $productId, string $address): void {
            echo "Shipping product '$productId' to $address\n";
        }
    }
    
    class NotificationService {
        public function sendConfirmation(string $email, string $message): void {
            echo "Sending confirmation email to $email: $message\n";
        }
    }
    
    // --- Facade --- //
    
    class OrderFacade {
        private InventoryService $inventory;
        private PaymentService $payment;
        private ShippingService $shipping;
        private NotificationService $notification;
    
        public function __construct() {
            $this->inventory = new InventoryService();
            $this->payment = new PaymentService();
            $this->shipping = new ShippingService();
            $this->notification = new NotificationService();
        }
    
        /**
         * Simplified order process using the Facade
         */
        public function placeOrder(string $productId, float $amount, string $paymentMethod, string $address, string $customerEmail): void {
            echo "Starting order process...\n";
    
            if (!$this->inventory->checkStock($productId)) {
                echo "Product not available in stock.\n";
                return;
            }
    
            $this->inventory->reserveItem($productId);
    
            if (!$this->payment->processPayment($amount, $paymentMethod)) {
                echo "Payment failed. Order canceled.\n";
                return;
            }
    
            $this->shipping->ship($productId, $address);
            $this->notification->sendConfirmation($customerEmail, "Your order for '$productId' has been successfully processed!");
    
            echo "Order completed successfully.\n";
        }
    }
    
    // --- Client Code --- //
    $orderFacade = new OrderFacade();
    $orderFacade->placeOrder(
        productId: 'SKU12345',
        amount: 199.99,
        paymentMethod: 'Credit Card',
        address: '123 Business Street, Cityville',
        customerEmail: 'customer@example.com'
    );

    As you can see, a facade class can be used to wrap all the interfaces and classes for a subsystem. It is your decision as to what you want to wrap.

    Now let’s improve this code by using Dependency Injection (DI) in it.

    Dependency Injection means giving an object its dependencies from something outside, instead of the object creating them by itself.

    <?php
    // --- Interfaces --- //
    
    interface InventoryInterface {
        public function checkStock(string $productId): bool;
        public function reserveItem(string $productId): void;
    }
    
    interface PaymentInterface {
        public function processPayment(float $amount, string $method): bool;
    }
    
    interface ShippingInterface {
        public function ship(string $productId, string $address): void;
    }
    
    interface NotificationInterface {
        public function sendConfirmation(string $email, string $message): void;
    }
    
    // --- Concrete Implementations --- //
    
    class InventoryService implements InventoryInterface {
        public function checkStock(string $productId): bool {
            echo "Checking stock for product: $productId\n";
            return true;
        }
    
        public function reserveItem(string $productId): void {
            echo "Reserving item: $productId\n";
        }
    }
    
    /**
     * Stripe payment service
     */
    class StripePaymentService implements PaymentInterface {
        public function processPayment(float $amount, string $method): bool {
            echo "Processing payment of $$amount via $method\n";
            return true;
        }
    }
    
    /**
     * PayPal payment service
     */
    class PaypalPaymentService implements PaymentInterface {
        public function processPayment(float $amount, string $method): bool {
            echo "Processing PayPal payment of $$amount using $method\n";
            return true;
        }
    }
    
    class ShippingService implements ShippingInterface {
        public function ship(string $productId, string $address): void {
            echo "Shipping product '$productId' to $address\n";
        }
    }
    
    class NotificationService implements NotificationInterface {
        public function sendConfirmation(string $email, string $message): void {
            echo "Sending confirmation email to $email: $message\n";
        }
    }
    
    // --- Facade --- //
    
    class OrderFacade {
        public function __construct(
            private InventoryInterface $inventory,
            private PaymentInterface $payment,
            private ShippingInterface $shipping,
            private NotificationInterface $notification
        ) {}
    
        /**
         * High-level method hiding all subsystem complexity
         */
        public function placeOrder(
            string $productId,
            float $amount,
            string $paymentMethod,
            string $address,
            string $customerEmail
        ): void {
            echo "Starting order process...\n";
    
            if (!$this->inventory->checkStock($productId)) {
                echo "Product not available in stock.\n";
                return;
            }
    
            $this->inventory->reserveItem($productId);
    
            if (!$this->payment->processPayment($amount, $paymentMethod)) {
                echo "Payment failed. Order canceled.\n";
                return;
            }
    
            $this->shipping->ship($productId, $address);
            $this->notification->sendConfirmation(
                $customerEmail,
                "Your order for '$productId' has been successfully processed!"
            );
    
            echo "Order completed successfully.\n";
        }
    }
    
    // --- Client Code --- //
    $inventory = new InventoryService();
    $payment = new PaypalPaymentService();
    $shipping = new ShippingService();
    $notification = new NotificationService();
    
    $orderFacade = new OrderFacade($inventory, $payment, $shipping, $notification);
    
    $orderFacade->placeOrder(
        productId: 'SKU12345',
        amount: 199.99,
        paymentMethod: 'PayPal',
        address: '123 Business Street, Cityville',
        customerEmail: 'customer@example.com'
    );
    

    What changed?

    • Interfaces: define contracts for each subsystem. You can swap implementations easily (e.g., replace PayPalPaymentService with StripePaymentService).
    • Dependency Injection: the Facade doesn’t create its dependencies — they’re injected externally in client code.
    • Loose coupling: classes depend on abstractions, not concrete classes.
    • Testable: in unit tests, you can inject mock objects.