PHP / Constructor ба destructor

Constructor ба destructor

Өмнөх хичээлд __construct() аргыг товчхон танилцсан. Энэ хичээлд constructor-ийн бүх боломжуудыг гүнзгийрүүлэн судална. Мөн түүний эсрэг хос болох __destruct() — объект устах мөчид ажилладаг арга — тай танилцана. Эдгээр хоёр арга нь PHP классын амьдралын мөчлөг (lifecycle)-ийг удирддаг чухал хэрэгсэл юм.

Constructor — объект төрөх мөч

__construct() нь new гэсэн үгээр объект үүсгэх мөчид автоматаар нэг удаа дуудагддаг. Шинж чанаруудыг эхлүүлэх, шаардлагатай нөхцөл шалгах, нөөц бэлдэх зэрэгт ашигладаг:

php
<?php
class Өгөгдлийн_сан {
    private string $хост;
    private string $нэр;
    private bool   $холбогдсон = false;

    public function __construct(string $хост, string $нэр) {
        $this->хост = $хост;
        $this->нэр  = $нэр;

        // Холболтыг constructor-д эхлүүлнэ
        $this->холбох();
    }

    private function холбох(): void {
        // Жинхэнэ програмд PDO холболт нээнэ
        $this->холбогдсон = true;
        echo "'{$this->нэр}' өгөгдлийн санд холбогдлоо.\n";
    }

    public function холбогдсон_эсэх(): bool {
        return $this->холбогдсон;
    }
}

$дб = new Өгөгдлийн_сан("localhost", "myapp");
echo $дб->холбогдсон_эсэх() ? "Холбогдсон" : "Холбогдоогүй";
// "myapp" өгөгдлийн санд холбогдлоо.
// Холбогдсон
?>

Constructor-д шалгалт хийж, буруу утга ирвэл Exception шиддэг нь шилдэг дадал:

php
<?php
class Цалин {
    public float $дүн;

    public function __construct(float $дүн) {
        if ($дүн < 0) {
            throw new InvalidArgumentException("Цалин сөрөг байж болохгүй.");
        }
        $this->дүн = $дүн;
    }
}

try {
    $зөв   = new Цалин(500_000);   // ажиллана
    $буруу = new Цалин(-1000);     // Exception шидэнэ
} catch (InvalidArgumentException $e) {
    echo "Алдаа: " . $e->getMessage(); // Алдаа: Цалин сөрөг байж болохгүй.
}
?>

PHP 8 — constructor property promotion

PHP 8.0-аас эхлэн constructor-д шинж чанарыг маш богино хэлбэрээр зарлаж болдог болсон. public/private/protected гэж параметрийн өмнө бичихэд автоматаар шинж чанар үүсэж, утга онооно:

php
<?php
// Уламжлалт хэлбэр — давтамжтай
class Хэрэглэгч_Хуучин {
    public string $нэр;
    public string $имэйл;
    private int   $нас;

    public function __construct(string $нэр, string $имэйл, int $нас) {
        $this->нэр   = $нэр;
        $this->имэйл = $имэйл;
        $this->нас   = $нас;
    }
}

// PHP 8 богино хэлбэр — яг ижил үр дүн
class Хэрэглэгч {
    public function __construct(
        public string $нэр,
        public string $имэйл,
        private int   $нас,
    ) {}

    public function нас_авах(): int {
        return $this->нас;
    }
}

$х = new Хэрэглэгч("Болд", "bold@example.mn", 25);
echo $х->нэр;        // Болд
echo $х->нас_авах(); // 25
?>

Хоёр хэлбэр яг ижил ажилладаг — PHP 8 хувилбар зүгээр л богино бичлэг юм.

Destructor — объект устах мөч

__destruct() нь объект санах ойгоос устах мөчид автоматаар дуудагддаг. Нээсэн файл хаах, log бичих, холболт таслах зэрэгт ашигладаг:

php
<?php
class Файл_Зохицуулагч {
    private mixed $файл;
    private string $нэр;

    public function __construct(string $нэр) {
        $this->нэр  = $нэр;
        $this->файл = fopen($нэр, 'a');
        echo "'{$нэр}' файл нээгдлээ.\n";
    }

    public function бичих(string $мөр): void {
        fwrite($this->файл, $мөр . "\n");
    }

    public function __destruct() {
        if ($this->файл) {
            fclose($this->файл);
            echo "'{$this->нэр}' файл хаагдлаа.\n";
        }
    }
}

{
    $лог = new Файл_Зохицуулагч('app.log');
    $лог->бичих("[INFO] Програм эхэллээ");
    $лог->бичих("[INFO] Хэрэглэгч нэвтэрлээ");
} // Энд $лог scope-оос гарахад destructor автоматаар дуудагдана

// Гаралт:
// 'app.log' файл нээгдлээ.
// 'app.log' файл хаагдлаа.
?>

fclose() гэж гараар дуудахаа мартсан ч destructor нь автоматаар хаадаг тул нөөц алдагдахаас хамгаалдаг.

Constructor ба destructor хамт

php
<?php
class Холболт {
    private static int $тоолуур = 0;

    public function __construct(public string $нэр) {
        self::$тоолуур++;
        echo "Холболт #{" . self::$тоолуур . "} нээгдлээ: {$this->нэр}\n";
    }

    public function __destruct() {
        echo "Холболт хаагдлаа: {$this->нэр}\n";
        self::$тоолуур--;
    }

    public static function идэвхтэй_тоо(): int {
        return self::$тоолуур;
    }
}

$а = new Холболт("үндсэн");   // Холболт #1 нээгдлээ: үндсэн
$б = new Холболт("нөөц");     // Холболт #2 нээгдлээ: нөөц
echo Холболт::идэвхтэй_тоо() . " холболт идэвхтэй\n"; // 2
unset($а);                     // Холболт хаагдлаа: үндсэн
echo Холболт::идэвхтэй_тоо() . " холболт идэвхтэй\n"; // 1
?>

Дараагийн хичээлд:

OOP-н хамгийн хүчтэй механизмуудын нэг болох өвлөлт (inheritance)-ийг судална. Нэг классаас шинэ класс үүсгэж, шинж чанар болон аргуудыг хэрхэн өвлөн авах, parent:: гэж хэрхэн ашиглахыг жишээгээр үзнэ.