Object Oriented Programming is about writing code using Objects. Once we understand the concept of an "Object" and its main Principles, we are ready to start writing OOP code.

Prerequisites:

 

OOP always start with a main file where the main entry point is located. This is the file that the Web Server will refer the client requests if this file is part of a Web Application.

This file is commonly named as index and in the case of PHP Language is called index.php

 In this file we include the main "Object" and a call to the main "Method" on this "Object" that will be the entry point of the entire Application.

Examples of these:

Symfony 5.4:

<?php

use App\Kernel;

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

return function (array $context) {
    return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

 

Legacy Symfony 2.8 app_dev (index when in dev mode):

<?php

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Debug\Debug;

// If you don't want to setup permissions the proper way, just uncomment the following PHP line
// read http://symfony.com/doc/current/book/installation.html#checking-symfony-application-configuration-and-setup
// for more information
//umask(0000);

/**
 * @var Composer\Autoload\ClassLoader $loader
 */
$loader = require __DIR__.'/../app/autoload.php';
Debug::enable(E_RECOVERABLE_ERROR & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED, false);
//switch to Debug::enable(); for all messages
$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

Joomla 3.4 index:

<?php
/**
 * @package    Joomla.Site
 *
 * @copyright  (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
 * @license    GNU General Public License version 2 or later; see LICENSE.txt
 */

/**
 * Define the application's minimum supported PHP version as a constant so it can be referenced within the application.
 */
define('JOOMLA_MINIMUM_PHP', '5.3.10');

if (version_compare(PHP_VERSION, JOOMLA_MINIMUM_PHP, '<'))
{
	die('Your host needs to use PHP ' . JOOMLA_MINIMUM_PHP . ' or higher to run this version of Joomla!');
}

// Saves the start time and memory usage.
$startTime = microtime(1);
$startMem  = memory_get_usage();

/**
 * Constant that is checked in included files to prevent direct access.
 * define() is used in the installation folder rather than "const" to not error for PHP 5.2 and lower
 */
define('_JEXEC', 1);

if (file_exists(__DIR__ . '/defines.php'))
{
	include_once __DIR__ . '/defines.php';
}

if (!defined('_JDEFINES'))
{
	define('JPATH_BASE', __DIR__);
	require_once JPATH_BASE . '/includes/defines.php';
}

require_once JPATH_BASE . '/includes/framework.php';

// Set profiler start time and memory usage and mark afterLoad in the profiler.
JDEBUG ? JProfiler::getInstance('Application')->setStart($startTime, $startMem)->mark('afterLoad') : null;

// Instantiate the application.
$app = JFactory::getApplication('site');

// Execute the application.
$app->execute();

Wordpress index:

<?php
/**
 * Front to the WordPress application. This file doesn't do anything, but loads
 * wp-blog-header.php which does and tells WordPress to load the theme.
 *
 * @package WordPress
 */

/**
 * Tells WordPress to load the WordPress theme and output it.
 *
 * @var bool
 */
define('WP_USE_THEMES', true);

/** Loads the WordPress Environment and Template */
require( dirname( __FILE__ ) . '/wp-blog-header.php' );

 

If you are building your Application from scratch, I encourage you to follow this pattern on having a single point of entry through the index.php file:

<?php
/*
 * Index file
 */

//------------------------------------------------
// Basic autoloader
include 'autoloader.php';

$app = new services\router();

$app->run();

 

In modern PHP Applications, the code is encapsulated in "Objects" called "Classes" and as Best Practice, there is always one "Class" per PHP file. These PHP files are organized in sub-directories of the main root directory of the Application. For easy access and handling of these, PHP has a concept called "namespace". In each PHP file before the class definition, the file's namespace is declared:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * Employees
 *
 * @ORM\Table(name="employees", indexes={@ORM\Index(name="fk_employees_bank_accounts1_idx", columns={"bank_accounts_id"}), @ORM\Index(name="fk_employees_tax_info1_idx", columns={"tax_info_id"}), @ORM\Index(name="fk_employees_audit_log1_idx", columns={"audit_log_id"}), @ORM\Index(name="fk_employees_contact_info1_idx", columns={"contact_info_id"})})
 * @ORM\Entity
 */
class Employees
{
   ...

 

The namespace is the path from the Application's root directory to the directory where the PHP file is actually placed.

In the PHP index file, the first thing to be done is to register these namespaces and files, so PHP can locate and load them with the "use" directive. A simple auto-loader script may look like this:

<?php
//namespace private;

/**
 * Basic autoload implementation
 */
function loadClass($className) {
        $fileName = '';
        $namespace = '';

        // Sets the include path as the "src" directory
        $includePath = dirname(__FILE__).DIRECTORY_SEPARATOR.'app';

        if (false !== ($lastNsPos = strripos($className, '\\'))) {
            $namespace = substr($className, 0, $lastNsPos);
            $className = substr($className, $lastNsPos + 1);
            $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
        }
        $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
        $fullFileName = $includePath . DIRECTORY_SEPARATOR . $fileName;
      
        if (file_exists($fullFileName)) {
            require $fullFileName;
        } else {
            echo 'Class "'.$className.'" does not exist.';
        }
    }

spl_autoload_register('loadClass'); // Registers the autoloader

 

A better and simpler way to implement class auto-loading is to use composer package manager, even if you are not using any open source package, you can leverage the auto-loading logic provided by composer.

I suggest to place in the root of your Application a composer.json file like this:

{
    "type": "project",
    "license": "proprietary",
    "description": "Cool Project built from Scratch.",
    "require": {
        "php": ">=8.2"
    },
    "require-dev": {
    },
    "config": {
        "preferred-install": {
            "*": "dist"
        },
        "sort-packages": true,
        "allow-plugins": {
        }
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "replace": {
    },
    "scripts": {
    },
    "conflict": {
    },
    "extra": {
    }
}

Then open a SSH console and run:

$> composer install

 Composer will create a vendor folder and will put in it all these files used to register and auto-load all the PHP files.

 

 After that, all PHP class files placed in the "src" folder can be name-spaced starting with "App\" no matter how many sub-directory levels are needed to reach the PHP file:

<?php

namespace App\ProductsBundle\Api\Transformers;

...

 

The purpose of all Computer programs is to process an Input Request, process it according to some Business Logic and produce an Output Response.

In the case of PHP Web based Applications, the incoming client request is catch by the Web Server, then redirected to the PHP index file where the program will get the data out of the "Request Object" sent by the Web Server, process it, build a "Response Object" and send it back to the Web Server, which in turn send it back to the client's Browser.  This is called the Request - Response cycle.

 A typical visit to a web page produces a Web Request like this:

 

 And the response in this case is a Html page used to render the web page in the Browser's Tab Window along with all required Style Sheets and Javascript code as separate Requests.

 

 

Object Oriented Programming is about writing code using constructs called objects. We can say that everything can be called an object if we stick to the accepted definition by the Language Academia1. To understand the concept of object in Computer Programming we may start with some some real life object samples.

Mary is attending Elementary School. In her backpack she carries all her School supplies.

 

 

 We can call each one of the supplies including the Backpack an "Object". Some of these are:

    Backpack
    Sandwich
    Apple
    Banana
    Water Bottle
    Calculator
    Color Crayons
    Pencils
    Ruler
    Notebook

 

We can notice that each "Object" has "Properties".

Some of the properties of the Apple:

    Object Name: "Apple"
    Object Color: "Red"
    Object Type: "Food"
    Object has Seeds? "Yes"
    Object Need to be refrigerated? "No"

The Banana:

    Object Name: "Banana"
    Object Color: "Yellow"
    Object Type: "Food"
    Object has Seeds? "No"
    Object Need to be refrigerated? "No"

 

The Sandwich is an "Object" made of another "Objects" and also it has its own "Properties":

Object "Sandwich":

    Object "Ham":
        Name: "Honey Ham"
        Type: "Food"
        Need to be refrigerated? "Yes"
        Total Amount: 10
        Unit of Measure: Slice
    Object "Bread":
        Name: "Full Grain Bread"
        Type: "Food"
        Need to be refrigerated? "No"
        Total Amount: 25
        Unit of Measure: Slice
    Object "Cheese":
        Name: "Cheddar Cheese"
        Type: "Food"
        Need to be refrigerated? "Yes"
        Total Amount: 12
        Unit of Measure: Slice
    Object "Lettuce":
        Object Name: "Iceberg Lettuce"
        Object Color: "Green"
        Object Type: "Food"
        Object has Seeds? "No"
        Total Amount: 50
        Unit of Measure: Portion
    Ham Slices Count: "Not Defined Yet"
    Bread Slices Count: "Not Defined Yet"
    Cheese Slices Count: "Not Defined Yet"
    Has Mayo?: "Not Defined Yet"

 

 At this moment, we notice that the Sandwich has some "Properties" that say "Not Defined Yet". This is the initial "State" of the Sandwich "Object".

 There are "Actions" that can be taken for some of these "Objects" for changing their "State".

For Example, in the case of the Sandwich there is an "Action" that "Calls" another "Actions" in the included "Objects" and also an "Action" in the own Sandwich "Object":

Action "Make Me a Sandwich":

    From Included "Objects":
        "Action Add Some" of "Ham"
        "Action Add Some" of "Bread"
        "Action Add Some" of "Cheese"
        "Action Add Some" of "Lettuce"
    In "This" "Object":
        "Action Spread Some Mayo"

 

In Object Oriented Programming we call each "Object's" "Actions" a "Method" and each one may contain calls to other "Methods" or just simple logic to transform the "State" of the "Object" itself or another "Object's" "State" from "Stage 1" to "Stage 2".

When we refer to the "Object" itself we use the words "this" or "self".

We call all "Objects" used to make another one "Dependencies". In the case of the Sandwich "Object", its "Dependencies" are the "Ham", "Cheese", "Bread" and "Lettuce".

The action of including "Dependencies" is called "Dependency Injection" or "DI".

We use these "Objects" to encapsulate data and fragments of code in a well organized manner using these "Properties" and "Methods" to build Object Oriented Programs.

 

 

 

1 See Object definition by Merriam-Webster.

 

 As I was mentioning in past Article, understanding key Object Oriented Programming concepts is required to be able to produce Modern, High Quality PHP Software Applications that stick to OOP Best Practices and Principles.

Prerequisites:

Basics of Object Oriented Programming in PHP - Part II

 

In this Part I will continue covering  the most important things to keep in mind when developing new code:

  1. Group Classes in folders based on the main type or functionality.
  2. When developing in a PHP Framework like Symfony, from the entry point of a Request where you start coding, to the exit point with a Response, do not make your code to jump from one method to another more than 3 ~ 5 times, and do not make these jumps to be in more than 3~4 files.
  3. Always use type casting and doc blocks with annotations. This will will help in building a very robust Enterprise Grade Application and also will help your IDE Intelli-Sense to better index and understand you code, facilitating finding and auto-completion.
  4. Implement your code in such way  that each function does only one thing. It is also a must if you are required to implement PHP Unit Testing or building a TDD Application.
  5. Avoid as much as possible code obscuring things like functions handling functions.

 

1. Grouping Classes based on its type or main functionality is very important to keep a healthy code-base. It is accepted to name the folder as the type-functionality its Classes have. This way, its very easy to locate and include other Classes with the "use..." statement:

//Single file use statements
use App\ApiBundle\Exceptions\NotFoundException;
use App\ApiBundle\Exceptions\PreconditionFailedException;
use App\ApiBundle\Exceptions\SystemException;
use App\ApiBundle\Services\ApiGuzzleClient;
use App\SecurityBundle\Entity\User;


//Directory Aliasing
use App\UsersBundle\Api\Dtos as DTO;
use App\UsersBundle\Api\Transformers as TRANSFORMER;


class Users
{

//...

   /**
     * @param int $id
     * @return DTO\User
     */
    public function getUser(int $id): DTO\User
    {
        try {
            $user = $this->em->getRepository(User::class)->find($id);
            if ($user) {
                $getUserTransformer = new TRANSFORMER\GetUserTransformer($user);
                $userDto = $getUserTransformer->transform();
                return $this->response->buildOkResponse($userDto);
            } else {
                throw new NotFoundException('No results found');
            }
        } catch (Throwable $e) {
            throw new SystemException($e->getMessage());
        }
    }

//...


}

 

2. It is very common in old code-bases or in not too old but large ones, where over a few years, many different developers have been working on the same code, that an endless thread of calls from one function to another one start showing up.

This create something referred as "Spaguetti Code". It is very important to avoid at all costs falling into this anti-pattern. A High Quality, Highly Efficient Enterprise Software Application, must have the same in its code-base. To do so, once Classes are well organized by type in its corresponding folders, a typical Use Case like a "GET User by Id" shouldn't take more than 3 ~ 5 levels deep.

Example using PHP Symfony Framework:

Incoming Request

   --> UsersController>GetUserById

      --> UsersService>GetUser

               --> GetUserEntityFromDb

         --> TransformUserEntityIntoUserDto

         --> TransformUserDtoIntoJsonresponse

Outgoing Response

 Notice that we are transforming the Entity into something called DTO (Data transfer Object). This is because when building a Enterprise Class REST API, you must have a contract between the parties involved in the "deal". in this case the deal is between a Client and the Backend Application. I use DTOs to ensure all the required data is declared and its validated in terms of Data Types and Data constraints.

DTOs are the best way to ensure Data Integrity and Consistency and are a must when dealing with complex Data Structures where we have nested Dtos within other Dtos to several levels deep:

//Sample of a nested array of other Dtos
//...
    /**
     * @var array Array of CoolStuff
     * @JMS\Type("array<App\ProductsBundle\Api\Dtos\CoolStuff>")
     * @JMS\SerializedName("techStack")
     */
    public $techStack;
//Sample of Dto Validation
    /**
     * @var string Proposed Budget Source
     * @Assert\Length(
     *      max = 255,
     *      maxMessage = "proposedBudgetSource cannot be longer than 255 characters",
     *      allowEmptyString = false
     * )
     * @JMS\Type("string")
     * @JMS\SerializedName("proposedBudgetSource")
     */
    public $proposedBudgetSource;

I will explain DTOs and how to use them in other Articles.

 3. Type casting is one of the most important additions to the newest versions of PHP. When you implement a new function, you always type cast each one of the injected params and the return type. This will ensure consistency and integrity for your application.

Also, PHP Unit testing implementation will be much more easy and modern IDEs like PHPStorm have a feature called Intellisene that uses type casting to better understand your code and help you on code autocompletion, debuging and doc block generation.

 

    /**
     * @param int $id
     * @return DTO\User
     */
    public function getUser(int $id): ?DTO\User
    {
        try {
            $user = $this->em->getRepository(User::class)->find($id);
            if ($user) {
                $getUserTransformer = new GetUserTransformer($user);
                $userDto = $getUserTransformer->transform();
                return $this->response->buildOkResponse($userDto);
            } else {
                throw new NotFoundException('No results found');
            }
        } catch (Throwable $e) {
            throw new SystemException($e->getMessage());
        }
    }

4. It is very important to design your code in such a way that your functions do one thing only. Sometimes when you are writing code for business logic, you will need to evaluate conditions or loop through a collection of items, but always keep consistency with this rule. As best practice, the less arguments that you inject in function, the better, and avoid at all cost returning different object types if them are not exceptions.

This is totally wrong:

    /**
     * @param int $id
     * @return ApiResponse|mixed|void
     */
    public function getTheseguys(int $id)
    {
        try {
            $user = $this->em->getRepository(User::class)->find($id);
            if ($user) {
                //found a user..  return User Dto
                $getUserTransformer = new GetUserTransformer($user);
                $userDto = $getUserTransformer->transform();
                return $this->response->buildOkResponse($userDto);
            }
            $client = $this->em->getRepository(Client::class)->find($id);
            if ($client) {
                //found a client..  return Client Dto
                $getUserTransformer = new GetClientformer($client);
                $clientDto = $getUserTransformer->transform();
                return $this->response->buildOkResponse($clientDto);
            }
            $vendor = $this->em->getRepository(Vendor::class)->find($id);
            if ($vendor) {
                //found a vendor..  return Vendor Dto
                $getUserTransformer = new GetVendorformer($vendor);
                $vendorDto = $getUserTransformer->transform();
                return $this->response->buildOkResponse($vendorDto);
            }
        } catch (Throwable $e) {
            throw new SystemException($e->getMessage());
        }
    }

 Using a function to return 3 different data types will make the calling function to deal with 3 different data structures and it might cause data inconsistencies.

 5. Every time you write code, think that this code will exist for long time, maybe years or decades. So always be very explicit on the intention of each line. Put comments where it make sense, give your variables and functions a name that helps understand what is doing, so in the future other developers may understand your code easily. Avoid as much as possible using things like:

//...
call_user_func($callableNamePassedObtained3LevelsUp,$args);

//Perhaps a better alternative to make it more understandable in the future -- but also wrong
switch ($callableNamePassedObtained3LevelsUp) {
    case 'itsA':
        //...
        $this->serviceA->itsA($args);
        break;
    case 'itsB':
        //...
        $this->serviceB->itsB($args);
        break;
    case 'itsC':
        //...
        $this->serviceC->itsC($args);
        break;
}

 No matter that you use Switch to explicit have the name of each method required based on the value of the evaluated variable, this implementation is much more difficult to write PHP Unit Tests and also, as each called function may or may not return different things, its very likely that this application will have inconsistencies and errors in the future.

 

 

 

 

 

 

 

 

 

 

 

 

Understanding key Object Oriented Programming concepts is required to be able to produce Modern, High Quality PHP Software Applications that stick to OOP Best Practices and Principles.

Prerequisites:

As I mentioned in past Article, Objects are instances of a Class that act as a template and provides all the properties and methods for the object to use.

Objects are very useful for encapsulating, organize and reuse code. When building a modern Enterprise Class PHP Software Application, leveraging the power of OOP is key.

When you start a new Application, a new Module or a new Feature, your code must respond to Client Requests or to Events happening. Clients can be either Users, or other Applications. Events can be things like a cron job set to run daily at 11 pm.

To facilitate the implementation process, there are OOP Software Design Patterns1 that are accepted as Standard models to follow in the production of new Code.

Basically, the most important things to keep in mind when developing new code are:

  1. Keep things well organized.
  2. Always use one file for one class.
  3. Use Dependency Injection instead of Class Inheritance as much as possible.
  4. Group Classes in folders based on the main type or functionality.
  5. When developing in a PHP Framework like Symfony, from the entry point of a Request where you start coding, to the exit point with a Response, do not make your code to jump from one method to another more than 3 ~ 5 times, and do not make these jumps to be in more than 3~4 files.
  6. Always use type casting and doc blocks with annotations. This will will help in building a very robust Enterprise Grade Application and also will help your IDE Intelli-Sense to better index and understand you code, facilitating finding and auto-completion.
  7. Implement your code in such way  that each function does only one thing. It is also a must if you are required to implement PHP Unit Testing or building a TDD Application.

 

1. Keeping things well organized is very important for high quality code. The best way to achieve this is to follow industry proven patterns. If you are building Mini Services2 in REST API mode my recommendation for folder Structure is:

 

 There is a main ApiModule that will have all class files that are used by the other Modules, are being used by the System through the Request-Response cycle and other particular actions-events that are not part of any of the other Modules.

All other Modules like the ClientsModule shown above need no more than these folders.

If you plan to use Trait classes, then you may want to put the folder in the ApiModule.

Follow the Mini Services Architecture Part 1 section for more on this.

 

2. The times where we put thousands of lines of code in one single file are long gone. Modern PHP development requires to have a File named the same as its Class, so autoloading and namespaces can be handled in a very efficient way by composer.

Also it is very important to have the code well organized among all different Class files in a project. This is a sample Entity Class for the Zoo project i used in other Articles:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
 * Zoo
 *
 * @ORM\Table(name="zoo")
 * @ORM\Entity
 */
class Zoo
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string|null
     *
     * @ORM\Column(name="zoo_name", type="string", length=45, nullable=true)
     */
    private $zooName;
    
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Animals", mappedBy="zoo", cascade={"persist", "refresh", "remove"}, orphanRemoval=true)
     */
    private $animals;
   
    public function __construct()
    {
        $this->animals = new ArrayCollection();
    }
    
    
    /**
     * @return int|null
     */
    public function getId(): ?int
    {
        return $this->id;
    }
    
    /**
     * @return string|null
     */
    public function getZooName(): ?string
    {
        return $this->zooName;
    }
    
    /**
     * @param string|null $zooName
     * @return $this
     */
    public function setZooName(?string $zooName): self
    {
        $this->zooName = $zooName;

        return $this;
    }

    /**
     * @return Collection<int, Animals>
     */
    public function getAnimals(): Collection
    {
        return $this->animals;
    }
    
    /**
     * @param Animals $animal
     * @return $this
     */
    public function addAnimal(Animals $animal): self
    {
        if (!$this->animals->contains($animal)) {
            $this->animals->add($animal);
            $animal->setZoo($this);
        }

        return $this;
    }
    
    /**
     * @param Animals $animal
     * @return $this
     */
    public function removeAnimal(Animals $animal): self
    {
        if ($this->animals->removeElement($animal)) {
            // set the owning side to null (unless already changed)
            if ($animal->getZoo() === $this) {
                $animal->setZoo(null);
            }
        }

        return $this;
    }
    
}

And the file:

 As mentioned in 1. keeping thing well organized is key. In this case Zoo Class is an Entity Class so it lives in the Entity Folder.

 

 3. Class inheritance is one of the main characteristics of OOP. However, in modern development we have Dependency Injection available and if working with Symfony framework, DI works like magic when auto-wiring and auto-configure are enabled:

    /**
     * UsersService constructor.
     * @param EntityManagerInterface $em
     * @param CustomValidationServiceProvider $customValidation
     * @param ApiResponseTransformer $apiResponse
     * @param ValidatorInterface $validator
     * @param UserPasswordEncoderInterface $passwordEncoder
     * @param ContainerBagInterface $params
     * @param ApiGuzzleClient $client
     * @param DomainRolesService $domainRolesService
     * @param PiiCryptoService $piiCryptoService
     * @param Environment $twig
     * @param AuthorizationManager $authorizationManager
     */
    public function __construct(
        EntityManagerInterface $em,
        CustomValidationServiceProvider $customValidation,
        ApiResponseTransformer $apiResponse,
        ValidatorInterface $validator,
        UserPasswordEncoderInterface $passwordEncoder,
        ContainerBagInterface $params,
        ApiGuzzleClient $client,
        DomainRolesService $domainRolesService,
        PiiCryptoService $piiCryptoService,
        Environment $twig,
        AuthorizationManager $authorizationManager
    )
    {
        $this->em = $em;
        $this->validator = $validator;
        $this->customValidation = $customValidation;
        $this->response = $apiResponse;
        $this->passwordEncoder = $passwordEncoder;
        $this->params = $params;
        $this->httpClient = $client;
        $this->domainRolesService = $domainRolesService;
        $this->piiCryptoService = $piiCryptoService;
        $this->twig = $twig;
        $this->authorizationManager = $authorizationManager;
    }

 In this case, the above code is part of a Users Service Class that is in charge of all Business Logic related to Users. Each one the methods in each one of these Dependencies are available to be used just by doing something like this:

//Execute a Curl Post request using the httpClient
$response = $this->httpClient->getClient()->request('POST', $url, ['headers' => $headers, 'form_params' => $formParams]);


//Use the PII Crypto Service to hash the User Email
//Then use Entity Manager to query for this User in the DB
$user = $this->em->getRepository(User::class)->findOneBy(['email' => $this->piiCryptoService->hashData(base64_decode($email))]);

Also, these Dependencies can be injected in many other Classes. With this we follow PHP Best Practices and Stick to SOLID Principles.

 

 

Avoid as much as possible implementations like this:

<?php

//c.php
Class C
{
    private $someCProperty;
    
    public function __construct($params)
    {
        $this->someCProperty = $params->someCProperty;
    }
    
    public function car($c)
    {
        return 'this car';
    }
    
}


//b.php
Class B extends C
{
    public function __construct($params)
    {
        $paramsForC = $this->getParamsForCconstructor($params);
        parent::__construct($paramsForC);
    }
    
    private function getParamsForCconstructor($params)
    {
        return $params->someOtherPropertiesForC;
    }
    
    public function cars($b, $c)
    {
        return parent::car($c) . 'cars from b' . $b;
    }
}


//a.php
Class A extends B
{
    private $someProperty;
    
    
    public function __construct($params)
    {
        $paramsForB = $this->getParamsForBconstructor($params);
        parent::__construct($paramsForB);
        $this->someProperty = $params->someProperty;
    }
    
    private function getParamsForBconstructor($params)
    {
        return $params->someOtherPropertiesForB;
    }
    
    public function AvailableCars($a, $b, $c)
    {
        return parent::cars($b, $c);
    }
    
}

//some-other-file.php

$carsFromA = new A($formParams);

$availableCars = $carsFromA->AvailableCars($a, $b, $c);

$carsFromBandC = new B($paramsForBnC);

$availableCarsFromBnC = $carsFromBandC->cars($b, $c);














Such implementations are leveraging the power of Class Inheritance, but doing this, you will end with a very messy code, very complicated to understand and very susceptible to  fail every time an update or a new feature is added.

In the next Article, I will keep explaining the next recommendations.

 

 

 

1 For more info on this refer to:

2 Refer to my Articles here: