All Projects → BitBagCommerce → BitBagBible

BitBagCommerce / BitBagBible

Licence: MIT license
BitBag coding standards that rock!

Coding bible

This project follows PSR-4 coding standards and those recommended by Sylius and Symfony projects in this order. It is extended based on the experience of the whole BitBag team for everybody's sake.

Code Style

  1. Always follow PSR-4 recommendations;
  2. $elements = [1, 2, 3]; instead of $elements = array(1, 2, 3);
  3. Don't use annotations. Don't mess up the definition with implementation;
  4. Use Yoda-Style comparisons null === $var->getResult($anotherVar) instead of $var->getResult($anotherVar) === null
  5. Don't use PHPDoc. Use it only when it is REALLY valuable and in interfaces that return array of objects or collections, like:
interface Foo
{
    /**
     * @return Collection|ItemInterface[]
     */
    public function getItems(): Collection;
}
  1. Use inline PHPDoc only for fields inside the class, like:
final class Foo
{
    /** @var int */
    private $foo;
    
    /** @var string */
    private $bar;
    
    public function getFoo(): ?int
    {
        return $this->foo;
    }
    
    public function getBar(): ?string
    {
        return $this->bar;
    }
}
  1. Keep a blank line above the @return method definition in case it has more than @return annotation, for instance
interface Foo
{
    /**
     * @param string $key some valid and important comment
     *
     * @return Collection|ItemInterface[]
     */
    public function getItemsWithoutKey(string $key): Collection;
}
  1. Always use strict types declaration in each class header, like:
<?php

declare(strict_types=1);

namespace Foo\Bar;

final class Foo
{
}
  1. A method must not have more than two parameters inline. Otherwise, split them with \n. In an edge-case where two parameters are too long to fit your (and potentially your colleagues) screen, split them as well. Examples:
public function foo(string $firstParam, string $secondParam): void;

public function bar(
    FirstParamInterface $firstParam, 
    SecondParamInterface $secondParam,
    ThirdParamInterface $thirdParam
): void;

public function fooBarIsALongMethodName(
    WithEvenALongerParameter $firstParam,
    AndASecondParameterThatIsNotShorter $secondParameter
): void;
  1. Always use a trailing comma in arrays
$flavors = [
   'chocolate',
   'vanilla',
];
  1. Good to follow practices from https://mnapoli.fr/approaching-coding-style-rationally/
  2. Once you use PHPStorm (and yes, you do if you work at BitBag), you can open your IDE preferences (PHPStorm -> Preferences) and search for File and Code Templates. PHP Class Doc Comment, PHP File Header, PHP Interface Doc Comment are those templates that should at least be customized.
  3. Always use Easy Coding Standard library for code cleanup. We use one from the official Sylius Labs repository. If you start the project from scratch, use PHPStan. We use the official Sylius setup. Both ECS and PHPStan should be included in the CI process.

General

  1. No /.idea and other local config files in .gitignore. Put them into a global gitignore file, read more on https://help.github.com/articles/ignoring-files/#create-a-global-gitignore.
  2. We are working on NIX systems and we don't like Windows nor are we solving its existence goal and other problems.
  3. Code that is not documented doesn't exist. Writing documentation of a bundle/plugin/project is part of the development process. Remember that in the end, someone else is going to use your code who might not know each part of it. This also applies to writing Github repository descriptions, basic composer package information, etc.

Symfony / Sylius / Frameworks

  1. Use YAML (*.yaml) for defining routings and configs;
  2. Use XML (*.xml) for defining services, doctrine, and validation definitions;
  3. For services definitions in a single bundle use form.xml, event_listener.xml, etc. Don't put everything in the services.xml file, do it in public projects with only a few services. If you have more than one type of service inside your app, create a separate config file under the services/ directory.
  4. Repositories and Entities in public projects should not (and cannot) be defined as final.
  5. Entity fields in public projects (vendors) should be protected instead of private.
  6. Decorate resource factories with decoration pattern and do not call resource instance with new keyword directly. Instead, inject resource factory into the constructor and call createNew() on it. See Sylius\Component\Product\Factory\ProductFactory, sylius.custom_factory.product service definition and Symfony Service Decoration. The priority flag we are starting with equals 1 and is increased by one for each other decoration.
  7. For customizing forms use Symfony Form Extension.
  8. We follow command pattern implemented in SyliusShopApiPlugin. This means we use the same bus libraries and similar Command, CommandHandler, ViewRepository, ViewFactory, View approach.
  9. Creating a CLI Command using Symfony Console Component should follow the following rules:
    • execute method should have int as a return type. For the successful run, the command should return 0. For any errors during execution, the return can be 1 or any different error code number.
  10. In Sylius plugins, use traits for customizing models and use them inside your tests/Application/src for testing. This way we avoid handling reference conflicts in the final app.
  11. We don't use either autowire nor autoconfigure Symfony options as it is a very "magic" way of defining services. We always prefer to manually define services and inject proper arguments into them to have better control of our Container.
  12. If some of the service definition is tagged, don't use FQCN (Fully Qualified Class Name) as the service id.
  13. Don't use Sylius theme if you have one template in your project.

