Here you’ll find the code guidelines which you need to respect when developing at CollectiShop.
Functions
<?php
// Every function has a clear name, type hints and return types
function randomFunction(int $parameter): float
{
return round(($parameter / 100), 2);
}
Returning
<?php
// A function returns as quick as possible
function validateField(Field $field): bool
{
if (empty($field->getName())) {
return false;
}
if ($field->getType() === "email" && !filter_var($field->getType(), FILTER_VALIDATE_EMAIL)) {
return false;
}
return $field->isValid();
}
One responsability
Example of bad code
<?php
// Every function has its own responsability, so this is a bad
// example
function getPrice(float $price, bool $format = false): float|string
{
if ($format && floor($price) === $price) {
return "\$ " . number_format($price, 0) . ".-";
} elseif ($format) {
return "\$ " . number_format($price, 2);
}
return $price;
}
Example of good code
<?php
// Every function has its own responsability, so this is a good
// example
function getPrice(): float
{
return $this->price;
}
function getFormattedPrice(): string
{
if (floor($price) === $price) {
return "\$ " . number_format($price, 0) . ".-";
}
return "\$ " . number_format($price, 2);
}
Use of constants
Bad example
<?php
class Foo extends Bar
{
public function isRemovable(): bool
{
if (in_array($this->getId(), [1,2,3])) {
return false;
}
return parent::isRemovable();
}
}
Good example
<?php
class Foo extends Bar
{
public const FIRSTPAGE_ID = 1;
public const SECONDPAGE_ID = 2;
public const THIRDPAGE_ID = 3;
public const PAGEID_LIST = [
self::FIRSTPAGE_ID,
self::SECONDPAGE_ID,
self::THIRDPAGE_ID
];
public function isRemovable(): bool
{
if (in_array($this->getId(), self::PAGEID_LIST)) {
return false;
}
return parent::isRemovable();
}
}
Avoid (unnecessary) “else”
Bad example
<?php
class Foo extends Bar
{
public function validateData(array $data): bool
{
if (empty($data)) {
$isValid = false;
} else {
$isValid = true;
}
if (!isset($data["success"]) || $data["success"] === false) {
$isValid = false;
$success = false;
} else {
$success = true;
}
return ($isValid && $success);
}
}
Good example
<?php
class Foo extends Bar
{
public function validateData(array $data): bool
{
$isValid = false;
$success = false;
if (!empty($data)) {
$isValid = true;
}
if (isset($data["success"]) && $data["success"] === true) {
$success = true;
}
return ($isValid && $success);
}
}
Avoid (unnecessary) multilevel if-statements
Bad example
<?php
class Foo extends Bar
{
public function validateAddress(array $address): bool
{
$isValidAddress = false;
if (!empty($address)) {
if (!empty($address["zipcode"])) {
if (!empty($address["housenumber"])) {
$isValidAddress = true;
}
} else {
if (!empty($address["addressline1"])) {
if (!empty($address["addressline2"])) {
$isValidAddress = true;
}
}
}
}
return $isValidAddress;
}
}
Good example
<?php
class Foo extends Bar
{
public function validateAddress(array $address): bool
{
if (empty($address)) {
return false;
}
if (empty($address["zipcode"]) || empty($address["housenumber"])) {
return $this->validateAddressByAddressLines($address);
}
return true;
}
protected function validateAddressByAddressLines(array $address): bool
{
if (empty($address)) {
return false;
}
if (empty($address["addressline1"]) || empty($address["addressline2"])) {
return false;
}
return true;
}
}
Make use of correct comparison operators
<?php
class RaceCar extends Car
{
public function getInfo(): string
{
return "I'm a racecar";
}
}
class Car extends Vehicle
{
public function getInfo(): string
{
return "I'm a car";
}
public function setWheels(int $wheels): Car
{
if ($wheels >= 4) {
$this->wheels = $wheels;
}
throw new VehicleWheelsException("A car has at least 4 wheels");
}
public function setPerformance(int $performance): Car
{
return match ($performance <=> 600) {
-1 => $this,
0 => $this,
1 => new RaceCar(),
}
}
public function getAverageMilesDriven(int ...$milesDriven): int
{
return (array_sum($milesDriven) / count($milesDriven));
}
public function getMilesDriven(): int
{
return $this->milesDriven ?? 0;
}
public function getDistanceDriven(): int
{
return ($this->milesDriven ?: ($this->kilometersDriven ?? 0));
}
}
Preference for passing objects above plain data types
Bad example
<?php
class Foo extends Bar
{
public function __construct(int $id, string $title, string $slug)
{
...
}
}
$newsArticle = new NewsArticle();
$foo = new Foo(
$newsArticle->getId(),
$newsArticle->getTitle(),
$newsArticle->getSlug()
);
Good example
<?php
class Foo extends Bar
{
public function __construct(NewsArticle $newsArticle)
{
...
}
}
$newsArticle = new NewsArticle();
$foo = new Foo($newsArticle);
Even better example
<?php
interface NewsArticleInterface
{
public function getId(): int;
public function getTitle(): string;
public function getSlug(): string;
}
class Foo extends Bar
{
public function __construct(NewsArticleInterface $newsArticle)
{
...
}
}
$newsArticle = new NewsArticle();
$foo = new Foo($newsArticle);
Use helper objects instead of global variables
Bad example
<?php
$title = (string)($_POST["title"] ?? "");
$referer = (string)($_SESSION["referer"] ?? "");
$search = (string)($_REQUEST["search"] ?? "");
Good example
<?php
$title = Request::getPost("title");
$referer = Session::get("referer");
$search = Request::getRequest("search");
Use an ORM instead of plain queries
<?php
public function getNewsArticlesBySearch(string $query): array
{
$queryBuilder = new QueryBuilder();
$queryBuilder->select(["id", "title", "slug"])
->from(NewsArticle::table)
->where("title LIKE ?", "%" . $query . "%");
return $queryBuilder->fetchResults(NewsArticle::class);
}
Keep functions small
Bad example
<?php
class Order
{
public function getTotal(): float
{
$total = 0.00;
foreach ($this->getOrderLines() as $orderLine) {
$total += ($orderLine->getPrice() * $orderLine->getAmount());
}
foreach ($this-getShippingCosts() as $shippingCost) {
$total += $shippingCost->getPrice();
}
foreach ($this->getPaymentCosts() as $paymentCost) {
$total += $paymentCost->getPrice();
}
foreach ($this-getDiscounts() as $discount) {
if ($discount->hasDiscountPercentage()) {
$total -= 100 - (($total / 100) * $discount->getPercentage());
} else {
$total -= $discount->getPrice();
}
}
$vatTotal = 0.00;
foreach ($this->orderLines() as $orderLine) {
$vatTotal += ($orderLines->getPrice() * $orderLines->getAmount()) * $orderLine->getVatPercentage();
}
foreach ($this-getShippingCosts() as $shippingCost) {
$vatTotal += ($shippingCost->getPrice() * $shippingCost->getVatPercentage());
}
foreach ($this->getPaymentCosts() as $paymentCost) {
$vatTotal += ($paymentCost->getPrice() * $paymentCost->getVatPercentage());
}
foreach ($this-getDiscounts() as $discount) {
if ($discount->hasDiscountPercentage()) {
$vatTotal -= (100 - (($total / 100) * $discount->getPercentage())) * $discount->getVatPercentage();
} else {
$vatTotal -= ($discount->getPrice() * $discount->getVatPercentage());
}
}
$total += $vatTotal;
return $total;
}
}
Good example
<?php
class Order
{
public function getSubTotal(): float
{
$subTotal = 0.00;
foreach ($this->getOrderLines() as $orderLine) {
$subTotal += $orderLine->getTotal();
}
return $subTotal;
}
public function getShippingTotal(): float
{
$total = 0.00;
foreach ($this-getShippingCosts() as $shippingCost) {
$total += $shippingCost->getTotal();
}
return $total;
}
public function getPaymentTotal(): float
{
$total = 0.00;
foreach ($this-getPaymentCosts() as $paymentCost) {
$total += $paymentCost->getTotal();
}
return $total;
}
public function getDiscountTotal(float $total): float
{
$total = 0.00;
foreach ($this-getDiscounts() as $discount) {
$total += $discount->getDiscountByTotal($total);
}
return $total;
}
public function getVatTotal(): float
{
$vatCalculator = new VatCalculator();
$total = $vatCalculator->getSubTotalVat();
$total += $vatCalculator->getShippingTotalVat();
$total += $vatCalculator->getPaymentTotalVat();
$total -= $vatCalculator->getDiscountTotalVat($total);
return $total;
}
public function getTotal(): float
{
$total = $this->getSubTotal();
$total += $this->getShippingTotal();
$total += $this->getPaymentTotal();
$total -= $this->getDiscountTotal($total);
$total += $this->getVatTotal();
return $total;
}
}