Chapterبەش 6 · Lectureوانە 6.2

Inheritance, Interfaces and Traitsبۆماوە، نێوبەستەکان و ترەیتەکان

Building classes on other classes in PHP: extends and the "is a" test, parent::__construct(), overriding with parent:: and #[Override], protected, final, abstract classes, interfaces (implements, several at once), polymorphism through an interface, traits, enums (PHP 8.1) in brief, and an overview of namespaces, autoloading with spl_autoload_register() and Composer. A worked example builds a bazaar till that takes cash, card and mobile-wallet payments. Every example runs on PHP 8.2 and works the same on 8.3, 8.4 and 8.5.

بنیاتنانی لەسەر تر لە PHPـدا: extends و تاقیکردنەوەی «جۆرێکە لە»، ⁦parent::__construct()⁩، بە parent:: و ⁦#[Override]⁩، protected، final، ، (implements، چەندانێک پێکەوە)، لە ڕێگەی ، ، ⁦(PHP 8.1)⁩ بە کورتی و تێڕوانینێکی گشتی بۆ ، بە ⁦spl_autoload_register()⁩ و Composer. نموونەیەکی کارپێکراو شوێنی پارەدانێکی بازاڕ دروست دەکات کە پارەی نەختینە، کارت و جزدانی مۆبایل وەردەگرێت. هەموو نموونەیەک لەسەر ⁦PHP 8.2⁩ کار دەکات و لەسەر ⁦8.3⁩، ⁦8.4⁩ و ⁦8.5⁩ هەمان شتە.

26 min read23 خولەک خوێندنەوە · 19 stepsهەنگاو

What you will learnچی فێر دەبیت

  • Build a child class with extends, call parent::__construct() and override methods correctly.
  • Choose between public, protected, private and final inside a class family.
  • Write abstract classes and interfaces, and decide which one a problem needs.
  • Use polymorphism through an interface so that new classes plug in without changing old code.
  • Use a trait and a backed enum, and explain namespaces, autoloading and Composer.
  • بە extends دروست بکەیت، ⁦parent::__construct()⁩ بانگ بکەیت و بە دروستی دووپاتە پێناسە بکەیتەوە.
  • لە ناو خێزانێکی لە نێوان public، protected، private و final هەڵبژێریت.
  • و بنووسیت و بڕیار بدەیت کێشەیەک کامیانی پێویستە.
  • لە ڕێگەی بەکار بهێنیت بۆ ئەوەی نوێ بێ گۆڕینی کۆدی کۆن بچنە ناوەوە.
  • و نرخدار بەکار بهێنیت و ، و Composer ڕوون بکەیتەوە.

Why inheritance?بۆچی بۆماوە؟

The bazaar shop now sells more than jars on a shelf. A gift card has a code; apples are sold by the kilo. Copying the whole Product class into GiftCard and FreshProduct would repeat the same code three times, and every fix would have to be made three times. solves this: a (child class) extends a (parent class) and receives its members, then adds or changes only what is different.

دووکانی بازاڕەکە ئێستا تەنیا قوتووی سەر ڕەفە نافرۆشێت. کارتی دیاری کۆدێکی هەیە؛ سێو بە کیلۆ دەفرۆشرێت. کۆپیکردنی هەموو Product بۆ ناو GiftCard و FreshProduct هەمان کۆد سێ جار دووپاتە دەکاتەوە و هەر چاککردنێک دەبێت سێ جار بکرێت. ئەمە چارەسەر دەکات: بە extends لە دروست دەبێت و بەشەکانی وەردەگرێت، پاشان تەنیا ئەوەی جیاوازە زیاد دەکات یان دەیگۆڕێت.

Use the "is a" test before you inherit: a gift card is a product, so GiftCard extends Product fits. A cart is not a product; it has products, so the cart of the last lesson holds Product objects instead (). The general idea is also taught in Java and Python; this lesson shows PHP's way.

پێش تاقیکردنەوەی «جۆرێکە لە» بەکار بهێنە: کارتی دیاری جۆرێکە لە بەرهەم، بۆیە GiftCard extends Product دەگونجێت. سەبەتە جۆرێک لە بەرهەم نییە؛ بەرهەمی تێدایە، بۆیە سەبەتەکەی وانەی پێشوو لەبری ئەوە Product هەڵدەگرێت (). بیرۆکە گشتییەکە لە Java و Pythonـیشدا دەخوێندرێت؛ ئەم وانەیە ڕێگای PHP پیشان دەدات.

A class diagram. At the top the parent class (superclass) Product with + $name, # $price and + label(). Below it two child classes (subclasses) joined to it by lines marked extends with a hollow triangle: GiftCard, which adds + code() (new), and FreshProduct, which adds + $kg (new) and + label() (changed). A small key: + public, # protected. Text: a GiftCard is a Product, a FreshProduct is a Product; a child class gets the public and protected members of its parent, then adds or changes some.هێڵکارییەکی کلاس. لە سەرەوە کلاسی باوکەکە Product بە + $name، # $price و + label(). لە خوارەوەی دوو کلاسی منداڵ بە هێڵی extends و سێگۆشەیەکی بەتاڵ پێیەوە بەستراون: GiftCard کە + code() (نوێ) زیاد دەکات و FreshProduct کە + $kg (نوێ) و + label() (گۆڕاو) زیاد دەکات. کلیلێکی بچووک: + public، # protected. دەق: GiftCard و FreshProduct هەردووکیان جۆرێکن لە Product؛ کلاسێکی منداڵ بەشە public و protectedـەکانی کلاسی باوکەکە وەردەگرێت، پاشان هەندێکیان زیاد دەکات یان دەیانگۆڕێت.
A family of classes: one parent, two children.خێزانێک لە : یەک باوک، دوو منداڵ.CêIT

extends: building on a parent classextends: بنیاتنان لەسەر کلاسێکی باوک

Write class GiftCard extends Product. The child receives every public and protected property and method of the parent. If the child has no constructor of its own, PHP uses the parent's, so new GiftCard("Bookshop card", 25000) fills name and price. A class may extend only one parent (single inheritance).

class GiftCard extends Product بنووسە. هەموو و public و protectedـی وەردەگرێت. ئەگەر خۆی نەبێت، PHP هی باوکەکە بەکار دەهێنێت، بۆیە new GiftCard("Bookshop card", 25000) name و price پڕ دەکاتەوە. تەنیا دەتوانێت لە یەک extends بکات ( تاک).

GiftCard extends ProductGiftCard لە Product extends دەکاتextends.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
class Product
{
    public function __construct(
        public string $name,
        protected int $price,
    ) {}

    public function label(): string
    {
        return $this->name . ": " . number_format($this->price) . " IQD";
    }
}

class GiftCard extends Product              // a GiftCard is a Product
{
    public function code(): string          // a new method only gift cards have
    {
        return strtoupper(substr($this->name, 0, 3)) . "-" . $this->price;
    }
}

$card = new GiftCard("Bookshop card", 25000);   // uses Product's constructor
echo $card->label(), "\n";                     // inherited from Product
echo $card->code(), "\n";                      // its own method
var_dump($card instanceof Product);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 6

protected: hidden from outside code, but open to child classes.

protected: لە کۆدی دەرەوە شاراوەیە، بەڵام بۆ کراوەیە.

Lineدێڕی 15

extends Product: GiftCard gets name, price and label().

extends Product: GiftCard name، price و ⁦label()⁩ وەردەگرێت.

Lineدێڕی 19

The child may use the protected $price in its own method.

دەتوانێت $priceـی protected لە خۆیدا بەکار بهێنێت.

Lineدێڕی 23-26

Product's constructor and label() work for a GiftCard; instanceof Product is true.

و ⁦label()⁩ـی Product بۆ GiftCardـێک کار دەکەن؛ instanceof Product ڕاستە.

Left, the class Product with public $name, protected $price, private $cost and public label(). An arrow marked extends goes to GiftCard on the right, which shows: $name with a tick, use it anywhere; $price with a tick, use it inside GiftCard; $cost with a cross, it exists but GiftCard cannot reach it; label() with a tick, use it anywhere; and + code(), its own new method. Bottom: public and protected members are inherited, private ones stay locked inside Product; reading $this->cost inside GiftCard gives Warning: Undefined property.لای چەپ، کلاسی Product بە public $name، protected $price، private $cost و public label(). تیرێک کە extendsـی لەسەرە بۆ GiftCard لای ڕاست دەچێت کە پیشان دەدات: $name بە نیشانەی ڕاست، لە هەموو شوێنێک بەکاری بهێنە؛ $price بە نیشانەی ڕاست، لە ناو GiftCard بەکاری بهێنە؛ $cost بە خاچ، هەیە بەڵام GiftCard ناگاتێ؛ label() بە نیشانەی ڕاست، لە هەموو شوێنێک بەکاری بهێنە؛ و + code()، میتۆدێکی نوێی خۆی. خوارەوە: بەشە public و protectedـەکان بۆماوە دەکرێن، privateـەکان لە ناو Product قفڵ دەمێننەوە؛ خوێندنەوەی $this->cost لە ناو GiftCard هۆشداریی Undefined property دەدات.
What a child class receives. چی وەردەگرێت.CêIT

extends: building on a parent classextends: بنیاتنان لەسەر کلاسێکی باوک

parent::__construct(): building the parent's part⁦parent::__construct()⁩: دروستکردنی بەشی کلاسی باوکەکە

FreshProduct needs its own constructor, because it takes a price per kilo and a weight. When a child writes its own constructor, PHP does not call the parent's by itself. Call it with parent::__construct(...), usually as the first line, so the Product part of the object is built. (Java calls super(...) automatically; PHP never does.)

FreshProduct خۆی پێویستە، چونکە نرخی کیلۆیەک و کێشێک وەردەگرێت. کاتێک خۆی دەنووسێت، PHP خۆی باوک بانگ ناکات. بە parent::__construct(...) بانگی بکە، زۆرجار وەک یەکەم دێڕ، بۆ ئەوەی بەشی Productـی دروست بێت. (Java خۆی super(...) بانگ دەکات؛ PHP هەرگیز.)