Testing

  1. Before you implement any new functional feature, write Behat scenario first (Gherkin, *.feature file).
  2. After writing the scenario, write a proper scenario execution (Contexts, Pages).
  3. Use Behat Contexts that are divided into Hooks - generic app Background, Setup specific resource background, Ui - specific interaction.
  4. Before starting implementing new functional code, make sure all your core logic is covered with PHPSpec (code without framework dependencies, like Commands, Forms, Configuration, Fixtures, etc.)
  5. PHPSpecs are always final classes with functions without public visibility and : void return type:
final class ProductSpec extends ObjectBehavior
{
    function it_follows_bitbag_coding_standards(): void
    {
        Assert::true($this->followsStandards());
    }
}

OOP / Architecture

  1. Make your code as simple as it's possible (follow single responsibility principle and KISS principle).
  2. Use interfaces for any core logic class implementation, especially Models and Services (so that you follow a single responsibility principle).
  3. Use final any time it is possible (in order to avoid infinite inheritance chain, in order to customize some parts use Decorator and Dependency Injection patterns).
    • The only exception to this rule is only for a framework/library specific requirements. I.e Doctrine Entities cannot be final classes because of reflection issues.
  4. Be more careful when you think Singleton is something you need in the project. If it is you should go and rethink the code.
  5. Be careful with static statement, probably you will never need to use it.
  6. Use ADR pattern for controllers. For instance, your controller should not extend any class and contain just an __invoke method. It should also be suffixed with Action keyword.
<?php

declare(strict_types=1);

namespace App\Controller\Action;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Repository\FooRepositoryInterface;

final class SayHelloToTheWorldAction
{
    /** @var FooRepositoryInterface */
    private $fooRepository;

    public function __construct(FooRepositoryInterface $fooRepository)
    {
        $this->fooRepository = $fooRepository;
    }

    public function __invoke(Request $request): Response
    {
        return new Response("Hello world!");
    }
}

Workflow

  1. Commit messages should be written (if only it's possible) with the following convention: [Project spec state][Bundle] max 64 characters description in english written in Present Simple.
  2. If there is an opened issue on Jira for a specific task, your branch should be named sit_[ISSUE_NUMBER]. If not, it should be named with the first letter of your name and your surname. In my case (Mikołaj Król) it would be mkrol.

Open Source

  1. Open source is made by forks if only more than one person is in charge of maintenance of specific package.
  2. We follow http://docs.sylius.org/en/latest/contributing/ contribution standards
  3. Any *.php file created by the BitBag developer (in Open Source project) needs to have at least the following definition where the author is the user who created this file:
<?php

/*
 * This file has been created by developers from BitBag.
 * Feel free to contact us once you face any issues or want to start
 * You can find more information about us on https://bitbag.io and write us
 * an email on [email protected].
 */

 declare(strict_types=1);
 
 namespace Foo;
 
 use Foo\Bar\App;
 
 final class Bar implements BarInterface
 {
     public const SOME_CONST = 'foo';
     public const SOME_OTHER_CONST = 'bar';
     
     /** @var string */
     private $someProperty;
    
     public function inheritedMethod(): string
     {
         //some body
     }
     
     private function someFunction(SomeServiceInterface $someService): ?NullOrInterfacedObject
     {
         $items = $someService->getCollection();
         
         /** @var WeUseThisBlockDefinitionIfNecessaryOfCourseWithInterface $someOtherProperty */
         $someOtherProperty = $someService->getSomething();
         // Use break line between any operation in your code. Imagine the code as block diagram, where every new line is an arrow between operations.
         foreach ($items as $item) {
             $item->doSomething();
             
             if (false === $item->getProperty()) { // Always use strict comparison with expected result on the left
                 return;
             }
             
             continue;
         }
         
         $someService->someOutputAction();
         
         $this->someProperty->someOtherOutputAction();
         
         return $someOtherProperty;
     }
 }

Git

-1. Not confident with Git yet? Visit the simple guide! 0. Use Gitflow whenever you can:

  1. How to write a commit message and use Git in a proper way? Read here
  2. If you use Git in IDE - make sure it follows all standards. We don't care what GUI/CLI you use as long as you know what happens under the hood.

Be smart and keep in mind that once you do something stupid, I will find you and I will force you to work with Laravel or Magento. There is nothing called a stupid question, but please ask it in a smart way :). It's better to talk about a feature with the whole team for 30 minutes than lose 8 hours on implementing some dummy code that will destroy the current codebase order and deep the technical debt.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].