FreshProduct calls the parent's constructorFreshProduct دروستکەری باوک بانگ دەکاتparent_construct.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
class Product
{
    public function __construct(
        public string $name,
        protected int $price,
    ) {}

    public function label(): string
    {
        return $this->name . ": " . number_format($this->price) . " IQD";
    }
}

class FreshProduct extends Product         // sold by the kilo
{
    public function __construct(
        string $name,
        int $pricePerKg,
        public float $kg,
    ) {
        parent::__construct($name, (int) round($pricePerKg * $kg));
    }
}

$apples = new FreshProduct("Apples", 3000, 1.5);
echo $apples->label(), "\n";
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 17-21

The child's own parameters; $kg is promoted to a property.

خودی ؛ $kg بەرز دەکرێتەوە بۆ .

Lineدێڕی 22

Send the name and the price (3000 × 1.5 = 4500) up to Product.

ناوەکە و نرخەکە ⁦(3000 × 1.5 = 4500)⁩ بۆ Product بنێرە.

Without that line, the Product part stays empty, and the first read of $this->name stops the script:

بەبێ ئەو دێڕە، بەشی Product بەتاڵ دەمێنێتەوە و یەکەم خوێندنەوەی $this->name ڕادەگرێت:

parent::__construct() is missing⁦parent::__construct()⁩ نییەparent_construct_missing.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
class Product
{
    public function __construct(
        public string $name,
        protected int $price,
    ) {}

    public function label(): string
    {
        return $this->name . ": " . number_format($this->price) . " IQD";
    }
}

class FreshProduct extends Product
{
    public function __construct(string $name, int $pricePerKg, public float $kg)
    {
        // parent::__construct(...) is missing
    }
}

$apples = new FreshProduct("Apples", 3000, 1.5);
echo $apples->label(), "\n";
Left, three steps: new FreshProduct("Apples", 3000, 1.5); then FreshProduct::__construct(), where kg = 1.5 is a promoted parameter; then parent::__construct("Apples", 4500), because 3000 × 1.5 = 4500 is sent up to Product. Right, the finished $apples object with two parts: the Product part (name = "Apples", price = 4500) and the FreshProduct part (kg = 1.5), with arrows from the steps that fill them. Bottom, in orange: forget parent::__construct and the Product part stays empty: Error: Typed property Product::$name must not be accessed before initialization. Last line: PHP never calls the parent's constructor by itself when the child has its own.لای چەپ، سێ هەنگاو: new FreshProduct("Apples", 3000, 1.5)؛ پاشان FreshProduct::__construct() کە kg = 1.5 پارامیتەرێکی بەرزکراوەیە؛ پاشان parent::__construct("Apples", 4500)، چونکە 3000 × 1.5 = 4500 بۆ Product دەنێردرێت. لای ڕاست، ئۆبجێکتەکەی تەواوی $apples بە دوو بەش: بەشی Product (name = "Apples"، price = 4500) و بەشی FreshProduct (kg = 1.5)، بە تیر لەو هەنگاوانەوە کە پڕیان دەکەنەوە. خوارەوە، بە پرتەقاڵی: ئەگەر بانگکردنی parent::__construct لەبیر بکەیت، بەشی Product بەتاڵ دەمێنێتەوە: Error: Typed property Product::$name must not be accessed before initialization. دوایین دێڕ: کاتێک کلاسی منداڵەکە دروستکەری خۆی هەبێت، PHP هەرگیز خۆی دروستکەری باوک بانگ ناکات.
parent::__construct() builds the parent's part of the object.⁦parent::__construct()⁩ بەشی باوکی دروست دەکات.CêIT

Overriding: a new version of a methodپێناسەکردنەوەی میتۆد: وەشانێکی نوێی میتۆدێک

A child may write a method with the same name as one it inherits. This is : for a FreshProduct, its own label() runs instead of Product's. Inside it, parent::label() still reaches the parent's version, so the child can add to it instead of rewriting it. The new method must be compatible: the same name, parameters that still accept what the parent accepted, and a visibility that is the same or wider.

دەتوانێت بە هەمان ناوی ئەوەی بۆماوەی کردووە بنووسێت. ئەمە : بۆ FreshProductـێک، label()ـی خۆی لەبری هی Product جێبەجێ دەبێت. لە ناویدا parent::label() هێشتا دەگاتە وەشانی باوکەکە، بۆیە دەتوانێت شتی بۆ زیاد بکات لەبری ئەوەی دووپاتە بینووسێتەوە. نوێیەکە دەبێت گونجاو بێت: هەمان ناو، کە هێشتا ئەوە وەردەگرن کە باوکەکە وەریدەگرت و وەک خۆی یان فراوانتر.

FreshProduct overrides label()FreshProduct label() دووپاتە پێناسە دەکاتەوەoverride.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
class Product
{
    public function __construct(
        public string $name,
        protected int $price,
    ) {}

    public function label(): string
    {
        return $this->name . ": " . number_format($this->price) . " IQD";
    }
}

class FreshProduct extends Product
{
    public function __construct(string $name, int $pricePerKg, public float $kg)
    {
        parent::__construct($name, (int) round($pricePerKg * $kg));
    }

    #[\Override]                          // PHP 8.3+ checks that Product has label()
    public function label(): string       // a new version of label()
    {
        return parent::label() . " (" . $this->kg . " kg)";
    }
}

$items = [new Product("Tea", 5000), new FreshProduct("Apples", 3000, 1.5)];
foreach ($items as $item) {
    echo $item->label(), "\n";
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 22

#[\Override] (PHP 8.3) asks PHP to check that the parent really has label(). PHP 8.2 simply ignores it, so the file runs on both.

#[\Override] ⁦(PHP 8.3)⁩ داوا لە PHP دەکات بپشکنێت کە باوکەکە بەڕاستی label()ـی هەیە. ⁦PHP 8.2⁩ تەنیا پشتگوێی دەخات، بۆیە لەسەر هەردووکیان کار دەکات.

Lineدێڕی 25

parent::label() gives "Apples: 4,500 IQD"; the child adds the weight.

parent::label() ⁦"Apples: 4,500 IQD"⁩ دەدات؛ کێشەکەی بۆ زیاد دەکات.

Lineدێڕی 29-32

One loop, two classes: each object runs its own label().

یەک ، دوو : هەر ⁦label()⁩ـی خۆی جێبەجێ دەکات.

Product at the top with label(), FreshProduct below it with its own label(), found first, joined to Product by a line with a hollow triangle. The call $tea->label() goes straight to Product's label() and prints Tea: 5,000 IQD. The call $apples->label() goes to FreshProduct's label(), and a dashed arrow marked parent::label() goes up to Product's label(); it prints Apples: 4,500 IQD (1.5 kg). Bottom: PHP looks in the object's own class first, then goes up to the parent.Product لە سەرەوە بە label()، FreshProduct لە خوارەوەی بە label()ـی خۆی کە یەکەم جار دەدۆزرێتەوە و بە هێڵێک و سێگۆشەیەکی بەتاڵ بە Productـەوە بەستراوە. بانگکردنی $tea->label() ڕاستەوخۆ دەچێتە label()ـی Product و Tea: 5,000 IQD چاپ دەکات. بانگکردنی $apples->label() دەچێتە label()ـی FreshProduct و تیرێکی پچڕپچڕ کە parent::label()ـی لەسەرە بۆ label()ـی Product سەردەکەوێت؛ Apples: 4,500 IQD (1.5 kg) چاپ دەکات. خوارەوە: PHP یەکەم جار لە کلاسی خودی ئۆبجێکتەکەدا دەگەڕێت، پاشان بۆ کلاسی باوکەکە سەردەکەوێت.
Which label() runs?کام ⁦label()⁩ جێبەجێ دەبێت؟CêIT

Making an inherited method less visible is refused, because code that may call label() on any Product must still be able to call it on a FreshProduct:

کەمکردنەوەی بۆماوەیی ڕەت دەکرێتەوە، چونکە ئەو کۆدەی دەتوانێت label() لەسەر هەر Productـێک بانگ بکات دەبێت هێشتا بتوانێت لەسەر FreshProductـێکیش بانگی بکات:

public in the parent, protected in the childpublic لە باوکدا، protected لە کلاسی منداڵداnarrow_visibility.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
class Product
{
    public function label(): string
    {
        return "a product";
    }
}

class FreshProduct extends Product
{
    protected function label(): string   // public -> protected: not allowed
    {
        return "fresh";
    }
}

Overriding: a new version of a methodپێناسەکردنەوەی میتۆد: وەشانێکی نوێی میتۆدێک

protected and private in a familyprotected و private لە خێزانێکدا

protected sits between the two others: the class itself and its children may use the member, outside code may not. A private member of the parent still exists inside a child object, but the child's code cannot see it. PHP then looks for a property of GiftCard with that name, finds none and gives a warning:

protected لە نێوان ئەو دووانەی تردایە: خودی و دەتوانن بەشەکە بەکار بهێنن، کۆدی دەرەوە ناتوانێت. بەشێکی privateـی باوک هێشتا لە ناو هەیە، بەڵام کۆدی نایبینێت. ئەوکات PHP بەدوای GiftCard بەو ناوە دەگەڕێت، هیچ نادۆزێتەوە و دەدات:

protected works in the child, private does notprotected لە کلاسی منداڵدا کار دەکات، private ناprotected_private.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
class Product
{
    protected int $price = 5000;       // children may use it
    private int $cost = 4000;          // only Product may use it
}

class GiftCard extends Product
{
    public function show(): void
    {
        echo "price: ", $this->price, "\n";
        echo "cost: ", $this->cost, "\n";
    }
}

(new GiftCard())->show();
A table. Columns: inside the class itself, inside a child class, outside (other code). Row public: yes, yes, yes. Row protected: yes, yes, no. Row private: yes, no, no. Bottom: choose the smallest one that still works: private first, then protected, and public only when needed.خشتەیەک. ستوونەکان: لە ناو خودی کلاسەکە، لە ناو کلاسێکی منداڵدا، لە دەرەوە (کۆدی تر). ڕیزی public: بەڵێ، بەڵێ، بەڵێ. ڕیزی protected: بەڵێ، بەڵێ، نەخێر. ڕیزی private: بەڵێ، نەخێر، نەخێر. خوارەوە: بچووکترینیان هەڵبژێرە کە هێشتا کار دەکات: یەکەم private، پاشان protected و public تەنیا کاتێک پێویست بێت.
Who may use a public, protected or private member.کێ دەتوانێت بەشێکی public، protected یان private بەکار بهێنێت.CêIT

final: no more changesfinal: گۆڕانکاریی تر نییە

Sometimes a class or a method must stay exactly as it is, for example the code that takes money. final class CashPayment cannot be extended, and a final method cannot be overridden. PHP checks this as soon as it reads the class:

هەندێک جار یان دەبێت ڕێک وەک خۆی بمێنێتەوە، بۆ نموونە ئەو کۆدەی پارە وەردەگرێت. final class CashPayment ناتوانرێت extends بکرێت و final ناتوانرێت دووپاتە پێناسە بکرێتەوە. PHP هەر کە دەخوێنێتەوە ئەمە دەپشکنێت:

A final classکلاسێکی finalfinal.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
final class CashPayment
{
    public function pay(int $amount): string
    {
        return "Paid " . number_format($amount) . " IQD in cash";
    }
}

class CheapCash extends CashPayment {}
A final methodمیتۆدێکی finalfinal_method.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
class Checkout
{
    final public function total(int $sum): int
    {
        return $sum;
    }
}

class FreeCheckout extends Checkout
{
    public function total(int $sum): int
    {
        return 0;
    }
}

Since PHP 8.1 a class constant can be final too, and since 8.4 a property. The Java version of the same idea is in final classes and methods.

لە ⁦PHP 8.1⁩ـەوە دەتوانێت final بێت و لە ⁦8.4⁩ـەوە . وەشانی Javaـی هەمان بیرۆکە لە کلاسەکان و میتۆدەکانی finalدایە.

Abstract classes: half-finished plansکلاسە ڕووتەکان: نەخشەی نیوەتەواو

Every payment prints a receipt the same way, but each kind pays differently. An holds the shared code and leaves the different part as an : a signature without a body. You cannot make an object of an abstract class; a child must write every abstract method (or be abstract itself). A class with even one abstract method must be declared abstract.

هەموو پارەدانێک بە هەمان شێوە پسوولە چاپ دەکات، بەڵام هەر جۆرێک بە شێوەیەکی جیاواز پارە دەدات. کۆدە هاوبەشەکە هەڵدەگرێت و بەشە جیاوازەکە وەک بەجێ دەهێڵێت: سەرەدێڕێک بێ جەستە. ناتوانیت لە دروست بکەیت؛ دەبێت هەموو بنووسێت (یان خۆی ڕووت بێت). تەنانەت بە یەک دەبێت بە abstract بناسێنرێت.

An abstract PaymentPaymentـێکی ڕووتabstract.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
abstract class Payment
{
    public function __construct(protected int $amount) {}

    abstract public function pay(): string;     // no body: each child writes it

    public function receipt(): string            // ready code, shared by all children
    {
        return "Receipt: " . $this->pay();
    }
}

class CashPayment extends Payment
{
    public function pay(): string
    {
        return number_format($this->amount) . " IQD paid in cash";
    }
}

$p = new CashPayment(37000);
echo $p->receipt(), "\n";
$q = new Payment(37000);       // not allowed
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

abstract class: a plan, not a finished thing.

abstract class: نەخشەیەک، نەک شتێکی تەواو.

Lineدێڕی 6

No body: each child must write pay().

بێ جەستە: هەر دەبێت pay() بنووسێت.

Lineدێڕی 8-11

Ready code that every child shares; it calls the child's pay().

کۆدی ئامادە کە هەموو بەکاری دەهێنێت؛ pay()ـی بانگ دەکات.

Lineدێڕی 24

Making an object of the abstract class itself is refused.

دروستکردنی لە خودی ڕەت دەکرێتەوە.

Left, a dashed purple box abstract Payment with # $amount, abstract pay(): string (no body) and receipt(): string (ready code). Below it CashPayment, which writes pay(), joined by extends with a hollow triangle. Right, in orange: new Payment(37000); gives Error: Cannot instantiate abstract class Payment. In green: $p = new CashPayment(37000); echo $p->receipt(); prints Receipt: 37,000 IQD paid in cash. Bottom: you cannot make an object of it; a child class must write its abstract methods.لای چەپ، سندووقێکی مۆری پچڕپچڕ abstract Payment بە # $amount، abstract pay(): string (بێ جەستە) و receipt(): string (کۆدی ئامادە). لە خوارەوەی CashPayment کە pay() دەنووسێت، بە extends و سێگۆشەیەکی بەتاڵ بەستراوە. لای ڕاست، بە پرتەقاڵی: new Payment(37000); هەڵەی Error: Cannot instantiate abstract class Payment دەدات. بە سەوز: $p = new CashPayment(37000); echo $p->receipt(); Receipt: 37,000 IQD paid in cash چاپ دەکات. خوارەوە: ناتوانیت ئۆبجێکتی لێ دروست بکەیت؛ کلاسێکی منداڵ دەبێت میتۆدە ڕووتەکانی بنووسێت.
An abstract class is half finished. نیوەتەواوە.CêIT

Interfaces: a contractنێوبەستەکان: گرێبەستێک

An lists public methods without bodies: it says what a class can do, never how. A class signs it with implements and must then write every one of those methods. An interface has no properties and no ready code (PHP 8.4 adds property declarations), and you cannot make an object of it. Java's version is in Interfaces. Interfaces are also how a class receives its helpers from outside: see Dependency Injection.

لیستێکی publicـە بێ جەستە: دەڵێت چی دەتوانێت بکات، هەرگیز نالێت چۆن. بە implements واژووی دەکات و پاشان دەبێت هەموو ئەو بنووسێت. نە هەیە نە کۆدی ئامادە (⁦PHP 8.4⁩ ناساندنی زیاد دەکات) و ناتوانیت لێ دروست بکەیت. وەشانی Java لە نێوبەستەکاندایە. هەروەها ئەو ڕێگایەن کە یاریدەدەرەکانی لە دەرەوە وەردەگرێت: بڕوانە تێخستنی وابەستەیی.

The cashier's one questionتاکە پرسیاری کاشێرەکە

At the till of a bazaar shop the cashier asks only one thing: "Pay 37,000 dinars." Cash, a bank card or a phone wallet all answer that question in their own way, and the cashier does not need to know how. The interface is that question; each payment class is one way of answering it.

لە شوێنی پارەدانی دووکانێکی بازاڕ، کاشێرەکە تەنیا یەک شت دەپرسێت: «⁦37,000⁩ دینار بدە.» پارەی نەختینە، کارتی بانکی یان جزدانی مۆبایل هەموویان بە شێوەی خۆیان وەڵامی ئەو پرسیارە دەدەنەوە و کاشێرەکە پێویست ناکات بزانێت چۆن. ئەو پرسیارەیە؛ هەر پارەدان ڕێگایەکی وەڵامدانەوەیەتی.

Where the analogy stops workingسنووری ئەم نموونەیە

PHP checks only that the method exists with the right parameters and return type; it cannot check that the method really takes the money.

PHP تەنیا دەپشکنێت کە بە و جۆری دروست هەیە؛ ناتوانێت بپشکنێت کە بەڕاستی پارەکە وەردەگرێت.

One interface, two classesیەک نێوبەستە، دوو کلاسinterface.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod
{
    public function pay(int $amount): string;   // a promise, no body
}

class CashPayment implements PaymentMethod
{
    public function pay(int $amount): string
    {
        return "Cash: " . number_format($amount) . " IQD";
    }
}

class CardPayment implements PaymentMethod
{
    public function __construct(private string $last4) {}

    public function pay(int $amount): string
    {
        return "Card ****" . $this->last4 . ": " . number_format($amount) . " IQD";
    }
}

$cash = new CashPayment();
$card = new CardPayment("4821");
echo $cash->pay(37000), "\n";
echo $card->pay(37000), "\n";
var_dump($card instanceof PaymentMethod);
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-5

The contract: one public method, no body.

گرێبەستەکە: یەک public، بێ جەستە.

Lineدێڕی 7

implements PaymentMethod: CashPayment promises to have pay().

implements PaymentMethod: CashPayment بەڵێن دەدات pay()ـی هەبێت.

Lineدێڕی 29

An object of a class that implements the interface is also an instance of the interface.

ئەو جێبەجێ دەکات، هەروەها ئەو .

At the top a dashed purple box: interface PaymentMethod with public function pay(int $amount): string; and the words only the promise, no body. Below, three green classes, CashPayment, CardPayment and WalletPayment, each with a purple plug of the same shape that fits the interface, joined to it by dashed lines marked implements with hollow triangles. Each writes pay() { … } and gives its own result: Cash: 37,000 IQD; Card 4821: …; Wallet 0700*23: …. Bottom: each class keeps the promise in its own way; code that needs a PaymentMethod accepts any of them.لە سەرەوە سندووقێکی مۆری پچڕپچڕ: interface PaymentMethod بە public function pay(int $amount): string; و تێبینیی تەنیا بەڵێنەکە، بێ جەستە. لە خوارەوە سێ کلاسی سەوز، CashPayment، CardPayment و WalletPayment، هەریەکەیان پلاگێکی مۆری هەمان شێوەی هەیە کە لەگەڵ نێوبەستەکە دەگونجێت و بە هێڵی پچڕپچڕی implements و سێگۆشەی بەتاڵ پێیەوە بەستراون. هەریەکەیان pay() { … } دەنووسێت و ئەنجامی خۆی دەدات: Cash: 37,000 IQD؛ Card 4821: …؛ Wallet 0700*23: …. خوارەوە: هەر کلاسێک بە شێوەی خۆی بەڵێنەکە دەباتە سەر؛ ئەو کۆدەی PaymentMethodـی دەوێت هەر یەکێکیان وەردەگرێت.
An interface is a contract: same shape, different inside. گرێبەستێکە: هەمان شێوە، ناوەڕۆکی جیاواز.CêIT

Forget one method of the contract, or try to make an object of the interface itself, and PHP refuses:

ئەگەر یەکێک لە گرێبەستەکە لەبیر بکەیت، یان هەوڵ بدەیت لە خودی دروست بکەیت، PHP ڕەتی دەکاتەوە:

A class that breaks the contractکلاسێک کە گرێبەستەکە دەشکێنێتinterface_missing.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod
{
    public function pay(int $amount): string;
}

class WalletPayment implements PaymentMethod
{
    public function send(int $amount): string    // wrong name: pay() is missing
    {
        return "Sent " . $amount;
    }
}
new on an interfacenew لەسەر نێوبەستەیەکnew_interface.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod
{
    public function pay(int $amount): string;
}

$m = new PaymentMethod();

Interfaces: a contractنێوبەستەکان: گرێبەستێک

Several interfaces at onceچەند نێوبەستەیەک پێکەوە

A class may extend only one parent, but it may implement as many interfaces as it needs, separated by commas. A card payment can be refunded; cash at this shop cannot. instanceof asks whether an object keeps a contract. When a class does both, extends comes first: class CardPayment extends Payment implements PaymentMethod, Refundable.

تەنیا دەتوانێت لە یەک extends بکات، بەڵام دەتوانێت هەر چەند پێویست بێت جێبەجێ بکات و بە فاریزە لێکیان جیا دەکاتەوە. پارەدان بە کارت دەتوانرێت بگەڕێنرێتەوە؛ پارەی نەختینە لەم دووکانەدا ناگەڕێنرێتەوە. instanceof دەپرسێت ئایا گرێبەستێک دەباتە سەر. کاتێک هەردووکیان دەکات، extends یەکەم دێت: class CardPayment extends Payment implements PaymentMethod, Refundable.

Two interfaces on one classدوو نێوبەستە لەسەر یەک کلاسseveral.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod
{
    public function pay(int $amount): string;
}

interface Refundable
{
    public function refund(int $amount): string;
}

class CardPayment implements PaymentMethod, Refundable
{
    public function pay(int $amount): string
    {
        return "Card paid " . number_format($amount) . " IQD";
    }

    public function refund(int $amount): string
    {
        return "Card refunded " . number_format($amount) . " IQD";
    }
}

class CashPayment implements PaymentMethod
{
    public function pay(int $amount): string
    {
        return "Cash paid " . number_format($amount) . " IQD";
    }
}

foreach ([new CardPayment(), new CashPayment()] as $m) {
    echo $m->pay(12000), "\n";
    if ($m instanceof Refundable) {
        echo $m->refund(12000), "\n";
    } else {
        echo get_class($m), " cannot refund\n";
    }
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 12

CardPayment keeps two contracts.

CardPayment دوو گرێبەست دەباتە سەر.

Lineدێڕی 35

instanceof Refundable is true only for the card.

instanceof Refundable تەنیا بۆ کارتەکە ڕاستە.

CardPayment wears two dashed purple badges, PaymentMethod and Refundable, and has pay() and refund(); its line reads implements PaymentMethod, Refundable. CashPayment wears one badge, PaymentMethod, and has pay(). Below: $card instanceof Refundable gives true (tick) and $cash instanceof Refundable gives false (cross); instanceof asks whether the object has that contract. Bottom: a class extends at most one parent, but it may implement many interfaces.CardPayment دوو نیشانەی مۆری پچڕپچڕی هەیە (PaymentMethod و Refundable) و pay() و refund()ـی هەیە؛ دێڕەکەی دەڵێت implements PaymentMethod, Refundable. CashPayment یەک نیشانەی هەیە (PaymentMethod) و pay()ـی هەیە. لە خوارەوە: $card instanceof Refundable ڕاست (true) دەدات و $cash instanceof Refundable هەڵە (false) دەدات؛ instanceof دەپرسێت ئایا ئۆبجێکتەکە ئەو گرێبەستەی هەیە. خوارەوە: کلاسێک تەنیا یەک کلاسی باوکی دەبێت، بەڵام دەتوانێت چەندین نێوبەستە جێبەجێ بکات.
One parent, but many interfaces.یەک ، بەڵام چەندین .CêIT

Polymorphism: one call, many formsفرەشێوەیی: یەک بانگکردن، چەندین شێوە

checkout() below accepts any PaymentMethod. It calls $method->pay($total) without asking which class it got, and each object runs its own pay(). This is : one call, many forms. Next month the shop can add an instalment plan as a new class that implements the interface, and checkout() will not change at all.

checkout()ـی خوارەوە هەر PaymentMethodـێک وەردەگرێت. $method->pay($total) بانگ دەکات بێ ئەوەی بپرسێت کام وەرگرتووە و هەر pay()ـی خۆی جێبەجێ دەکات. ئەمە : یەک بانگکردن، چەندین شێوە. مانگی داهاتوو دووکانەکە دەتوانێت پلانی قیست وەک نوێ زیاد بکات کە جێبەجێ دەکات و checkout() هیچ ناگۆڕێت.

One checkout, three payment methodsیەک checkout، سێ ڕێگای پارەدانpolymorphism.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod
{
    public function pay(int $amount): string;
}

class CashPayment implements PaymentMethod
{
    public function pay(int $amount): string
    {
        return "Cash: " . number_format($amount) . " IQD, change counted at the till";
    }
}

class CardPayment implements PaymentMethod
{
    public function __construct(private string $last4) {}

    public function pay(int $amount): string
    {
        return "Card ****{$this->last4}: " . number_format($amount) . " IQD sent to the bank";
    }
}

class WalletPayment implements PaymentMethod
{
    public function __construct(private string $phone) {}

    public function pay(int $amount): string
    {
        $hidden = substr($this->phone, 0, 4) . "*****" . substr($this->phone, -2);
        return "Wallet $hidden: " . number_format($amount) . " IQD approved in the app";
    }
}

function checkout(int $total, PaymentMethod $method): void   // any PaymentMethod is welcome
{
    echo $method->pay($total), "\n";
}

$methods = [
    new CashPayment(),
    new CardPayment("4821"),
    new WalletPayment("07000000123"),
];
foreach ($methods as $method) {
    checkout(37000, $method);    // one call, three behaviours
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 31

The wallet shows only the start and the end of the phone number.

جزدانەکە تەنیا سەرەتا و کۆتایی ژمارەی مۆبایلەکە پیشان دەدات.

Lineدێڕی 36

The parameter type is the interface, not one class.

جۆری ، نەک یەک .

Lineدێڕی 46-48

The same line runs three different pay() methods.

هەمان دێڕ سێ pay()ـی جیاواز جێبەجێ دەکات.

An animation in four steps. Left, the function checkout(int $total, PaymentMethod $method) with the line echo $method->pay($total);. Right, three classes: CashPayment, CardPayment and WalletPayment. Step 1: an arrow goes to CashPayment and the output line Cash: 37,000 IQD, change counted at the till appears. Step 2: the arrow goes to CardPayment; Card 4821: 37,000 IQD sent to the bank. Step 3: the arrow goes to WalletPayment; Wallet 0700*23: 37,000 IQD approved in the app, and checkout did not change. Step 4: one call, many forms: this is polymorphism. Under it: foreach ($methods as $method) { checkout(37000, $method); }.
An animation in four steps. Left, the function checkout(int $total, PaymentMethod $method) with the line echo $method->pay($total);. Right, three classes: CashPayment, CardPayment and WalletPayment. Step 1: an arrow goes to CashPayment and the output line Cash: 37,000 IQD, change counted at the till appears. Step 2: the arrow goes to CardPayment; Card 4821: 37,000 IQD sent to the bank. Step 3: the arrow goes to WalletPayment; Wallet 0700*23: 37,000 IQD approved in the app, and checkout did not change. Step 4: one call, many forms: this is polymorphism. Under it: foreach ($methods as $method) { checkout(37000, $method); }.
The same call, three different pay() methods.هەمان بانگکردن، سێ ⁦pay()⁩ـی جیاواز.CêIT

The type declaration also protects checkout(). A Coupon class that happens to have a pay() method, but never promised the contract with implements, is refused:

پاسەوانی checkout()ـیش دەکات. Coupon کە بە ڕێکەوت pay()ـی هەیە، بەڵام هەرگیز بە implements بەڵێنی گرێبەستەکەی نەداوە، ڕەت دەکرێتەوە:

A pay() method is not enoughمیتۆدی ⁦pay()⁩ بەس نییەwrong_type.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod
{
    public function pay(int $amount): string;
}

class Coupon                        // has pay(), but does not implement PaymentMethod
{
    public function pay(int $amount): string
    {
        return "Coupon used";
    }
}

function checkout(int $total, PaymentMethod $method): void
{
    echo $method->pay($total), "\n";
}

checkout(37000, new Coupon());

Polymorphism: one call, many formsفرەشێوەیی: یەک بانگکردن، چەندین شێوە

Abstract class or interface?کلاسی ڕووت یان نێوبەستە؟

Both describe methods that others must write, and neither can be created with new. The differences decide which one fits, and many programs use both: an interface for the contract and an abstract class for code that several of its classes share, as the worked example does.

هەردووکیان ئەو وەسف دەکەن کە ئەوانی تر دەبێت بیاننووسن و هیچیان بە new دروست ناکرێن. جیاوازییەکان دیاری دەکەن کامیان دەگونجێت و زۆر بەرنامە هەردووکیان بەکار دەهێنن: بۆ گرێبەستەکە و بۆ ئەو کۆدەی چەند هاوبەشی دەکەن، وەک نموونە کارپێکراوەکە.

Abstract class and interface in PHP. و لە PHPـدا.
CompareبەراوردAbstract classInterface
How a class uses it چۆن بەکاری دەهێنێتextends: only oneextends: تەنیا یەکimplements: as many as neededimplements: هەر چەندێک پێویست بێت
PropertiesYes, anyبەڵێ، هەر جۆرێکNo (PHP 8.4 adds property declarations); constants onlyنەخێر (⁦PHP 8.4⁩ ناساندنی زیاد دەکات)؛ تەنیا
Method bodiesجەستەی Yes, finished and abstract methodsبەڵێ، تەواو و ڕووتNo: only public method signaturesنەخێر: تەنیا سەرەدێڕی public
ConstructorYesبەڵێAllowed but not recommendedڕێگەپێدراوە، بەڵام پێشنیار ناکرێت
Use it whenکەی بەکاری بهێنیتClosely related classes share code and data نزیک لە یەک کۆد و هاوبەشیان هەیەUnrelated classes share an ability بێپەیوەندی توانایەکی هاوبەشیان هەیە

Traits: shared code for unrelated classesترەیتەکان: کۆدی هاوبەش بۆ کلاسەکانی بێپەیوەندی

PHP has single inheritance: a class has at most one parent. But Cart and Checkout are unrelated, and both want the same history log. A is a named group of methods and properties that PHP copies into a class with use; the manual calls it "language-assisted copy and paste". A trait cannot make objects itself.

PHP تاکی هەیە: زۆرترین یەک هەیە. بەڵام Cart و Checkout پەیوەندییان بە یەکەوە نییە و هەردووکیان هەمان تۆماری مێژوویان دەوێت. کۆمەڵەیەکی ناونراوی و کە PHP بە use کۆپییان دەکاتە ناو ؛ ڕێبەری PHP پێی دەڵێت «کۆپی و لکاندن بە یارمەتیی زمانەکە». خۆی ناتوانێت دروست بکات.

One trait in two unrelated classesیەک ترەیت لە دوو کلاسی بێپەیوەندیداtraits.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
trait KeepsHistory                       // a box of reusable members
{
    private array $history = [];

    protected function remember(string $event): void
    {
        $this->history[] = $event;
    }

    public function history(): string
    {
        return implode(" | ", $this->history);
    }
}

class Cart
{
    use KeepsHistory;                    // copy the trait's members in here

    public function add(string $item): void
    {
        $this->remember("added $item");
    }
}

class Checkout
{
    use KeepsHistory;                    // the same members, in an unrelated class

    public function pay(int $amount): void
    {
        $this->remember("paid " . number_format($amount));
    }
}

$cart = new Cart();
$cart->add("Tea");
$cart->add("Honey");
$till = new Checkout();
$till->pay(20000);

echo $cart->history(), "\n";
echo $till->history(), "\n";
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-15

The trait: a private property, a protected helper and a public method.

: private، یارمەتیدەرێکی protected و public.

Lineدێڕی 19

use KeepsHistory; copies the trait's members into Cart.

use KeepsHistory; بەشەکانی کۆپی دەکاتە ناو Cartـەوە.

Lineدێڕی 29

The same members, in a class with a different job.

هەمان بەشەکان، لە کە کارێکی جیاوازی هەیە.

Lineدێڕی 43-44

Each object keeps its own history.

هەر مێژووی خۆی هەڵدەگرێت.

At the top a dashed purple box: trait KeepsHistory with private array $history, remember(string $event) and history(): string. Dashed arrows marked copied in by use go down to two blue classes with no common parent class: class Cart, with use KeepsHistory;, the copied $history, remember() and history(), and its own add(string $item); and class Checkout, with the same copied members and its own pay(int $amount). Bottom: on a name clash the class's own method wins, then the trait's, then the parent's; a trait cannot make objects: new KeepsHistory() is an error.لە سەرەوە سندووقێکی مۆری پچڕپچڕ: trait KeepsHistory بە private array $history، remember(string $event) و history(): string. تیری پچڕپچڕ کە «بە use کۆپی دەکرێتە ناوەوە»ـی لەسەرە بۆ دوو کلاسی شین دەچن کە هیچ کلاسێکی باوکی هاوبەشیان نییە: class Cart، بە use KeepsHistory;، $history، remember() و history()ـی کۆپیکراو و add(string $item)ـی خۆی؛ و class Checkout، بە هەمان بەشە کۆپیکراوەکان و pay(int $amount)ـی خۆی. خوارەوە: لە ناوی هاوبەشدا یەکەم میتۆدی خودی کلاسەکە دەباتەوە، پاشان هی ترەیتەکە، پاشان هی کلاسی باوکەکە؛ ترەیت ناتوانێت ئۆبجێکت دروست بکات: new KeepsHistory() هەڵەیە.
A trait: shared code for unrelated classes.: کۆدی هاوبەش بۆ بێپەیوەندی.CêIT
A trait is not a classترەیت کلاس نییەtrait_new.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
trait KeepsHistory
{
    public function history(): string { return ""; }
}

$h = new KeepsHistory();

Enums: a fixed list of values (PHP 8.1)ئینەمەکان: لیستێکی جێگیری نرخەکان ⁦(PHP 8.1)⁩

A payment can only be paid, failed or refunded. Storing that as free text invites mistakes such as "payed". An (PHP 8.1) is a type with a fixed list of cases; nothing else can sneak in. A backed enum gives each case a string or int value, which is what you store in a database; from() turns the stored text back into a case and tryFrom() returns null for unknown text. Enums may have methods. Java's enums are in Enums.

پارەدانێک تەنیا دەتوانێت دراو، سەرنەکەوتوو یان گەڕێنراوە بێت. هەڵگرتنی ئەمە وەک دەقی ئازاد دەرگا بۆ هەڵەی وەک "payed" دەکاتەوە. ⁦(PHP 8.1)⁩ جۆرێکە بە لیستێکی جێگیری caseـەکان؛ هیچ شتێکی تر ناتوانێت بچێتە ناوی. نرخدار string یان int دەداتە هەر caseـێک کە ئەوەیە لە هەڵیدەگریت؛ from() دەقە هەڵگیراوەکە دەکاتەوە بە caseـێک و tryFrom() بۆ دەقی نەناسراو null دەگەڕێنێتەوە. دەتوانن هەبێت. Java لە ئینەمەکاندان.

PaymentStatus as a backed enumPaymentStatus وەک ئینەمێکی نرخدارenums.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
enum PaymentStatus: string
{
    case Paid = "paid";
    case Failed = "failed";
    case Refunded = "refunded";

    public function label(): string
    {
        return match ($this) {
            PaymentStatus::Paid => "Payment received",
            PaymentStatus::Failed => "Payment failed, try again",
            PaymentStatus::Refunded => "Money returned",
        };
    }
}

$status = PaymentStatus::Paid;
echo $status->name, " / ", $status->value, " / ", $status->label(), "\n";

$fromDb = PaymentStatus::from("refunded");     // text from a database -> a case
echo $fromDb->label(), "\n";

var_dump(PaymentStatus::tryFrom("lost"));      // unknown text -> null
echo count(PaymentStatus::cases()), " cases\n";
var_dump($status === PaymentStatus::Paid);

PaymentStatus::from("lost");                   // unknown text -> ValueError
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2

: string: every case has a string value.

: string: هەموو caseـێک stringـی هەیە.

Lineدێڕی 8-15

An enum may have methods; match ($this) picks the text for each case.

دەتوانێت هەبێت؛ match ($this) دەقی هەر caseـێک هەڵدەبژێرێت.

Lineدێڕی 21

from() for values you trust, such as your own database.

from() بۆ ئەو متمانەت پێیانە، وەک خۆت.

Lineدێڕی 24

tryFrom() for values you do not trust: unknown text gives null.

tryFrom() بۆ ئەو متمانەت پێیان نییە: دەقی نەناسراو null دەدات.

Lineدێڕی 28

from() with unknown text throws a ValueError.

from() بە دەقی نەناسراو ValueError دەدات.

Left, a purple box enum PaymentStatus: string with three cases: case Paid = "paid", case Failed = "failed" and case Refunded = "refunded". Right, three calls: PaymentStatus::from("refunded") gives PaymentStatus::Refunded (tick); PaymentStatus::tryFrom("lost") gives null, no such case (question mark); PaymentStatus::from("lost") gives a ValueError (cross), with the message "lost" is not a valid backing value for enum PaymentStatus. Bottom: each case has a name ($status->name) and, in a backed enum, a value ($status->value); use tryFrom() for input you do not trust, such as text from a form.لای چەپ، سندووقێکی مۆر enum PaymentStatus: string بە سێ case: case Paid = "paid"، case Failed = "failed" و case Refunded = "refunded". لای ڕاست، سێ بانگکردن: PaymentStatus::from("refunded") PaymentStatus::Refunded دەدات (نیشانەی ڕاست)؛ PaymentStatus::tryFrom("lost") null دەدات، caseـێکی وا نییە (نیشانەی پرسیار)؛ PaymentStatus::from("lost") ValueError دەدات (خاچ)، بە پەیامی "lost" is not a valid backing value for enum PaymentStatus. خوارەوە: هەر caseـێک ناوێکی هەیە ($status->name) و لە ئینەمێکی نرخداردا نرخێکیشی هەیە ($status->value)؛ بۆ نێوخستەیەکی بەکارهێنەر کە متمانەت پێی نییە، وەک دەقی فۆرمێک، tryFrom() بەکار بهێنە.
An enum: a fixed list of allowed values.: لیستێکی جێگیری ڕێگەپێدراو.CêIT

Namespaces and autoloading: an overviewبۆشایی ناوەکان و بارکردنی خۆکار: تێڕوانینێکی گشتی

Real projects have many classes, often from other people's libraries. Two libraries may both have a class called Payment. A works like a folder for names: Bazaar\Payments\CashPayment and OtherShop\Payment can live side by side. A file declares its namespace on the first line (namespace Bazaar\Payments;), and another file imports a name once with use and then writes it short.

پرۆژە ڕاستەقینەکان زۆریان هەیە، زۆرجار لە خەڵکی ترەوە. لەوانەیە دوو هەردووکیان بە ناوی Payment هەبێت. وەک بۆ ناوەکان کار دەکات: Bazaar\Payments\CashPayment و OtherShop\Payment دەتوانن لە تەنیشت یەکەوە بژین. لە یەکەم دێڕدا خۆی ڕادەگەیەنێت (namespace Bazaar\Payments;) و تر بە use یەک جار ناوێک هاوردە دەکات و پاشان بە کورتی دەینووسێت.

With one class per file, a big project would need a require for every class. removes that list: you register a function with spl_autoload_register(), and PHP calls it with the class name the first time a class is used. The usual rule (PSR-4) makes the namespace follow the folders, so the function can turn a name into a path. The example below runs on XAMPP as it is; [autoload] lines show the moment each file is loaded. (include and require explains require.)

بە یەک بۆ هەر ، پرۆژەیەکی گەورە بۆ هەموو requireـێکی پێویست دەبوو. ئەو لیستە لادەبات: بە spl_autoload_register() تۆمار دەکەیت و PHP یەکەم جار کە بەکار دێت بە ناوی ئەو بانگی دەکات. یاسا باوەکە ⁦(PSR-4)⁩ وا دەکات شوێنی بکەوێت، بۆیە دەتوانێت ناوێک بکات بە . نموونەی خوارەوە وەک خۆی لەسەر XAMPP کار دەکات؛ دێڕەکانی [autoload] ئەو ساتە پیشان دەدەن کە هەر بار دەکرێت. (include و require require ڕوون دەکاتەوە.)

The project's foldersفۆڵدەرەکانی پرۆژەکەbazaar/TEXT
bazaar/
├── index.php
└── src/
    ├── Payments/
    │   ├── PaymentMethod.php
    │   └── CashPayment.php
    └── Shop/
        └── Checkout.php
A class in the namespace BazaarPaymentsکلاسێک لە بۆشایی ناوەکانی BazaarPaymentssrc/Payments/CashPayment.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
namespace Bazaar\Payments;

class CashPayment implements PaymentMethod
{
    public function pay(int $amount): string
    {
        return "Cash: " . number_format($amount) . " IQD";
    }
}
Importing a name with useهاوردەکردنی ناوێک بە usesrc/Shop/Checkout.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
namespace Bazaar\Shop;

use Bazaar\Payments\PaymentMethod;     // import the name once

class Checkout
{
    public function finish(int $total, PaymentMethod $method): string
    {
        return $method->pay($total);
    }
}
The autoloader and the main scriptبارکەرەکە و دەسنووسە سەرەکییەکەindex.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
// Autoloader: turn a class name into a file name, then load that file
spl_autoload_register(function (string $class): void {
    $prefix = "Bazaar\\";
    if (!str_starts_with($class, $prefix)) {
        return;                                    // not one of our classes
    }
    $relative = substr($class, strlen($prefix));   // "Payments\CashPayment"
    $file = __DIR__ . "/src/" . str_replace("\\", "/", $relative) . ".php";
    echo "[autoload] $class\n";
    require $file;
});

use Bazaar\Payments\CashPayment;
use Bazaar\Shop\Checkout;

$till = new Checkout();
echo $till->finish(37000, new CashPayment()), "\n";
echo Checkout::class, "\n";
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 3-12

The autoloader: "BazaarShopCheckout" becomes src/Shop/Checkout.php.

بارکەرەکە: ⁦"BazaarShopCheckout"⁩ دەبێتە ⁦src/Shop/Checkout.php⁩.

Lineدێڕی 14-15

use imports the full names once.

use ناوە تەواوەکان یەک جار هاوردە دەکات.

Lineدێڕی 17-18

Checkout is loaded at new Checkout(); CashPayment and the interface it implements follow.

Checkout لە کاتی new Checkout()ـدا بار دەکرێت؛ CashPayment و ئەو جێبەجێی دەکات بە دوایدا دێن.

Lineدێڕی 19

::class gives the full name of a class.

::class ناوی تەواوی دەدات.

Left, three class names: BazaarPaymentsPaymentMethod, BazaarPaymentsCashPayment and BazaarShopCheckout. Right, the folder tree bazaar/ with index.php and src/, which holds Payments/ (PaymentMethod.php, CashPayment.php) and Shop/ (Checkout.php). Dashed arrows join each name to its file. A green box gives the autoloader's rule (PSR-4): Bazaar → src/, → /, and add ".php". Bottom: spl_autoload_register() loads each file the first time its class is used; Composer can write this autoloader for you (vendor/autoload.php).لای چەپ، سێ ناوی کلاس: BazaarPaymentsPaymentMethod، BazaarPaymentsCashPayment و BazaarShopCheckout. لای ڕاست، داری فۆڵدەرەکانی bazaar/ بە index.php و src/ کە Payments/ (PaymentMethod.php، CashPayment.php) و Shop/ (Checkout.php)ـی تێدایە. تیری پچڕپچڕ هەر ناوێک بە فایلی خۆیەوە دەبەستێت. سندووقێکی سەوز یاسای بارکردنی خۆکار (PSR-4) دەدات: Bazaar ← src/، ← / و ".php" زیاد بکە. خوارەوە: spl_autoload_register() هەر فایلێک ئەو کاتە بار دەکات کە کلاسەکەی بۆ یەکەم جار بەکار دێت؛ Composer دەتوانێت ئەم بارکەرە بۆت بنووسێت (vendor/autoload.php).
Namespaces follow folders, so classes can be found. شوێنی دەکەوێت، بۆیە دەدۆزرێنەوە.CêIT

Namespaces and autoloading: an overviewبۆشایی ناوەکان و بارکردنی خۆکار: تێڕوانینێکی گشتی

Composer in one screenComposer لە یەک شاشەدا

Composer is PHP's , like pip for Python. A composer.json file lists the a project needs (from the Packagist site) and the autoload rule. composer install downloads them into vendor/ and writes vendor/autoload.php, so one require line loads every class: the libraries' and yours. composer.lock records the exact versions, so everyone installs the same ones. Frameworks such as Laravel and Symfony, and the ORMs of Object-Relational Mapping, are installed this way. This course does not install Composer; the example below is for reading only.

Composer PHPـیە، وەک pip بۆ Python. composer.json ئەو ڕیز دەکات کە پرۆژەیەک پێویستیەتی (لە ماڵپەڕی Packagistـەوە) لەگەڵ یاسای . composer install داباریان دەکات بۆ ناو vendor/ و vendor/autoload.php دەنووسێت، بۆیە یەک دێڕی require هەموو بار دەکات: هی و هی خۆت. composer.lock وەشانە وردەکان تۆمار دەکات، بۆیە هەموو کەس هەمان وەشان دادەمەزرێنێت. وەک Laravel و Symfony و ORMـەکانی ⁦Object-Relational Mapping⁩ بەم شێوەیە دادەمەزرێنرێن. ئەم خولە Composer دانامەزرێنێت؛ نموونەی خوارەوە تەنیا بۆ خوێندنەوەیە.

A composer.json (example, not run in this course)⁦composer.json⁩ـێک (نموونە، لەم خولەدا جێبەجێ ناکرێت)composer.jsonJSON
{
    "require": {
        "vendor/package": "^2.0"
    },
    "autoload": {
        "psr-4": { "Bazaar\\": "src/" }
    }
}
Three boxes from left to right. composer.json, with "require" for a package and "autoload" with "psr-4": {"Bazaar\": "src/"}. An arrow marked composer install goes to vendor/, which holds the packages, composer/ and autoload.php: libraries plus one autoloader. An arrow goes on to index.php, which writes require __DIR__ . "/vendor/autoload.php";, use BazaarShopCheckout; and $till = new Checkout();. An orange band: not installed in this course; the course's examples use spl_autoload_register() instead. Bottom: composer.lock records the exact versions, so everyone installs the same ones.سێ سندووق لە چەپەوە بۆ ڕاست. composer.json بە "require" بۆ پاکێجێک و "autoload" بە "psr-4": {"Bazaar\": "src/"}. تیرێک کە composer installـی لەسەرە بۆ vendor/ دەچێت کە پاکێجەکان، composer/ و autoload.php هەڵدەگرێت: کتێبخانەکان و یەک بارکەر. تیرێک بەردەوام دەبێت بۆ index.php کە require __DIR__ . "/vendor/autoload.php";، use BazaarShopCheckout; و $till = new Checkout(); دەنووسێت. پشتێنێکی پرتەقاڵی: لەم خولەدا دانامەزرێنرێت؛ نموونەکانی خولەکە لەبری ئەوە spl_autoload_register() بەکار دەهێنن. خوارەوە: composer.lock وەشانە وردەکان تۆمار دەکات، بۆیە هەموو کەس هەمان وەشان دادەمەزرێنێت.
Composer, PHP's package manager, in one picture.Composer، PHP، لە یەک وێنەدا.CêIT

Worked example: paying at the bazaar tillنموونەی کارپێکراو: پارەدان لە شوێنی پارەدانی بازاڕ

The task. The till must take 37,000 IQD by cash, by bank card or by mobile wallet, report "paid" or "failed", and keep a history. The design. An interface PaymentMethod is the contract. CashPayment implements it directly. Card and wallet both take money from a balance, so they share an abstract class ElectronicPayment; only the fee differs (in this example the wallet adds 1 %). The enum PaymentStatus allows only Paid or Failed, and the trait KeepsHistory gives Checkout its log. Classes that must not be changed are final.

ئەرکەکە. شوێنی پارەدان دەبێت ⁦37,000⁩ دینار بە پارەی نەختینە، بە کارتی بانکی یان بە جزدانی مۆبایل وەربگرێت، «دراو» یان «سەرنەکەوتوو» ڕابگەیەنێت و مێژوویەک هەڵبگرێت. دیزاینەکە. PaymentMethod گرێبەستەکەیە. CashPayment ڕاستەوخۆ جێبەجێی دەکات. کارت و جزدان هەردووکیان پارە لە باڵانسێک دەبەن، بۆیە هاوبەشیان هەیە، ElectronicPayment؛ تەنیا کرێکە جیاوازە (لەم نموونەیەدا جزدانەکە ⁦1%⁩ زیاد دەکات). PaymentStatus تەنیا Paid یان Failed ڕێگە دەدات و KeepsHistory تۆمارەکەی دەداتە Checkout. ئەو نابێت بگۆڕدرێن finalـن.

The till: interface, abstract class, trait and enum togetherشوێنی پارەدان: نێوبەستە، کلاسی ڕووت، ترەیت و ئینەم پێکەوەworked.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
enum PaymentStatus: string
{
    case Paid = "paid";
    case Failed = "failed";
}

interface PaymentMethod
{
    public function pay(int $amount): PaymentStatus;
    public function describe(): string;
}

trait KeepsHistory
{
    private array $history = [];

    protected function remember(string $event): void
    {
        $this->history[] = $event;
    }

    public function history(): array
    {
        return $this->history;
    }
}

final class CashPayment implements PaymentMethod
{
    public function __construct(private int $given) {}

    public function pay(int $amount): PaymentStatus
    {
        return $this->given >= $amount ? PaymentStatus::Paid : PaymentStatus::Failed;
    }

    public function describe(): string
    {
        return "cash";
    }
}

abstract class ElectronicPayment implements PaymentMethod
{
    public function __construct(protected int $balance) {}

    abstract protected function feePercent(): int;

    public function pay(int $amount): PaymentStatus
    {
        $cost = $amount + intdiv($amount * $this->feePercent(), 100);
        if ($cost > $this->balance) {
            return PaymentStatus::Failed;
        }
        $this->balance -= $cost;
        return PaymentStatus::Paid;
    }
}

final class CardPayment extends ElectronicPayment
{
    protected function feePercent(): int { return 0; }
    public function describe(): string { return "bank card"; }
}

final class WalletPayment extends ElectronicPayment
{
    protected function feePercent(): int { return 1; }
    public function describe(): string { return "mobile wallet"; }
}

class Checkout
{
    use KeepsHistory;

    public function finish(int $total, PaymentMethod $method): PaymentStatus
    {
        $status = $method->pay($total);
        $this->remember(number_format($total) . " IQD by " . $method->describe() . ": " . $status->value);
        return $status;
    }
}

$till = new Checkout();
$till->finish(37000, new CashPayment(40000));
$till->finish(37000, new CardPayment(100000));
$till->finish(37000, new WalletPayment(30000));

foreach ($till->history() as $i => $line) {
    echo $i + 1, ". ", $line, "\n";
}
Explain the codeڕوونکردنەوەی کۆدەکە
Lineدێڕی 2-6

The only two results a payment can have.

تەنیا دوو ئەنجام کە پارەدانێک دەتوانێت هەیبێت.

Lineدێڕی 8-12

The contract every payment method signs.

ئەو گرێبەستەی هەموو ڕێگایەکی پارەدان واژووی دەکات.

Lineدێڕی 29-35

Cash: paid if the customer gave enough.

پارەی نەختینە: دراو ئەگەر کڕیار بەشی پێویستی دابێت.

Lineدێڕی 44-59

Shared code for card and wallet; each child gives only its fee.

کۆدی هاوبەش بۆ کارت و جزدان؛ هەر تەنیا کرێی خۆی دەدات.

Lineدێڕی 52

37,000 + 1 % = 37,370 for the wallet; its balance of 30,000 is not enough.

⁦37,000 + 1% = 37,370⁩ بۆ جزدانەکە؛ باڵانسی ⁦30,000⁩ـی بەس نییە.

Lineدێڕی 73-83

Checkout depends only on the interface and records each payment with the trait.

Checkout تەنیا پشت بە دەبەستێت و هەر پارەدانێک بە تۆمار دەکات.

A class diagram. At the top the interface PaymentMethod with pay(int): PaymentStatus and describe(): string. final CashPayment (- $given) and abstract ElectronicPayment (# $balance, feePercent(): int abstract, pay() checks the balance) implement it, shown by dashed lines with hollow triangles. final CardPayment (fee 0 %) and final WalletPayment (fee 1 %) extend ElectronicPayment, shown by solid lines with a hollow triangle. Checkout (use KeepsHistory; finish(int, PaymentMethod)) uses PaymentMethod and the enum PaymentStatus (Paid | Failed), shown by dotted arrows. A legend explains the three kinds of line.هێڵکارییەکی کلاس. لە سەرەوە نێوبەستەی PaymentMethod بە pay(int): PaymentStatus و describe(): string. final CashPayment (- $given) و abstract ElectronicPayment (# $balance، feePercent(): int ڕووت، pay() باڵانس دەپشکنێت) جێبەجێی دەکەن، بە هێڵی پچڕپچڕ و سێگۆشەی بەتاڵ. final CardPayment (کرێ 0%) و final WalletPayment (کرێ 1%) لە ElectronicPayment extends دەکەن، بە هێڵی پڕ و سێگۆشەیەکی بەتاڵ. Checkout (use KeepsHistory; finish(int, PaymentMethod)) PaymentMethod و ئینەمی PaymentStatus (Paid | Failed) بەکار دەهێنێت، بە تیری خاڵخاڵ. ڕوونکردنەوەیەک سێ جۆری هێڵەکە ڕوون دەکاتەوە.
Who connects to whom in the checkout program.لە بەرنامەی checkoutـدا کێ بە کێوە بەستراوە.CêIT

Common mistakesهەڵە باوەکان

All messages below come from PHP 8.2. Most of them appear as soon as PHP reads the class, before any line of the program runs.

هەموو پەیامەکانی خوارەوە لە ⁦PHP 8.2⁩ـەوە هاتوون. زۆربەیان هەر کە PHP دەخوێنێتەوە دەردەکەون، پێش جێبەجێبوونی هیچ دێڕێکی بەرنامەکە.

Inheritance, interface and trait mistakes, with PHP 8.2's real messages.هەڵەکانی ، و ، بە پەیامە ڕاستەقینەکانی ⁦PHP 8.2⁩.
MistakeهەڵەWhat PHP saysPHP چی دەڵێتFixچاککردن
A child constructor without parent::__construct() بەبێ parent::__construct()Error: Typed property Product::$name must not be accessed before initializationCall parent::__construct(...) in the child's constructor.لە parent::__construct(...) بانگ بکە.
class Delivery extends Product, Service {}Parse error: syntax error, unexpected token ",", expecting "{"One parent only; share the rest with interfaces or traits.تەنیا یەک باوک؛ باقییەکە بە یان هاوبەش بکە.
class CardPayment implements PaymentMethod extends Payment {}Parse error: syntax error, unexpected token "extends", expecting "{"extends first, then implements.یەکەم extends، پاشان implements.
A method of the interface is missing نییەClass WalletPayment contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (PaymentMethod::pay)Write the missing method with the same signature. نەبووەکە بە هەمان سەرەدێڕ بنووسە.
protected function label() over a public oneprotected function label() لەسەر یەکێکی publicAccess level to FreshProduct::label() must be public (as in class Product)Keep the same visibility or make it wider.هەمان بهێڵەوە یان فراوانتری بکە.
new Payment(37000) // abstractError: Cannot instantiate abstract class PaymentMake an object of a finished child class. لە تەواو دروست بکە.
new PaymentMethod() // interfaceError: Cannot instantiate interface PaymentMethodMake an object of a class that implements it. لە دروست بکە کە جێبەجێی دەکات.
A class with pay() but no implements بە pay() بەڵام بەبێ implementsTypeError: checkout(): Argument #2 ($method) must be of type PaymentMethod, Coupon givenAdd implements PaymentMethod to the class.implements PaymentMethod بۆ زیاد بکە.
class CheapCash extends CashPayment // finalClass CheapCash cannot extend final class CashPaymentDo not extend it; use it, or implement the interface yourself.extendsـی مەکە؛ بەکاری بهێنە یان خۆت جێبەجێ بکە.
implements before extendsimplements پێش extendsimplements_first.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod {}
class Payment {}
class CardPayment implements PaymentMethod extends Payment {}

Exercisesڕاهێنانەکان

Exerciseڕاهێنان1. Delivery prices (abstract class)١. نرخی گەیاندن (کلاسی ڕووت)

Write an abstract class Delivery with an abstract method cost(float $km): int and a finished method quote(float $km): string that prints the class name and the cost (static::class gives the class name of the object). Write BikeDelivery (1,000 IQD + 250 per km) and CarDelivery (2,000 IQD + 400 per km), and print a quote for 5 km from each.

Delivery بنووسە بە cost(float $km): int و تەواوی quote(float $km): string کە ناوی و تێچووەکە چاپ دەکات (static::class ناوی دەدات). BikeDelivery (⁦1,000⁩ دینار + ⁦250⁩ بۆ هەر کیلۆمەترێک) و CarDelivery (⁦2,000⁩ دینار + ⁦400⁩ بۆ هەر کیلۆمەترێک) بنووسە و بۆ 5 کیلۆمەتر نرخی هەریەکەیان چاپ بکە.

Hintئاماژە

abstract public function cost(float $km): int; has no body.

abstract public function cost(float $km): int; جەستەی نییە.

Show answerوەڵامەکە پیشان بدە

quote() is written once in the parent; each child gives only its own cost().

quote() تەنیا یەک جار لە باوکدا نووسراوە؛ هەر تەنیا cost()ـی خۆی دەدات.

ex_delivery.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
abstract class Delivery
{
    abstract public function cost(float $km): int;

    public function quote(float $km): string
    {
        return static::class . ": " . number_format($this->cost($km)) . " IQD for $km km";
    }
}

class BikeDelivery extends Delivery
{
    public function cost(float $km): int
    {
        return 1000 + (int) round(250 * $km);
    }
}

class CarDelivery extends Delivery
{
    public function cost(float $km): int
    {
        return 2000 + (int) round(400 * $km);
    }
}

foreach ([new BikeDelivery(), new CarDelivery()] as $d) {
    echo $d->quote(5), "\n";
}
Exerciseڕاهێنان2. Discounts (interface and polymorphism)٢. داشکاندن (نێوبەستە و فرەشێوەیی)

Write an interface Discount with apply(int $price): int, and two classes: PercentDiscount (for example 10 %) and FixedDiscount (for example 2,000 IQD off, but never below 0). Write a function finalPrice(int $price, Discount $discount): int and test it with 15,000 IQD and with 1,500 IQD.

Discount بنووسە بە apply(int $price): int و دوو : PercentDiscount (بۆ نموونە ⁦10%⁩) و FixedDiscount (بۆ نموونە ⁦2,000⁩ دینار کەمکردنەوە، بەڵام هەرگیز لە 0 کەمتر نا). finalPrice(int $price, Discount $discount): int بنووسە و بە ⁦15,000⁩ دینار و ⁦1,500⁩ دینار تاقیی بکەرەوە.

Hintئاماژە

Each class keeps its own number (percent or amount) as a promoted private property.

هەر ژمارەی خۆی (ڕێژە یان بڕ) وەک privateـی بەرزکراوە هەڵدەگرێت.

Show answerوەڵامەکە پیشان بدە

finalPrice() does not know which discount it gets; max(0, …) keeps the price from going below zero.

finalPrice() نازانێت کام داشکاندن وەردەگرێت؛ max(0, …) ناهێڵێت نرخەکە لە سفر کەمتر بێت.

ex_discount.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface Discount
{
    public function apply(int $price): int;
}

class PercentDiscount implements Discount
{
    public function __construct(private int $percent) {}

    public function apply(int $price): int
    {
        return $price - intdiv($price * $this->percent, 100);
    }
}

class FixedDiscount implements Discount
{
    public function __construct(private int $amount) {}

    public function apply(int $price): int
    {
        return max(0, $price - $this->amount);
    }
}

function finalPrice(int $price, Discount $discount): int
{
    return $discount->apply($price);
}

echo finalPrice(15000, new PercentDiscount(10)), "\n";
echo finalPrice(15000, new FixedDiscount(2000)), "\n";
echo finalPrice(1500, new FixedDiscount(2000)), "\n";
Exerciseڕاهێنان3. Tea sizes (enum)٣. قەبارەی چا (ئینەم)

A tea house sells small, medium and large glasses for 500, 750 and 1,000 IQD. Write a backed enum TeaSize with the values "S", "M" and "L" and a method price(). The waiter wrote the order ["S", "L", "L", "M"]: print the total, then show what tryFrom("XL") gives.

چایخانەیەک پەرداخی بچووک، مامناوەند و گەورە بە ⁦500⁩، ⁦750⁩ و ⁦1,000⁩ دینار دەفرۆشێت. نرخداری TeaSize بنووسە بە ⁦"S"⁩، ⁦"M"⁩ و ⁦"L"⁩ و price(). گارسۆنەکە داواکاریی ["S", "L", "L", "M"]ـی نووسیوە: کۆی گشتی چاپ بکە، پاشان پیشانی بدە tryFrom("XL") چی دەدات.

Hintئاماژە

match ($this) { TeaSize::Small => 500, … } inside price().

match ($this) { TeaSize::Small => 500, … } لە ناو price()ـدا.

Show answerوەڵامەکە پیشان بدە

TeaSize::from($code) turns each letter into a case; an unknown size gives null with tryFrom().

TeaSize::from($code) هەر پیتێک دەکات بە caseـێک؛ قەبارەیەکی نەناسراو بە tryFrom() null دەدات.

ex_teasize.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
enum TeaSize: string
{
    case Small = "S";
    case Medium = "M";
    case Large = "L";

    public function price(): int
    {
        return match ($this) {
            TeaSize::Small => 500,
            TeaSize::Medium => 750,
            TeaSize::Large => 1000,
        };
    }
}

$order = ["S", "L", "L", "M"];          // what the waiter wrote down
$total = 0;
foreach ($order as $code) {
    $total += TeaSize::from($code)->price();
}
echo "Total: ", number_format($total), " IQD\n";
var_dump(TeaSize::tryFrom("XL"));
Exerciseڕاهێنان4. Money formatting (trait)٤. ڕێکخستنی بڕی پارە (ترەیت)

Both Product and a Receipt class need to show amounts as "15,000 IQD". Put a method money(int $amount): string in a trait FormatsMoney, use it in both classes, and print a product label and a receipt line.

هەردوو Product و Receipt پێویستیان بەوەیە بڕی پارە وەک ⁦"15,000 IQD"⁩ پیشان بدەن. money(int $amount): string بخە ناو FormatsMoney، لە هەردوو بەکاری بهێنە و لەیبڵی بەرهەمێک و دێڕێکی پسوولە چاپ بکە.

Hintئاماژە

use FormatsMoney; goes inside each class body.

use FormatsMoney; دەچێتە ناو جەستەی هەر .

Show answerوەڵامەکە پیشان بدە

The formatting code exists once, although Product and Receipt have no common parent.

کۆدی ڕێکخستنەکە تەنیا یەک جار هەیە، هەرچەندە Product و Receipt هیچ باوکێکی هاوبەشیان نییە.

ex_trait_money.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
trait FormatsMoney
{
    public function money(int $amount): string
    {
        return number_format($amount) . " IQD";
    }
}

class Product
{
    use FormatsMoney;

    public function __construct(public string $name, public int $price) {}

    public function label(): string
    {
        return $this->name . ": " . $this->money($this->price);
    }
}

class Receipt
{
    use FormatsMoney;

    public function line(string $what, int $amount): string
    {
        return str_pad($what, 10) . $this->money($amount);
    }
}

echo (new Product("Honey", 15000))->label(), "\n";
echo (new Receipt())->line("Total", 37000), "\n";
Exerciseڕاهێنان5. Read the error٥. هەڵەکە بخوێنەرەوە

A student writes class WalletPayment implements PaymentMethod with a method send(int $amount) instead of pay(int $amount). Before running it, say which message PHP will print and on which line, then fix the class.

قوتابییەک class WalletPayment implements PaymentMethod دەنووسێت بە send(int $amount) لەبری pay(int $amount). پێش جێبەجێکردنی، بڵێ PHP چ پەیامێک و لە کام دێڕدا چاپ دەکات، پاشان چاک بکە.

Show answerوەڵامەکە پیشان بدە

PHP stops at the class line itself (line 7), because the contract is broken as soon as the class is read. The fix is to rename send() to pay() with the same signature as in the interface.

PHP لە دێڕی خودی (دێڕی ⁦7⁩) ڕادەوەستێت، چونکە هەر کە دەخوێندرێتەوە گرێبەستەکە شکاوە. چاککردنەکە گۆڕینی ناوی send()ـە بۆ pay() بە هەمان سەرەدێڕی ناو .

interface_missing.phpPHPRuns on the serverلەسەر ڕاژەکاری وێب کار دەکات
<?php
interface PaymentMethod
{
    public function pay(int $amount): string;
}

class WalletPayment implements PaymentMethod
{
    public function send(int $amount): string    // wrong name: pay() is missing
    {
        return "Sent " . $amount;
    }
}

Summaryکورتە

  • class Child extends Parent: the child receives the parent's public and protected members. One parent only; use the "is a" test.
  • A child with its own constructor must call parent::__construct(...); PHP never does it for you.
  • Overriding writes a new version of a method; parent::method() reaches the old one. Visibility may only stay the same or widen. #[\Override] (8.3) checks the name.
  • protected: the class and its family. final: no child class, or no override.
  • An abstract class is half finished: no new, children write its abstract methods. An interface is a pure contract of public methods; a class may implement many.
  • Polymorphism: code that asks for an interface works with every class that implements it, even future ones.
  • A trait copies methods into unrelated classes with use. An enum (8.1) is a fixed list of cases; use tryFrom() for untrusted input.
  • Namespaces group names like folders; an autoloader (spl_autoload_register(), or Composer's vendor/autoload.php) loads each class file when it is first needed.
  • Everything here ran on PHP 8.2 and works the same on 8.3, 8.4 and 8.5. Next, classes meet the database in Object-Relational Mapping.
  • class Child extends Parent: بەشە public و protectedـەکانی وەردەگرێت. تەنیا یەک باوک؛ تاقیکردنەوەی «جۆرێکە لە» بەکار بهێنە.
  • کە خۆی هەیە دەبێت parent::__construct(...) بانگ بکات؛ PHP هەرگیز بۆت ناکات.
  • وەشانێکی نوێی دەنووسێت؛ parent::method() دەگاتە کۆنەکە. تەنیا دەتوانێت وەک خۆی بمێنێتەوە یان فراوانتر بێت. #[\Override] ⁦(8.3)⁩ ناوەکە دەپشکنێت.
  • protected: و خێزانەکەی. final: هیچ ، یان هیچ دووپاتە پێناسەکردنەوەیەک.
  • نیوەتەواوە: newـی نییە و دەنووسن. گرێبەستێکی پاکی publicـە؛ دەتوانێت چەندینیان جێبەجێ بکات.
  • : ئەو کۆدەی داوای دەکات لەگەڵ هەموو ئەو جێبەجێی دەکەن کار دەکات، تەنانەت ئەوانەی داهاتووش.
  • بە use کۆپی دەکاتە ناو بێپەیوەندی. ⁦(8.1)⁩ لیستێکی جێگیری caseـەکانە؛ بۆ نامتمانە tryFrom() بەکار بهێنە.
  • ناوەکان وەک گرووپ دەکات؛ بارکەرێک (spl_autoload_register()، یان vendor/autoload.phpـی Composer) هەر ئەو کاتە بار دەکات کە بۆ یەکەم جار پێویست بێت.
  • هەموو شتێکی ئێرە لەسەر ⁦PHP 8.2⁩ جێبەجێ کرا و لەسەر ⁦8.3⁩، ⁦8.4⁩ و ⁦8.5⁩ هەمان شێوە کار دەکات. دواتر، لە ⁦Object-Relational Mapping⁩ـدا بە دەبەسترێن.

Lesson complete!وانەکە تەواو بوو!

Well done. Review the key terms, then go on to the next lesson.دەستخۆش. سەیرێکی زاراوە سەرەکییەکان بکەرەوە، پاشان بڕۆ بۆ وانەی داهاتوو.

Key termsزاراوە سەرەکییەکان

Next lessonوانەی داهاتوو

PHP Superglobalsسووپەرگلۆباڵەکانی PHP

References and further readingسەرچاوەکان و خوێندنەوەی زیاتر

This lecture is a first edition. The Kurdish text and terminology are waiting for review by Kurdish IT teachers.ئەم وانەیە چاپی یەکەمە. دەقە کوردییەکە و زاراوەکانی چاوەڕێی پێداچوونەوەی مامۆستایانی کوردی بواری IT ن.

Appearanceڕووکار

Modeدۆخ
Styleشێواز
Coloursڕەنگەکان

Saved on this device only.تەنیا لەسەر ئەم ئامێرە پاشەکەوت دەکرێت.