PHP — ნულიდან პრაქტიკულ ვებ-სერვისამდე

ეს არის PHP-ის სრული სახელმძღვანელო ქართულ ენაზე: საფუძვლებიდან (ცვლადები, ტიპები, ოპერატორები) — საშუალო დონემდე (ფუნქციები, მასივები, OOP, ფაილები, სესიები, უსაფრთხოება, REST API).

თითოეული თავი შეიცავს თეორიას, სინტაქსს, კოდის მაგალითებს, ხშირ შეცდომებს, საუკეთესო პრაქტიკას, სავარჯიშოებსა და ინტერაქტიულ ქვიზებს. ყველა მაგალითი დაწერილია თანამედროვე PHP 8.2+ სტილში.

20 თავი 181 თემა 20 თავი + ქვიზები

1 📘 თავი 1. PHP-ის შესავალი

რა არის PHP, სად გამოიყენება, როგორ სრულდება კოდი, როგორ დავაყენოთ და დავწეროთ პირველი პროგრამა.

რა არის PHP PHP 8.2+

PHP (PHP: Hypertext Preprocessor) არის სერვერის მხარეს მომუშავე (server-side) სკრიპტული ენა, რომელიც განკუთვნილია უპირველესად ვებ-აპლიკაციების შესაქმნელად.

PHP ინტერპრეტირებადია — კოდი არ ითარგმნება წინასწარ მანქანურ ენაზე, არამედ სრულდება სერვერზე მოთხოვნის დროს. შედეგად ბრაუზერს უბრუნდება უკვე მზა HTML.

PHP დინამიკურად ტიპიზებულია: ცვლადის ტიპს ენა თავად ადგენს მნიშვნელობის მიხედვით.

მაგალითები

PHP HTML-ის შიგნით

PHP კოდი ჩაშენებულია HTML-ში და სერვერზე გარდაიქმნება ტექსტად.

PHP
<?php
// დინამიკური მნიშვნელობა, რომელსაც სერვერი ითვლის
$weekday = date("l");
?>
<!DOCTYPE html>
<html lang="ka">
<body>
    <h1>გამარჯობა!</h1>
    <p>დღეს არის: <?= $weekday ?></p>
</body>
</html>
💡 რჩევა: PHP-ის ფაილს აქვს გაფართოება .php და სრულდება მხოლოდ PHP-ის მქონე ვებ-სერვერზე.

✓ საუკეთესო პრაქტიკა

  • PHP ძირითადად backend-ისთვისაა — ლოგიკა, მონაცემთა ბაზა, API. ვიზუალს HTML/CSS წყვეტს.
  • გამოიყენე უახლესი სტაბილური ვერსია (PHP 8.2+) — სწრაფია და უსაფრთხო.

✗ ხშირი შეცდომები

  • PHP-ს ხშირად ურევენ JavaScript-ში — JS ბრაუზერში სრულდება, PHP კი სერვერზე.
  • ფიქრი, რომ PHP მხოლოდ „ძველი" ენაა — სინამდვილეში თანამედროვე PHP 8 ძალიან განსხვავდება ძველისგან.

სად გამოიყენება PHP

PHP-ზე მუშაობს ინტერნეტის უზარმაზარი ნაწილი. ის გამოიყენება:

  • ვებ-საიტების backend — სერვერული ლოგიკა, ფორმების დამუშავება, ავტორიზაცია;
  • CMS სისტემები — WordPress, Drupal, Joomla (WordPress მსოფლიო საიტების დიდ ნაწილს ამუშავებს);
  • REST API-ები — მობილური და frontend აპლიკაციებისთვის;
  • e-commerce — Magento, WooCommerce;
  • CLI სკრიპტები — ავტომატიზაცია, cron დავალებები.
💡 რჩევა: თუ ვებ-პროგრამირებას სწავლობ, PHP-ის ცოდნა დიდ უპირატესობას გაძლევს — ბაზარზე ბევრი ვაკანსიაა.

🌍 რეალური გამოყენება

ცნობილი ფრეიმვორკები: Laravel და Symfony — სწორედ PHP-ზეა აგებული და დიდ პროექტებში გამოიყენება.

შესრულების მოდელი

PHP-ის შესრულების მოდელი

PHP-ის სასიცოცხლო ციკლი მარტივია:

  1. მომხმარებელი ბრაუზერში ითხოვს გვერდს (request);
  2. ვებ-სერვერი PHP ფაილს გადასცემს PHP ინტერპრეტატორს;
  3. ინტერპრეტატორი ასრულებს კოდს და გამოიმუშავებს HTML-ს;
  4. სერვერი ამ HTML-ს უგზავნის ბრაუზერს (response).

მნიშვნელოვანია: მომხმარებელი ვერ ხედავს PHP-ის კოდს — მხოლოდ შედეგ HTML-ს. ეს განასხვავებს PHP-ს (server-side) JavaScript-ისგან (client-side).

მნიშვნელობები

მნიშვნელობააღწერა
Client-side სრულდება ბრაუზერში (HTML, CSS, JavaScript).
Server-side სრულდება სერვერზე (PHP) — კოდი მომხმარებელს არ უჩანს.

✗ ხშირი შეცდომები

  • მცდარი წარმოდგენა, რომ $_GET-ით მიღებული მონაცემი უსაფრთხოა — ის მომხმარებლისგან მოდის და უნდა გადამოწმდეს.

ინსტალაცია

PHP-ის ინსტალაცია

PHP-ის გასაშვებად რამდენიმე გზა არსებობს:

  • php.net-დan PHP-ის ჩამოტვირთვა და დაყენება;
  • XAMPP / Laragon — მზა პაკეტი (Apache + PHP + MySQL);
  • ჩაშენებული სერვერი — განვითარებისთვის საუკეთესო.

ვერსიის შესამოწმებლად ტერმინალში აკრიფე php -v.

სინტაქსი

Syntax
php -v
php -S localhost:8000

მაგალითები

ჩაშენებული სერვერის გაშვება

საქაღალდეში, სადაც index.php დევს, გაუშვი:

Terminal
# გადადი პროექტის საქაღალდეში და გაუშვი სერვერი
php -S localhost:8000

# ბრაუზერში გახსენი:
# http://localhost:8000
💡 რჩევა: Windows-ზე ყველაზე მარტივია Laragon ან XAMPP.

✓ საუკეთესო პრაქტიკა

  • განვითარებისთვის გამოიყენე php -S — დამატებითი კონფიგურაცია არ სჭირდება.
  • პროდაქშენში გამოიყენე სრულფასოვანი სერვერი (Nginx/Apache + PHP-FPM).

პირველი პროგრამა

PHP-ში ტექსტის გამოსატანად გამოიყენება echo. შევქმნათ ფაილი index.php:

მაგალითები

Hello, World!
PHP
<?php
echo "გამარჯობა, სამყარო!";
რამდენიმე ხაზი

echo და print გამოაქვს ტექსტი; \n ახალ ხაზს ნიშნავს ორმაგ ბრჭყალებში.

PHP
<?php
echo "ხაზი 1\n";
echo "ხაზი 2\n";
print "print-იც მუშაობს\n";

✓ საუკეთესო პრაქტიკა

  • გამოიყენე გასაგები სახელები და სუფთა ფორმატირება თავიდანვე.

✗ ხშირი შეცდომები

  • წერტილ-მძიმის ; დავიწყება ბრძანების ბოლოს — გამოიწვევს syntax error.
  • \n ერთმაგ ბრჭყალებში არ მუშაობს როგორც ახალი ხაზი — საჭიროა ორმაგი ბრჭყალები.

PHP ტეგები

PHP კოდი იწერება სპეციალურ ტეგებში. ყველაფერი ამ ტეგების გარეთ — ჩვეულებრივი HTML-ია.

  • <?php ... ?> — სტანდარტული ტეგი;
  • <?= ... ?> — მოკლე echo (იგივეა, რაც <?php echo ... ?>).

თუ ფაილი მხოლოდ PHP-ს შეიცავს, დამხურავი ?> არ არის სავალდებულო — რეკომენდებულიც კი არ არის, რადგან ზედმეტი სიმბოლოები პრობლემას ქმნის.

სინტაქსი

Syntax
<?php
  // PHP კოდი
?>

<?= $value ?>

მაგალითები

HTML-ისა და PHP-ის შერევა
PHP
<?php $name = "ნინო"; ?>
<!DOCTYPE html>
<html lang="ka">
<body>
    <h1>მოგესალმებით, <?= $name ?>!</h1>
    <?php if ($name !== "") : ?>
        <p>კარგი დღე გისურვებთ.</p>
    <?php endif; ?>
</body>
</html>
💡 რჩევა: მხოლოდ PHP-ის ფაილებში დამხურავი ?> საერთოდ არ დაწერო.

✗ ხშირი შეცდომები

  • დამხურავი ?>-ის შემდეგ ცარიელი ხაზი — შესაძლოა გამოიწვიოს „headers already sent" შეცდომა.
  • მოკლე ტეგების <? ?> გამოყენება — ყველა სერვერზე არ არის ჩართული; ყოველთვის წერე <?php.

სავარჯიშოები — თავი 1

📝 სავარჯიშოები

გაამყარე ცოდნა შემდეგი დავალებებით. ჯერ თავად სცადე, შემდეგ გახსენი გადაწყვეტა.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დაწერე სკრიპტი, რომელიც ეკრანზე გამოიტანს შენს სახელსა და გვარს ორ ცალკე ხაზზე.

✅ გადაწყვეტა
PHP
<?php
echo "გიორგი\n";
echo "მაისურაძე\n";
საშუალო სავარჯიშო 2

HTML გვერდში, PHP ცვლადით $city, გამოიტანე წინადადება „მე ვცხოვრობ [ქალაქში]". გამოიყენე მოკლე echo ტეგი.

💡 მინიშნება
ცვლადი განსაზღვრე <?php ?> ბლოკში, შემდეგ <?= $city ?> HTML-ში.
✅ გადაწყვეტა
PHP
<?php $city = "თბილისი"; ?>
<!DOCTYPE html>
<html lang="ka">
<body>
    <p>მე ვცხოვრობ <?= $city ?>-ში.</p>
</body>
</html>
პრაქტიკული სავარჯიშო 3

გაუშვი ჩაშენებული PHP სერვერი (php -S localhost:8000) და დარწმუნდი, რომ index.php იხსნება ბრაუზერში. დაამატე გვერდზე მიმდინარე თარიღი date("Y-m-d").

✅ გადაწყვეტა
PHP
<?php
// index.php — შემდეგ: php -S localhost:8000
?>
<!DOCTYPE html>
<html lang="ka">
<body>
    <h1>ჩემი პირველი PHP გვერდი</h1>
    <p>დღევანდელი თარიღი: <?= date("Y-m-d") ?></p>
</body>
</html>

ქვიზი — თავი 1

❓ ქვიზი

აირჩიე სწორი პასუხი. დაწკაპუნებისთანავე ნახავ შედეგსა და ახსნას.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. სად სრულდება PHP კოდი?

  2. 2. რომელი ტეგი ხსნის PHP კოდს?

  3. 3. რას ნიშნავს მოკლე ტეგი <?= $x ?>?

  4. 4. რომელი ბრძანებით ვუშვებთ ჩაშენებულ სერვერს?

  5. 5. რა გაფართოება აქვს PHP ფაილს?

2 🔢 თავი 2. ცვლადები და მონაცემთა ტიპები

ცვლადები, კონსტანტები და ძირითადი ტიპები: int, float, string, bool, null და ტიპთა გარდაქმნა.

ცვლადები (Variables)

ცვლადები

ცვლადი არის „კონტეინერი" მონაცემისთვის. PHP-ში ცვლადი ყოველთვის იწყება $ სიმბოლოთი.

დასახელების წესები: სახელი იწყება ასოთი ან _-ით, შემდეგ შეიძლება ასოები, ციფრები და _. ცვლადის სახელი case-sensitive-ია: $name და $Name სხვადასხვაა.

სინტაქსი

Syntax
$variableName = value;

მაგალითები

ცვლადის შექმნა და გამოყენება
PHP
<?php
$name = "ანა";
$age = 21;
$isStudent = true;

echo "$name არის $age წლის\n";

✓ საუკეთესო პრაქტიკა

  • გამოიყენე აღწერითი სახელები: $userEmail და არა $x.
  • დაიცავი ერთი სტილი — მაგ. camelCase.

✗ ხშირი შეცდომები

  • $-ის დავიწყება — name = 5; შეცდომაა.
  • ცვლადის სახელის დაწყება ციფრით — $1user დაუშვებელია.

კონსტანტები (Constants)

კონსტანტები

კონსტანტა არის მნიშვნელობა, რომელიც არ იცვლება შესრულების მსვლელობისას. იქმნება const-ით ან define()-ით.

კონსტანტას $ არ უძღვის წინ. სახელები ჩვეულებრივ მთლიანად დიდი ასოებითაა.

სინტაქსი

Syntax
const NAME = value;
define("NAME", value);

მნიშვნელობები

მნიშვნელობააღწერა
const ცხადდება compile დროს; გამოიყენება კლასებშიც.
define() ცხადდება შესრულების დროს; შეიძლება პირობით.
PHP_INT_MAX ჩაშენებული კონსტანტა — int-ის მაქსიმუმი.

მაგალითები

const და define
PHP
<?php
const PI = 3.14159;
define("APP_NAME", "ჩემი აპლიკაცია");

echo PI . "\n";
echo APP_NAME . "\n";
// PI = 4; // შეცდომა: კონსტანტის შეცვლა შეუძლებელია
💡 რჩევა: კონფიგურაციის მნიშვნელობები (აპის სახელი, ვერსია) კარგად ჯდება კონსტანტებში.

Integer

მთელი რიცხვი (int)

int არის მთელი რიცხვი დადებითი ან უარყოფითი, ათობითი, ჰექსადეციმალური (0x), ოქტალური (0o) ან ბინარული (0b) ჩაწერით.

მაგალითები

int-ის ფორმები
PHP
<?php
$decimal = 42;
$negative = -7;
$hex = 0x1A;      // 26
$binary = 0b1010; // 10

echo PHP_INT_MAX . "\n"; // უდიდესი int
var_dump(is_int($decimal));
💡 რჩევა: intdiv(10, 3) აბრუნებს მთელ ნაწილს (3), % კი ნაშთს.

Float

წილადი რიცხვი (float)

float არის ათწილადი რიცხვი. ყურადღება: მცურავწერტილიანი არითმეტიკა ზუსტი არ არის — ეს ყველა ენაში ასეა.

მაგალითები

float და სიზუსტის ხაფანგი
PHP
<?php
$price = 19.99;
$result = 0.1 + 0.2;

var_dump($result === 0.3); // false! (0.30000000000000004)

// სწორი შედარება — დასაშვები ცდომილებით:
$epsilon = 0.00001;
var_dump(abs($result - 0.3) < $epsilon); // true

✗ ხშირი შეცდომები

  • float-ების პირდაპირი შედარება ===-ით — გამოიყენე ცდომილება (epsilon).
  • ფულის შენახვა float-ში — ფინანსებში გამოიყენე მთელი რიცხვი (თეთრები) ან სპეც-ბიბლიოთეკა.

String

სტრიქონი (string)

სტრიქონი — სიმბოლოთა მიმდევრობა. იწერება ერთმაგ ' ან ორმაგ " ბრჭყალებში.

  • ორმაგი ბრჭყალები — ცვლადებს „კითხულობს" (ინტერპოლაცია) და \n მუშაობს;
  • ერთმაგი — ყველაფერი ლიტერალურია (სწრაფიც).

გრძელი ტექსტისთვის მოსახერხებელია heredoc (ინტერპოლაციით) და nowdoc (ლიტერალური).

სინტაქსი

Syntax
$s = "ტექსტი $var";  // ინტერპოლაცია
$s = 'ტექსტი';      // ლიტერალური
$s = $a . $b;        // კონკატენაცია

მაგალითები

ბრჭყალები, ინტერპოლაცია და heredoc
PHP
<?php
$name = "ლუკა";

echo "გამარჯობა, $name\n";       // გამარჯობა, ლუკა
echo 'გამარჯობა, $name' . "\n";  // გამარჯობა, $name (ლიტერალური)
echo "ჯამი: " . (2 + 3) . "\n";  // კონკატენაცია წერტილით

$html = <<<HTML
<div>მომხმარებელი: $name</div>
HTML;
echo $html . "\n";
💡 რჩევა: რთული მნიშვნელობებისთვის გამოიყენე ფიგურული ფრჩხილები: "ფასი: {$item["price"]}".

✗ ხშირი შეცდომები

  • ცვლადის ინტერპოლაციის მოლოდინი ერთმაგ ბრჭყალებში — იქ $name ლიტერალურად დარჩება.
  • კონკატენაციისთვის +-ის გამოყენება — PHP-ში სტრიქონებს . აერთებს.

Boolean

ლოგიკური (bool)

bool იღებს მხოლოდ ორ მნიშვნელობას: true ან false. გამოიყენება პირობებში.

ზოგი მნიშვნელობა ავტომატურად ითვლება false-ად (falsy): 0, 0.0, "", "0", [] (ცარიელი მასივი), null. დანარჩენი — true (truthy).

მაგალითები

truthy / falsy
PHP
<?php
var_dump((bool) 0);    // false
var_dump((bool) "");   // false
var_dump((bool) "0");  // false
var_dump((bool) "ა");  // true
var_dump((bool) []);   // false
var_dump((bool) [0]);  // true

✗ ხშირი შეცდომები

  • "0" სტრიქონი არის falsy — ეს ხშირად აბნევს.
  • სტრიქონი "false" არის truthy (ცარიელი არაა)!

Null

null

null ნიშნავს „მნიშვნელობის არარსებობას". ცვლადი არის null, თუ მისთვის მნიშვნელობა არ მინიჭებია ან ცხადად მიენიჭა null.

შესამოწმებლად: isset($x) (არსებობს და არააა null), is_null($x).

მაგალითები

null-ის შემოწმება
PHP
<?php
$value = null;

var_dump(is_null($value)); // true
var_dump(isset($value));   // false (null-ზე isset → false)

$data = ["name" => "ია"];
echo $data["email"] ?? "ელფოსტა უცნობია"; // null coalescing
💡 რჩევა: არარსებული გასაღების უსაფრთხოდ წასაკითხად გამოიყენე ?? ოპერატორი.

ტიპთა გარდაქმნა (Type Juggling)

ტიპთა გარდაქმნა

PHP ხშირად ავტომატურად გარდაქმნის ტიპებს (type juggling). ასევე შესაძლებელია ცხადი გარდაქმნა (casting): (int), (float), (string), (bool), (array).

გამოიყენე მკაცრი შედარება === (ტიპსაც ამოწმებს), რათა აირიდო მოულოდნელი შედეგები.

სინტაქსი

Syntax
$n = (int) "42";
$s = (string) 3.14;
settype($x, "integer");

მაგალითები

casting და მკაცრი შედარება
PHP
<?php
$num = (int) "42 ვაშლი";  // 42
$str = (string) 100;       // "100"

var_dump(0 == "a");   // PHP 8-ში: false
var_dump("1" == 1);   // true (მნიშვნელობით)
var_dump("1" === 1);  // false (ტიპით განსხვავდება)

// declare(strict_types=1)-ით ფუნქციები მკაცრად ამოწმებენ ტიპებს

✓ საუკეთესო პრაქტიკა

  • ფაილის პირველი ხაზი იყოს declare(strict_types=1);.
  • ყოველთვის ამჯობინე === და !==.

✗ ხშირი შეცდომები

  • ფაილის თავში declare(strict_types=1);-ის გამოტოვება — სუსტი ტიპიზაცია ფარულ შეცდომებს ქმნის.
  • ==-ის ნდობა იქ, სადაც === სჭირდება.

სავარჯიშოები — თავი 2

📝 სავარჯიშოები

ივარჯიშე ცვლადებსა და ტიპებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

შექმენი ცვლადები $firstName, $lastName და $age; გამოიტანე წინადადება მათი გამოყენებით.

✅ გადაწყვეტა
PHP
<?php
$firstName = "მარიამ";
$lastName = "ბერიძე";
$age = 19;

echo "$firstName $lastName, $age წლის\n";
საშუალო სავარჯიშო 2

აჩვენე, რომ 0.1 + 0.2 არ უდრის 0.3-ს პირდაპირ ===-ით, შემდეგ შეადარე ცდომილებით.

💡 მინიშნება
გამოიყენე abs() და მცირე epsilon მნიშვნელობა.
✅ გადაწყვეტა
PHP
<?php
$sum = 0.1 + 0.2;
var_dump($sum === 0.3);                 // false
var_dump(abs($sum - 0.3) < 0.00001);    // true
პრაქტიკული სავარჯიშო 3

მომხმარებლის შეყვანილი ასაკი ხშირად სტრიქონია (მაგ. "25"). გადააქციე int-ად, დაუმატე 1 და გამოიტანე. გაითვალისწინე არასწორი შეყვანა.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$input = "25"; // წარმოიდგინე, რომ მოდის ფორმიდან
$age = (int) $input;

if ($age <= 0) {
    echo "არასწორი ასაკი\n";
} else {
    echo "მომავალ წელს იქნები " . ($age + 1) . " წლის\n";
}

ქვიზი — თავი 2

❓ ქვიზი

შეამოწმე ცოდნა ცვლადებსა და ტიპებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 6
  1. 1. რა სიმბოლოთი იწყება ცვლადი PHP-ში?

  2. 2. რომელი მნიშვნელობა არ არის falsy?

  3. 3. რას დააბრუნებს "1" === 1?

  4. 4. როგორ ვქმნით კონსტანტას?

  5. 5. რა მოხდება isset($x)-ში, თუ $x = null?

  6. 6. რა ჩავწეროთ ფაილის თავში მკაცრი ტიპებისთვის?

3 ➗ თავი 3. ოპერატორები

არითმეტიკული, შედარების, ლოგიკური, მინიჭების ოპერატორები, ინკრემენტი/დეკრემენტი, სამმაგი და null coalescing.

არითმეტიკული

არითმეტიკული ოპერატორები

რიცხვებზე ოპერაციებისთვის: შეკრება, გამოკლება, გამრავლება, გაყოფა, ნაშთი და ახარისხება.

მნიშვნელობები

მნიშვნელობააღწერა
+ შეკრება
- გამოკლება
* გამრავლება
/ გაყოფა (შედეგი შეიძლება float იყოს)
% ნაშთი (modulo, მთელ რიცხვებზე)
** ახარისხება (2 ** 3 = 8)

მაგალითები

არითმეტიკა
PHP
<?php
echo 7 + 3 . "\n";       // 10
echo 7 / 2 . "\n";       // 3.5
echo 7 % 3 . "\n";       // 1 (ნაშთი)
echo 2 ** 10 . "\n";     // 1024
echo intdiv(7, 2) . "\n"; // 3 (მთელი გაყოფა)
💡 რჩევა: % ლუწობის შესამოწმებლად: $n % 2 === 0.

✗ ხშირი შეცდომები

  • ნულზე გაყოფა — 10 % 0 ან intdiv(10, 0) აგდებს DivisionByZeroError-ს.

შედარება

შედარების ოპერატორები

აბრუნებენ bool-ს. გამოიყენე მკაცრი === და !==, რომელიც ტიპსაც ამოწმებს.

მნიშვნელობები

მნიშვნელობააღწერა
== ტოლია მნიშვნელობით (ტიპს უგულებელყოფს)
=== ტოლია მნიშვნელობითა და ტიპით
!= არ უდრის
!== არ უდრის (ტიპის გათვალისწინებით)
&lt; &gt; &lt;= &gt;= ნაკლები / მეტი / ნაკლები ან ტოლი / მეტი ან ტოლი
&lt;=&gt; spaceship: აბრუნებს -1, 0 ან 1

მაგალითები

მკაცრი vs ფხვიერი შედარება
PHP
<?php
var_dump(5 == "5");   // true  (მნიშვნელობით)
var_dump(5 === "5");  // false (ტიპი განსხვავდება)
var_dump(10 <=> 20);  // -1 (spaceship)
var_dump(20 <=> 20);  // 0

✓ საუკეთესო პრაქტიკა

  • თითქმის ყოველთვის გამოიყენე === და !==.
  • spaceship <=> მოსახერხებელია usort()-ში დასალაგებლად.

ლოგიკური

ლოგიკური ოპერატორები

აერთებენ ლოგიკურ პირობებს. && და || მუშაობენ short-circuit პრინციპით — თუ შედეგი უკვე ცნობილია, მეორე ოპერანდი არ გამოითვლება.

მნიშვნელობები

მნიშვნელობააღწერა
&amp;&amp; AND — ორივე true უნდა იყოს
|| OR — ერთი მაინც true
! NOT — ლოგიკის შებრუნება
and / or / xor იგივე, მაგრამ დაბალი პრიორიტეტით — ფრთხილად!

მაგალითები

short-circuit
PHP
<?php
$age = 20;
$hasTicket = true;

if ($age >= 18 && $hasTicket) {
    echo "შესვლა დაშვებულია\n";
}

// short-circuit: მარჯვენა მხარე არ შესრულდება, თუ მარცხენა false-ია
$user = null;
if ($user !== null && $user->isActive()) {
    echo "აქტიურია\n";
}

✗ ხშირი შეცდომები

  • and/or-ის გამოყენება მინიჭებასთან — $x = true and false; მისცემს $x = true-ს დაბალი პრიორიტეტის გამო. გამოიყენე &&/||.

მინიჭება

მინიჭების ოპერატორები

ანიჭებენ მნიშვნელობას ცვლადს. შემოკლებული ფორმები აერთებენ ოპერაციასა და მინიჭებას.

მნიშვნელობები

მნიშვნელობააღწერა
= მინიჭება
+= -= *= /= არითმეტიკა + მინიჭება
.= სტრიქონის მიერთება + მინიჭება
??= null coalescing მინიჭება (PHP 7.4+)

მაგალითები

შემოკლებული მინიჭება
PHP
<?php
$total = 100;
$total += 50;   // 150
$total -= 30;   // 120

$name = "გია";
$name .= " ბერია";  // "გია ბერია"

$config = [];
$config["theme"] ??= "light"; // თუ არ არსებობს, მიენიჭოს "light"
echo $config["theme"] . "\n";

++ და --

ინკრემენტი და დეკრემენტი

++ ზრდის ერთით, -- ამცირებს. პრეფიქსული (++$x) ჯერ ცვლის, შემდეგ აბრუნებს; პოსტფიქსული ($x++) ჯერ აბრუნებს, შემდეგ ცვლის.

მაგალითები

pre vs post
PHP
<?php
$a = 5;
echo $a++ . "\n"; // 5 (ჯერ გამოიტანა, შემდეგ გაზარდა)
echo $a . "\n";   // 6

$b = 5;
echo ++$b . "\n"; // 6 (ჯერ გაზარდა, შემდეგ გამოიტანა)
💡 რჩევა: ციკლებში ხშირად სულერთია $i++ თუ ++$i, მაგრამ გამოსახულებაში სხვაობა მნიშვნელოვანია.

Ternary (?:)

სამმაგი ოპერატორი

მოკლე ფორმა if/else-ისა, რომელიც აბრუნებს მნიშვნელობას: პირობა ? თუ_true : თუ_false.

შემოკლებული ?: (Elvis) აბრუნებს მარცხენა მნიშვნელობას, თუ ის truthy-ია.

სინტაქსი

Syntax
$result = $condition ? $a : $b;
$x = $value ?: "default";

მაგალითები

ternary
PHP
<?php
$age = 20;
$status = $age >= 18 ? "ზრდასრული" : "არასრულწლოვანი";
echo $status . "\n";

$name = "";
echo ($name ?: "სტუმარი") . "\n"; // "სტუმარი"
💡 რჩევა: არარსებულ ცვლადზე გამოიყენე ??, არა ?:?: notice-ს იძლევა undefined-ზე.

✗ ხშირი შეცდომები

  • ჩალაგებული ternary გაუგებარია — PHP 8-ში ფრჩხილების გარეშე ჩალაგება შეცდომაა.

Null coalescing (??)

Null Coalescing ოპერატორი

?? აბრუნებს მარცხენა ოპერანდს, თუ ის არსებობს და არ არის null; წინააღმდეგ შემთხვევაში — მარჯვენას. notice-ს არ იძლევა არარსებულ გასაღებზე.

სინტაქსი

Syntax
$x = $a ?? $b;
$x ??= $default;

მაგალითები

?? და ??=
PHP
<?php
$config = ["theme" => "dark"];

$theme = $config["theme"] ?? "light"; // "dark"
$lang  = $config["lang"] ?? "ka";     // "ka" (არ არსებობს)

echo "$theme, $lang\n";

// ჯაჭვი:
$value = $a ?? $b ?? "default";

✓ საუკეთესო პრაქტიკა

  • მომხმარებლის შეყვანის ნაგულისხმევი მნიშვნელობებისთვის $_GET["q"] ?? "" იდეალურია.

სავარჯიშოები — თავი 3

📝 სავარჯიშოები

ივარჯიშე ოპერატორებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

გამოთვალე ორი რიცხვის ჯამი, სხვაობა, ნამრავლი და ნაშთი; გამოიტანე თითოეული.

✅ გადაწყვეტა
PHP
<?php
$a = 17;
$b = 5;
echo "ჯამი: " . ($a + $b) . "\n";
echo "სხვაობა: " . ($a - $b) . "\n";
echo "ნამრავლი: " . ($a * $b) . "\n";
echo "ნაშთი: " . ($a % $b) . "\n";
საშუალო სავარჯიშო 2

ცვლადით $score (0–100) დაადგინე შეფასება ternary-ით: 60-ზე მეტი — „ჩაბარდა", სხვა — „ვერ ჩააბარა".

💡 მინიშნება
გამოიყენე $score >= 60 ? ... : ....
✅ გადაწყვეტა
PHP
<?php
$score = 72;
$result = $score >= 60 ? "ჩაბარდა" : "ვერ ჩააბარა";
echo $result . "\n";
პრაქტიკული სავარჯიშო 3

ფორმიდან მოსული $_GET["page"] შეიძლება არ არსებობდეს. დააყენე ნაგულისხმევი 1, გადააქციე int-ად და დარწმუნდი, რომ ≥ 1.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$page = (int) ($_GET["page"] ?? 1);
if ($page < 1) {
    $page = 1;
}
echo "გვერდი: $page\n";

ქვიზი — თავი 3

❓ ქვიზი

შეამოწმე ცოდნა ოპერატორებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 6
  1. 1. რას აბრუნებს 7 % 3?

  2. 2. რომელი ოპერატორი ამოწმებს მნიშვნელობასაც და ტიპსაც?

  3. 3. რას აბრუნებს $config["x"] ?? "def", თუ გასაღები „x" არ არსებობს?

  4. 4. რას გამოიტანს $a = 5; echo $a++;?

  5. 5. რას აბრუნებს 10 <=> 20?

  6. 6. რომელია short-circuit-ის სწორი აღწერა?

4 🔀 თავი 4. პირობები

გადაწყვეტილების მიღება კოდში: if, else, elseif, switch და თანამედროვე match.

if

if ასრულებს ბლოკს მხოლოდ მაშინ, თუ პირობა true-ია.

სინტაქსი

Syntax
if (condition) {
    // კოდი
}

მაგალითები

მარტივი if
PHP
<?php
$temperature = 30;

if ($temperature > 25) {
    echo "ცხელა!\n";
}

✗ ხშირი შეცდომები

  • მინიჭების = შედარების ==-ის ნაცვლად — if ($x = 5) ანიჭებს, არ ადარებს.

else

else ასრულდება, თუ if-ის პირობა false-ია.

სინტაქსი

Syntax
if (condition) {
    // ...
} else {
    // ...
}

მაგალითები

if/else
PHP
<?php
$hour = 14;

if ($hour < 12) {
    echo "დილა მშვიდობისა\n";
} else {
    echo "შუადღე მშვიდობისა\n";
}

elseif

elseif ამოწმებს დამატებით პირობებს თანმიმდევრობით. პირველივე true ბლოკი სრულდება, დანარჩენი იგნორირდება.

სინტაქსი

Syntax
if (a) { }
elseif (b) { }
else { }

მაგალითები

შეფასების სისტემა
PHP
<?php
$score = 85;

if ($score >= 90) {
    echo "A\n";
} elseif ($score >= 80) {
    echo "B\n";
} elseif ($score >= 70) {
    echo "C\n";
} else {
    echo "F\n";
}
💡 რჩევა: elseif და else if თითქმის ექვივალენტურია; ალტერნატიულ სინტაქსში (:) მხოლოდ elseif მუშაობს.

switch

switch ადარებს ერთ მნიშვნელობას რამდენიმე case-ს (ფხვიერი == შედარებით). აუცილებელია break, თორემ შესრულება „ჩავარდება" შემდეგ case-ში (fallthrough).

სინტაქსი

Syntax
switch ($x) {
    case 1: ...; break;
    default: ...;
}

მაგალითები

switch break-ით
PHP
<?php
$day = 3;

switch ($day) {
    case 1:
        echo "ორშაბათი\n";
        break;
    case 6:
    case 7:
        echo "შაბათ-კვირა\n";
        break;
    default:
        echo "სამუშაო დღე\n";
}

✗ ხშირი შეცდომები

  • break-ის დავიწყება — გამოიწვევს არასასურველ fallthrough-ს.
  • switch იყენებს ==-ს (ფხვიერ შედარებას) — ფრთხილად შერეული ტიპებისას.

match (PHP 8) PHP 8.0+

match

match არის switch-ის თანამედროვე ალტერნატივა. ის:

  • იყენებს მკაცრ === შედარებას;
  • აბრუნებს მნიშვნელობას;
  • არ აქვს fallthrough — break არ სჭირდება;
  • აგდებს UnhandledMatchError-ს, თუ შესაბამისობა ვერ მოიძებნა და default არ არის.

სინტაქსი

Syntax
$x = match($value) {
    1, 2 => "ა",
    3    => "ბ",
    default => "გ",
};

მნიშვნელობები

მნიშვნელობააღწერა
switch ფხვიერი ==, fallthrough, არ აბრუნებს მნიშვნელობას
match მკაცრი ===, აბრუნებს მნიშვნელობას, break არ სჭირდება

მაგალითები

match vs switch
PHP
<?php
$status = "active";

$label = match ($status) {
    "active"   => "აქტიური",
    "inactive" => "არააქტიური",
    "banned"   => "დაბლოკილი",
    default    => "უცნობი",
};

echo $label . "\n"; // აქტიური

// რამდენიმე მნიშვნელობა ერთ შტოში:
$type = match (5) {
    1, 2, 3 => "მცირე",
    4, 5, 6 => "საშუალო",
    default => "დიდი",
};
echo $type . "\n"; // საშუალო

✓ საუკეთესო პრაქტიკა

  • როცა მნიშვნელობას აბრუნებ მცირე ვარიანტებიდან — ამჯობინე match.
  • ყოველთვის დაამატე default, თუ ყველა შემთხვევას არ ფარავ.

სავარჯიშოები — თავი 4

📝 სავარჯიშოები

ივარჯიშე პირობებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

ცვლადით $n დაადგინე, ლუწია თუ კენტი, და გამოიტანე შესაბამისი ტექსტი.

✅ გადაწყვეტა
PHP
<?php
$n = 7;
if ($n % 2 === 0) {
    echo "ლუწი\n";
} else {
    echo "კენტი\n";
}
საშუალო სავარჯიშო 2

დაწერე შეფასების კონვერტერი elseif-ით: 90+ → A, 80+ → B, 70+ → C, სხვა → F.

✅ გადაწყვეტა
PHP
<?php
$score = 76;
if ($score >= 90)      { $grade = "A"; }
elseif ($score >= 80)  { $grade = "B"; }
elseif ($score >= 70)  { $grade = "C"; }
else                   { $grade = "F"; }
echo $grade . "\n";
პრაქტიკული სავარჯიშო 3

გამოიყენე match და დღის ნომრიდან (1–7) დააბრუნე დღის სახელი ქართულად.

💡 მინიშნება
match-ში გამოიყენე default არასწორი ნომრისთვის.
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function dayName(int $day): string
{
    return match ($day) {
        1 => "ორშაბათი",
        2 => "სამშაბათი",
        3 => "ოთხშაბათი",
        4 => "ხუთშაბათი",
        5 => "პარასკევი",
        6 => "შაბათი",
        7 => "კვირა",
        default => "არასწორი დღე",
    };
}

echo dayName(5) . "\n"; // პარასკევი

ქვიზი — თავი 4

❓ ქვიზი

შეამოწმე ცოდნა პირობებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რა ხდება switch-ში, თუ break დაგვავიწყდა?

  2. 2. რა შედარებას იყენებს match?

  3. 3. რა მთავარი უპირატესობა აქვს match-ს switch-თან შედარებით?

  4. 4. რა მოხდება if ($x = 5)-ში?

  5. 5. რას აგდებს match შესაბამისობის გარეშე (default-ის გარეშე)?

5 🔁 თავი 5. ციკლები

კოდის გამეორება: while, do...while, for, foreach და მართვა break/continue-ით.

while

while იმეორებს ბლოკს, სანამ პირობა true-ია. პირობა მოწმდება ყოველი გამეორების დასაწყისში.

სინტაქსი

Syntax
while (condition) {
    // კოდი
}

მაგალითები

დათვლა 1-დან 5-მდე
PHP
<?php
$i = 1;
while ($i <= 5) {
    echo $i . " ";
    $i++;
}
echo "\n"; // 1 2 3 4 5

✗ ხშირი შეცდომები

  • მთვლელის ($i++) განახლების დავიწყება — გამოიწვევს უსასრულო ციკლს.

do...while

do...while ჯერ ასრულებს ბლოკს, შემდეგ ამოწმებს პირობას. ამიტომ ბლოკი მინიმუმ ერთხელ ყოველთვის სრულდება.

სინტაქსი

Syntax
do {
    // კოდი
} while (condition);

მაგალითები

მინიმუმ ერთი გაშვება
PHP
<?php
$n = 10;
do {
    echo "ერთხელ მაინც სრულდება\n";
    $n++;
} while ($n < 5); // პირობა false-ია, მაგრამ ბლოკი ერთხელ შესრულდა
💡 რჩევა: გამოიყენე, როცა მოქმედება ერთხელ მაინც უნდა შესრულდეს (მაგ. მენიუს ჩვენება).

for

for იდეალურია, როცა გამეორებათა რაოდენობა ცნობილია. სამი ნაწილი: ინიციალიზაცია; პირობა; ბიჯი.

სინტაქსი

Syntax
for ($i = 0; $i < n; $i++) {
    // კოდი
}

მაგალითები

for ციკლი
PHP
<?php
for ($i = 1; $i <= 5; $i++) {
    echo "ნაბიჯი $i\n";
}

// უკუმიმართულებით:
for ($i = 5; $i >= 1; $i--) {
    echo $i . " ";
}
echo "\n";

✓ საუკეთესო პრაქტიკა

  • მასივებზე გადასავლელად ამჯობინე foreach — სუფთა და უსაფრთხოა.

foreach

foreach სპეციალურად მასივებსა და ობიექტებზე გადასასვლელად. შეიძლება მხოლოდ მნიშვნელობა, ან გასაღები+მნიშვნელობა.

სინტაქსი

Syntax
foreach ($array as $value) { }
foreach ($array as $key => $value) { }

მაგალითები

მნიშვნელობებსა და გასაღებებზე
PHP
<?php
$fruits = ["ვაშლი", "ბანანი", "ატამი"];
foreach ($fruits as $fruit) {
    echo "- $fruit\n";
}

$prices = ["პური" => 1.5, "რძე" => 2.0];
foreach ($prices as $name => $price) {
    echo "$name: $price ₾\n";
}

// მითითებით (&) ცვლის ორიგინალ მასივს:
foreach ($fruits as &$f) {
    $f = mb_strtoupper($f);
}
unset($f); // აუცილებელია მითითების შემდეგ!

✗ ხშირი შეცდომები

  • მითითებით (&$f) გავლის შემდეგ unset($f)-ის დავიწყება — იწვევს რთულად დასაჭერ შეცდომებს.

break

break მთლიანად წყვეტს ციკლს. break N წყვეტს N ჩალაგებულ ციკლს ერთდროულად.

მაგალითები

ციკლის გაჩერება
PHP
<?php
foreach (range(1, 100) as $n) {
    if ($n > 5) {
        break; // ჩერდება, როცა n > 5
    }
    echo $n . " ";
}
echo "\n"; // 1 2 3 4 5

continue

continue ტოვებს მიმდინარე გამეორებას და გადადის შემდეგზე. continue N მოქმედებს N დონეზე.

მაგალითები

მხოლოდ ლუწების ჩვენება
PHP
<?php
for ($i = 1; $i <= 10; $i++) {
    if ($i % 2 !== 0) {
        continue; // კენტებს გამოტოვებს
    }
    echo $i . " ";
}
echo "\n"; // 2 4 6 8 10

✗ ხშირი შეცდომები

  • continue switch-ის შიგნით PHP-ში ეხება switch-საც — ხშირად საჭიროა continue 2.

სავარჯიშოები — თავი 5

📝 სავარჯიშოები

ივარჯიშე ციკლებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

გამოიტანე რიცხვები 1-დან 10-მდე for ციკლით.

✅ გადაწყვეტა
PHP
<?php
for ($i = 1; $i <= 10; $i++) {
    echo $i . " ";
}
echo "\n";
საშუალო სავარჯიშო 2

მასივზე ["ა", "ბ", "გ"] გაიარე foreach-ით და გამოიტანე ინდექსი და მნიშვნელობა.

✅ გადაწყვეტა
PHP
<?php
$letters = ["ა", "ბ", "გ"];
foreach ($letters as $index => $letter) {
    echo "$index => $letter\n";
}
პრაქტიკული სავარჯიშო 3

დათვალე 1-დან 100-მდე რიცხვების ჯამი, მაგრამ გამოტოვე 13-ის ჯერადები (continue).

💡 მინიშნება
ჯერადობა შეამოწმე $i % 13 === 0-ით.
✅ გადაწყვეტა
PHP
<?php
$sum = 0;
for ($i = 1; $i <= 100; $i++) {
    if ($i % 13 === 0) {
        continue;
    }
    $sum += $i;
}
echo "ჯამი: $sum\n";

ქვიზი — თავი 5

❓ ქვიზი

შეამოწმე ცოდნა ციკლებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რა განასხვავებს do...while-ს while-ისგან?

  2. 2. რომელი ციკლი ჯობს მასივზე გადასასვლელად?

  3. 3. რას აკეთებს continue?

  4. 4. რა იწვევს უსასრულო ციკლს while-ში?

  5. 5. foreach-ში მითითებით (&$v) გავლის შემდეგ რა უნდა გავაკეთოთ?

6 🧩 თავი 6. ფუნქციები

კოდის ხელახლა გამოყენებადი ბლოკები: გამოცხადება, პარამეტრები, დაბრუნება, ნაგულისხმევი და ცვალებადი არგუმენტები, ანონიმური და arrow ფუნქციები.

გამოცხადება

ფუნქციის გამოცხადება

ფუნქცია არის დასახელებული, ხელახლა გამოყენებადი კოდის ბლოკი. ცხადდება function-ით.

სინტაქსი

Syntax
function name(): type {
    // ...
    return value;
}

მაგალითები

მარტივი ფუნქცია
PHP
<?php
declare(strict_types=1);

function greet(): string
{
    return "გამარჯობა!";
}

echo greet() . "\n";

✓ საუკეთესო პრაქტიკა

  • ფუნქცია ერთ კონკრეტულ საქმეს უნდა აკეთებდეს.
  • სახელი იყოს ზმნური და აღწერითი: calculateTotal().

პარამეტრები

პარამეტრები გადასცემენ მონაცემს ფუნქციას. რეკომენდებულია ტიპის მითითება. ნაგულისხმევად არგუმენტი გადაეცემა მნიშვნელობით; &-ით — მითითებით.

სინტაქსი

Syntax
function f(int $a, string $b) { }
function f(int &$ref) { } // მითითებით

მაგალითები

მნიშვნელობით vs მითითებით
PHP
<?php
declare(strict_types=1);

function add(int $a, int $b): int
{
    return $a + $b;
}
echo add(3, 4) . "\n"; // 7

function increment(int &$value): void
{
    $value++; // ცვლის ორიგინალს
}
$x = 10;
increment($x);
echo $x . "\n"; // 11

✗ ხშირი შეცდომები

  • ტიპის გამოტოვება — კარგავ მკაცრ შემოწმებას და IDE-ს დახმარებას.

return

დაბრუნებული მნიშვნელობა

return აბრუნებს მნიშვნელობას და ასრულებს ფუნქციას. დაბრუნების ტიპი იწერება :-ის შემდეგ. void ნიშნავს „არაფერს აბრუნებს".

მაგალითები

დაბრუნების ტიპები
PHP
<?php
declare(strict_types=1);

function divide(float $a, float $b): float
{
    if ($b === 0.0) {
        return 0.0; // ადრეული დაბრუნება
    }
    return $a / $b;
}

function logMessage(string $msg): void
{
    echo "[LOG] $msg\n"; // არაფერს აბრუნებს
}

echo divide(10, 4) . "\n";
logMessage("დაიწყო");
💡 რჩევა: „ადრეული დაბრუნება" (early return) ამარტივებს კოდს — ამცირებს ჩალაგებას.

ნაგულისხმევი + named args

ნაგულისხმევი პარამეტრები

პარამეტრს შეიძლება ჰქონდეს ნაგულისხმევი მნიშვნელობა — მაშინ ის არჩევითია. PHP 8-ში არსებობს დასახელებული არგუმენტები (named arguments).

სინტაქსი

Syntax
function f(int $a, int $b = 10) { }
f(a: 1, b: 2); // named arguments

მაგალითები

ნაგულისხმევი და named
PHP
<?php
declare(strict_types=1);

function createUser(string $name, string $role = "user", bool $active = true): array
{
    return ["name" => $name, "role" => $role, "active" => $active];
}

print_r(createUser("ანა"));
// named arguments — შეგვიძლია მხოლოდ ერთი ავირჩიოთ:
print_r(createUser("გია", active: false));

✓ საუკეთესო პრაქტიკა

  • named arguments ზრდის წაკითხვადობას, როცა ფუნქციას ბევრი არჩევითი პარამეტრი აქვს.

✗ ხშირი შეცდომები

  • ნაგულისხმევი პარამეტრის დაყენება სავალდებულოს წინ — დაუშვებელია (აუცილებლები წინ უნდა იყოს).

Variadic (...)

ცვალებადი არგუმენტები

...$args საშუალებას აძლევს ფუნქციას მიიღოს ნებისმიერი რაოდენობის არგუმენტი მასივად. იგივე ... (spread) მასივის „გაშლისთვისაც" გამოიყენება.

სინტაქსი

Syntax
function sum(int ...$numbers): int { }
sum(...$array); // spread

მაგალითები

variadic და spread
PHP
<?php
declare(strict_types=1);

function sum(int ...$numbers): int
{
    return array_sum($numbers);
}

echo sum(1, 2, 3, 4) . "\n"; // 10

$nums = [5, 10, 15];
echo sum(...$nums) . "\n";    // 30 (spread)

Closures

ანონიმური ფუნქციები

ანონიმური ფუნქცია (closure) უსახელოა და ენიჭება ცვლადს ან გადაეცემა არგუმენტად. გარე ცვლადების „დასაჭერად" გამოიყენება use.

სინტაქსი

Syntax
$f = function($x) use ($y) {
    return $x + $y;
};

მაგალითები

closure და use
PHP
<?php
declare(strict_types=1);

$multiplier = 3;

$triple = function (int $n) use ($multiplier): int {
    return $n * $multiplier;
};

echo $triple(5) . "\n"; // 15

// closure როგორც callback:
$numbers = [1, 2, 3, 4];
$even = array_filter($numbers, function ($n) {
    return $n % 2 === 0;
});
print_r($even);

Arrow functions (fn) PHP 7.4+

Arrow ფუნქციები

Arrow ფუნქცია — closure-ის მოკლე ფორმა. ავტომატურად იჭერს გარე ცვლადებს (use არ სჭირდება) და ყოველთვის ერთ გამოსახულებას აბრუნებს.

სინტაქსი

Syntax
$f = fn($x) => $x * 2;

მაგალითები

fn() => ...
PHP
<?php
declare(strict_types=1);

$factor = 10;
$scale = fn(int $n): int => $n * $factor; // factor ავტომატურად აიჭირა

echo $scale(5) . "\n"; // 50

$numbers = [1, 2, 3];
$doubled = array_map(fn($n) => $n * 2, $numbers);
print_r($doubled);

✓ საუკეთესო პრაქტიკა

  • მოკლე callback-ებისთვის arrow function უფრო წაკითხვადია.

✗ ხშირი შეცდომები

  • arrow function-ში მრავალი ბრძანების მოლოდინი — ის მხოლოდ ერთ გამოსახულებას უჭერს მხარს.

სავარჯიშოები — თავი 6

📝 სავარჯიშოები

ივარჯიშე ფუნქციებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დაწერე ფუნქცია square(int $n): int, რომელიც აბრუნებს რიცხვის კვადრატს.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function square(int $n): int
{
    return $n * $n;
}

echo square(6) . "\n"; // 36
საშუალო სავარჯიშო 2

დაწერე ფუნქცია, რომელიც იღებს ნებისმიერ რაოდენობის რიცხვს (variadic) და აბრუნებს მათ საშუალო არითმეტიკულს.

💡 მინიშნება
გამოიყენე array_sum() და count().
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function average(float ...$nums): float
{
    if (count($nums) === 0) {
        return 0.0;
    }
    return array_sum($nums) / count($nums);
}

echo average(10, 20, 30) . "\n"; // 20
პრაქტიკული სავარჯიშო 3

გამოიყენე array_map და arrow function, რათა ფასების მასივს დაამატო 18% დღგ.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$prices = [100, 250, 80];
$withVat = array_map(fn(float $p): float => round($p * 1.18, 2), $prices);

print_r($withVat); // [118, 295, 94.4]

ქვიზი — თავი 6

❓ ქვიზი

შეამოწმე ცოდნა ფუნქციებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 6
  1. 1. რა აკეთებს return?

  2. 2. რას ნიშნავს დაბრუნების ტიპი void?

  3. 3. რა საჭიროა closure-ში გარე ცვლადის დასაჭერად?

  4. 4. რით განსხვავდება arrow function closure-ისგან?

  5. 5. რას ნიშნავს ...$args პარამეტრში?

  6. 6. სად უნდა იყოს სავალდებულო პარამეტრი ნაგულისხმევთან მიმართებაში?

7 📚 თავი 7. მასივები

ინდექსირებული, ასოციაციური და მრავალგანზომილებიანი მასივები და მათთან მუშაობის ყველაზე მნიშვნელოვანი ფუნქციები.

ინდექსირებული

ინდექსირებული მასივები

ინდექსირებული მასივი ინახავს მნიშვნელობებს რიცხვითი გასაღებებით (0-დან). იქმნება [] ლიტერალით.

სინტაქსი

Syntax
$arr = [v1, v2, v3];
$arr[] = v4; // დამატება

მაგალითები

შექმნა, წვდომა, დამატება
PHP
<?php
$fruits = ["ვაშლი", "ბანანი", "ატამი"];

echo $fruits[0] . "\n"; // ვაშლი
$fruits[] = "მსხალი";   // ბოლოში დამატება
echo count($fruits) . "\n"; // 4

ასოციაციური

ასოციაციური მასივები

ასოციაციური მასივი იყენებს ტექსტურ (ან შერეულ) გასაღებებს — „გასაღები → მნიშვნელობა". იდეალურია სტრუქტურირებული მონაცემისთვის.

სინტაქსი

Syntax
$person = ["name" => "ანა", "age" => 21];

მაგალითები

გასაღები → მნიშვნელობა
PHP
<?php
$person = [
    "name" => "ანა",
    "age"  => 21,
    "city" => "თბილისი",
];

echo $person["name"] . "\n";
$person["email"] = "ana@example.com"; // ახალი წყვილი

foreach ($person as $key => $value) {
    echo "$key: $value\n";
}
💡 რჩევა: არარსებული გასაღების უსაფრთხოდ წასაკითხად: $person["x"] ?? "default".

მრავალგანზომილებიანი

მრავალგანზომილებიანი მასივები

მასივი, რომლის ელემენტებიც თავად მასივებია. გამოიყენება ცხრილების, ჩანაწერების სიის და სხვა რთული მონაცემებისთვის.

მაგალითები

მომხმარებელთა სია
PHP
<?php
$users = [
    ["name" => "ანა", "age" => 21],
    ["name" => "გია", "age" => 30],
];

echo $users[1]["name"] . "\n"; // გია

foreach ($users as $user) {
    echo "{$user["name"]} — {$user["age"]} წლის\n";
}

count()

აბრუნებს მასივის ელემენტების რაოდენობას.

სინტაქსი

Syntax
count($array): int

მაგალითები

count
PHP
<?php
$items = [10, 20, 30];
echo count($items) . "\n"; // 3
echo count([]) . "\n";     // 0

in_array()

ამოწმებს, არსებობს თუ არა მნიშვნელობა მასივში. მესამე არგუმენტი true რთავს მკაცრ შემოწმებას (ტიპით).

სინტაქსი

Syntax
in_array($needle, $array, strict = false): bool

მაგალითები

in_array მკაცრი რეჟიმით
PHP
<?php
$ids = [1, 2, 3];

var_dump(in_array(2, $ids));         // true
var_dump(in_array("2", $ids));       // true (ფხვიერი)
var_dump(in_array("2", $ids, true)); // false (მკაცრი)

✓ საუკეთესო პრაქტიკა

  • თითქმის ყოველთვის გამოიყენე მესამე არგუმენტი true — მკაცრი შემოწმება უსაფრთხოა.

array_key_exists()

ამოწმებს გასაღების არსებობას. განსხვავება isset()-თან: array_key_exists true-ს აბრუნებს მაშინაც, თუ მნიშვნელობა null-ია, isset კი — false-ს.

მაგალითები

array_key_exists vs isset
PHP
<?php
$data = ["name" => null];

var_dump(array_key_exists("name", $data)); // true
var_dump(isset($data["name"]));            // false (null-ის გამო)

array_merge()

აერთებს ორ ან მეტ მასივს. ასოციაციურ მასივებში ერთნაირი ტექსტური გასაღები გადაიწერება; რიცხვითი გასაღებები ხელახლა ინომრება. + ოპერატორი კი ინარჩუნებს მარცხენა მასივის გასაღებებს.

მაგალითები

array_merge და + სხვაობა
PHP
<?php
$a = ["x" => 1, "y" => 2];
$b = ["y" => 99, "z" => 3];

print_r(array_merge($a, $b)); // y გადაიწერა: 99
print_r($a + $b);             // y დარჩა: 2

array_map()

იყენებს callback-ს მასივის ყოველ ელემენტზე და აბრუნებს ახალ მასივს იმავე გასაღებებით.

სინტაქსი

Syntax
array_map(callback, $array): array

მაგალითები

ელემენტების გარდაქმნა
PHP
<?php
$numbers = [1, 2, 3, 4];
$squares = array_map(fn($n) => $n ** 2, $numbers);
print_r($squares); // [1, 4, 9, 16]

array_filter()

ტოვებს მხოლოდ იმ ელემენტებს, რომლებზეც callback აბრუნებს true-ს. გასაღებები ნარჩუნდება (ხშირად საჭიროა array_values() გადასანომრად).

მაგალითები

ფილტრაცია
PHP
<?php
$numbers = [1, 2, 3, 4, 5, 6];
$even = array_filter($numbers, fn($n) => $n % 2 === 0);

print_r(array_values($even)); // [2, 4, 6]

// callback-ის გარეშე — შლის ყველა falsy მნიშვნელობას:
print_r(array_values(array_filter([0, 1, "", "a", null])));
💡 რჩევა: callback-ის გარეშე array_filter() შლის ყველა falsy მნიშვნელობას.

array_reduce()

კრებს მასივს ერთ მნიშვნელობამდე, ელემენტების მიმდევრობით „დაგროვებით" (accumulator).

სინტაქსი

Syntax
array_reduce($array, callback, $initial)

მაგალითები

ჯამი reduce-ით
PHP
<?php
$numbers = [1, 2, 3, 4];
$sum = array_reduce($numbers, fn($carry, $n) => $carry + $n, 0);
echo $sum . "\n"; // 10

// უდიდესი ფასის პოვნა:
$prices = [12, 45, 7, 99, 30];
$max = array_reduce($prices, fn($c, $p) => max($c, $p), 0);
echo $max . "\n"; // 99

sort()

sort() / rsort()

sort() ალაგებს მნიშვნელობებს ზრდადობით და ხელახლა ანომრებს გასაღებებს. rsort() — კლებადობით. ცვლის მასივს ადგილზე (in-place).

მაგალითები

sort და rsort
PHP
<?php
$nums = [3, 1, 4, 1, 5];
sort($nums);
print_r($nums); // [1, 1, 3, 4, 5]

rsort($nums);
print_r($nums); // [5, 4, 3, 1, 1]

✗ ხშირი შეცდომები

  • sort() არ აბრუნებს დალაგებულ მასივს — ის ცვლის ცვლადს ადგილზე და აბრუნებს bool-ს.

asort()

asort() / arsort()

ალაგებს მნიშვნელობებით, მაგრამ ინარჩუნებს გასაღებებს. გამოსადეგია ასოციაციური მასივებისთვის. arsort() — კლებადობით.

მაგალითები

asort გასაღებების შენახვით
PHP
<?php
$scores = ["ანა" => 85, "გია" => 92, "ნინო" => 78];
asort($scores);
print_r($scores); // ნინო=>78, ანა=>85, გია=>92 (გასაღებები შენარჩუნდა)

ksort()

ksort() / krsort()

ალაგებს გასაღებებით (ზრდადობით). krsort() — კლებადობით. ინარჩუნებს გასაღები–მნიშვნელობა წყვილებს.

მაგალითები

გასაღებებით დალაგება
PHP
<?php
$data = ["c" => 3, "a" => 1, "b" => 2];
ksort($data);
print_r($data); // a=>1, b=>2, c=>3
💡 რჩევა: მომხმარებლის წესით დასალაგებლად გამოიყენე usort() და spaceship <=>.

სავარჯიშოები — თავი 7

📝 სავარჯიშოები

ივარჯიშე მასივებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

შექმენი 5 რიცხვის მასივი და გამოიტანე მათი ჯამი array_sum()-ით.

✅ გადაწყვეტა
PHP
<?php
$nums = [10, 20, 30, 40, 50];
echo array_sum($nums) . "\n"; // 150
საშუალო სავარჯიშო 2

მასივიდან [5, 12, 8, 130, 44] დატოვე მხოლოდ 10-ზე დიდი რიცხვები (array_filter) და გადააქციე ორმაგად (array_map).

💡 მინიშნება
ჯერ filter, შემდეგ map; საჭიროების შემთხვევაში array_values().
✅ გადაწყვეტა
PHP
<?php
$nums = [5, 12, 8, 130, 44];
$big = array_filter($nums, fn($n) => $n > 10);
$doubled = array_map(fn($n) => $n * 2, $big);
print_r(array_values($doubled)); // [24, 260, 88]
პრაქტიკული სავარჯიშო 3

გქონდეს პროდუქტების ასოციაციური მასივი (სახელი => ფასი). დაალაგე ფასის ზრდადობით (გასაღებების შენახვით) და გამოთვალე საერთო ღირებულება.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$products = ["პური" => 1.5, "ყველი" => 12.0, "რძე" => 2.3];

asort($products); // ფასით, გასაღებები შენარჩუნდა
print_r($products);

$total = array_reduce($products, fn($c, $p) => $c + $p, 0.0);
echo "ჯამი: $total ₾\n";

ქვიზი — თავი 7

❓ ქვიზი

შეამოწმე ცოდნა მასივებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 6
  1. 1. რა დააბრუნა array_filter-მა?

  2. 2. რა განასხვავებს array_key_exists-ს isset-ისგan null მნიშვნელობაზე?

  3. 3. რომელი ფუნქცია ალაგებს მნიშვნელობებით და ინარჩუნებს გასაღებებს?

  4. 4. რას აბრუნებს sort()?

  5. 5. რას აკეთებს array_reduce?

  6. 6. რომელი უსაფრთხოა in_array-ში ტიპის გათვალისწინებით?

8 🔤 თავი 8. სტრიქონების დამუშავება

სტრიქონებთან მუშაობის ხშირად გამოყენებადი ფუნქციები. ქართულისთვის (UTF-8) ხშირად საჭიროა mb_* ვარიანტები.

⚠ ქართული და mb_*

ქართული და UTF-8 (mb_*)

ქართული სიმბოლო UTF-8-ში რამდენიმე ბაიტს იკავებს. ჩვეულებრივი strlen(), substr(), strtoupper() ბაიტებზე მუშაობს და ქართულს „ამტვრევს". ამიტომ ქართულ ტექსტზე გამოიყენე mb_ პრეფიქსიანი ვარიანტები: mb_strlen(), mb_substr(), mb_strtoupper().

მაგალითები

strlen vs mb_strlen
PHP
<?php
$word = "გამარჯობა";

echo strlen($word) . "\n";    // ბაიტების რაოდენობა (დიდი რიცხვი)
echo mb_strlen($word) . "\n"; // 9 — ნამდვილი სიმბოლოები
💡 რჩევა: ქართულ ტექსტზე ეჭვის შემთხვევაში ყოველთვის ამჯობინე mb_* ფუნქცია.

strlen()

strlen() / mb_strlen()

აბრუნებს სტრიქონის სიგრძეს. strlen() — ბაიტებში, mb_strlen() — სიმბოლოებში.

მაგალითები

სიგრძე
PHP
<?php
echo strlen("hello") . "\n";    // 5
echo mb_strlen("გამარჯობა") . "\n"; // 9

substr()

substr() / mb_substr()

ამოიღებs ქვესტრიქონს მითითებული პოზიციიდან. უარყოფითი დასაწყისი ითვლება ბოლოდან.

სინტაქსი

Syntax
substr($string, $start, $length)

მაგალითები

ქვესტრიქონი
PHP
<?php
$s = "PHP პროგრამირება";
echo substr("hello world", 0, 5) . "\n"; // hello
echo substr("hello", -2) . "\n";          // lo
echo mb_substr($s, 0, 3) . "\n";          // PHP (ქართულზე mb_substr)

strpos() / str_contains()

strpos() აბრუნებს ქვესტრიქონის პირველ პოზიციას ან false-ს. PHP 8-ში გაცილებით მოსახერხებელია str_contains(), str_starts_with(), str_ends_with().

მაგალითები

ძიება სტრიქონში
PHP
<?php
$email = "user@example.com";

var_dump(strpos($email, "@"));        // 4
var_dump(str_contains($email, "@"));  // true (PHP 8)
var_dump(str_ends_with($email, ".com")); // true

✗ ხშირი შეცდომები

  • if (strpos($s, "x")) — თუ პოზიცია 0-ია, ეს false-ად ჩაითვლება! გამოიყენე !== false ან str_contains.

str_replace()

ცვლის ქვესტრიქონის ყველა გამეორებას. მუშაობს მასივებზეც.

სინტაქსი

Syntax
str_replace($search, $replace, $subject)

მაგალითები

ჩანაცვლება
PHP
<?php
echo str_replace("ცუდი", "კარგი", "დღეს ცუდი ამინდია") . "\n";

// მასივებით — რამდენიმე ჩანაცვლება ერთდროულად:
$result = str_replace(["a", "e"], ["4", "3"], "leader");
echo $result . "\n"; // l34d3r

trim()

trim() / ltrim() / rtrim()

აშორებს ზედმეტ სიმბოლოებს (ნაგულისხმევად — გამოტოვებებს) კიდეებიდან. ltrim() — მარცხნიდან, rtrim() — მარჯვნიდან.

მაგალითები

გასუფთავება
PHP
<?php
$input = "   ანა  ";
echo "[" . trim($input) . "]\n"; // [ანა]

echo trim("...სათაური...", ".") . "\n"; // სათაური

✓ საუკეთესო პრაქტიკა

  • მომხმარებლის შეყვანა თითქმის ყოველთვის trim()-ით უნდა გაიწმინდოს.

strtoupper()

strtoupper() / mb_strtoupper()

გადააქცევs სტრიქონს მთლიანად დიდ ასოებად. ქართულს ეს არ ეხება (მაიუსკული/მინუსკული არ აქვს), მაგრამ ლათინურისთვის mb_ ვარიანტი UTF-8-ისთვის უსაფრთხოა.

მაგალითები

დიდი ასოები
PHP
<?php
echo strtoupper("hello") . "\n";       // HELLO
echo mb_strtoupper("café") . "\n";     // CAFÉ (UTF-8-ისთვის უსაფრთხო)

strtolower()

strtolower() / mb_strtolower()

გადააქცევს სტრიქონს პატარა ასოებად. გამოსადეგია, მაგ., ელფოსტის ნორმალიზებისთვის.

მაგალითები

პატარა ასოები
PHP
<?php
$email = "User@Example.COM";
echo strtolower($email) . "\n"; // user@example.com

explode()

ჭრის სტრიქონს გამყოფი სიმბოლოს მიხედვით და აბრუნებs მასივს.

სინტაქსი

Syntax
explode($delimiter, $string): array

მაგალითები

სტრიქონი → მასივი
PHP
<?php
$csv = "ანა,გია,ნინო";
$names = explode(",", $csv);
print_r($names); // ["ანა", "გია", "ნინო"]

$date = "2026-06-11";
[$year, $month, $day] = explode("-", $date);
echo "$day/$month/$year\n";

implode()

explode()-ის საპირისპირო: აერთებს მასივის ელემენტებს ერთ სტრიქონად გამყოფით.

სინტაქსი

Syntax
implode($glue, $array): string

მაგალითები

მასივი → სტრიქონი
PHP
<?php
$tags = ["php", "web", "backend"];
echo implode(", ", $tags) . "\n"; // php, web, backend
echo implode("/", [2026, 6, 11]) . "\n"; // 2026/6/11

sprintf()

sprintf() / printf()

აყალიბებს სტრიქონს შაბლონით. sprintf() აბრუნებს შედეგს, printf() ბეჭდავს. ძირითადი specifier-ები: %s (სტრიქონი), %d (მთელი), %f (float), %.2f (2 ათწილადი).

მაგალითები

ფორმატირება
PHP
<?php
$name = "ანა";
$price = 19.5;

echo sprintf("მომხმარებელი: %s\n", $name);
echo sprintf("ფასი: %.2f ₾\n", $price);   // 19.50
printf("%05d\n", 42);                       // 00042 (ნულებით შევსება)
💡 რჩევა: number_format(1234567.891, 2) — ფულის ლამაზად გამოსატანად (1,234,567.89).

სავარჯიშოები — თავი 8

📝 სავარჯიშოები

ივარჯიშე სტრიქონებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დაითვალე ქართული სიტყვის „პროგრამირება" სიმბოლოები (გახსოვდეს mb_).

✅ გადაწყვეტა
PHP
<?php
echo mb_strlen("პროგრამირება") . "\n"; // 12
საშუალო სავარჯიშო 2

მიიღე სრული სახელი „გია ბერია", გაჭერი გამოტოვებაზე და გამოიტანე ინიციალები (მაგ. „გ.ბ.").

💡 მინიშნება
გამოიყენე explode(" ", ...) და mb_substr().
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$fullName = "გია ბერია";
$parts = explode(" ", $fullName);
$initials = mb_substr($parts[0], 0, 1) . "." . mb_substr($parts[1], 0, 1) . ".";
echo $initials . "\n"; // გ.ბ.
პრაქტიკული სავარჯიშო 3

ნორმალიზე მომხმარებლის ელფოსტა: მოაშორე ზედმეტი გამოტოვებები და გადააქციე პატარა ასოებად. შემდეგ შეამოწმე, შეიცავs თუ არა „@"-ს.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$raw = "   User@Example.COM  ";
$email = strtolower(trim($raw));

if (str_contains($email, "@")) {
    echo "ნორმალიზებული: $email\n";
} else {
    echo "არასწორი ელფოსტა\n";
}

ქვიზი — თავი 8

❓ ქვიზი

შეამოწმე ცოდნა სტრიქონებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 6
  1. 1. რომელი ფუნქცია უნდა გამოვიყენოთ ქართული ტექსტის სიგრძისთვის?

  2. 2. რას აბრუნებს strpos("abc", "x")?

  3. 3. რა აკეთებს explode(",", "a,b,c")?

  4. 4. რომელია PHP 8-ის სუფთა გზა „შეიცავს თუ არა"-ს შესამოწმებლად?

  5. 5. რას აკეთებს trim(" hi ")?

  6. 6. რას გამოიტანs sprintf("%.2f", 3.1)?

9 🧬 თავი 9. სერიალიზაცია

მონაცემის გადაქცევა შესანახ/გადასაცემ ფორმატად და უკან: serialize/unserialize და json_encode/json_decode.

serialize()

serialize() გადააქცევs PHP-ის ნებისმიერ მნიშვნელობას (მასივი, ობიექტი) ტექსტურ წარმოდგენად, რომელიც შეიძლება შეინახო ფაილში ან ბაზაში.

სინტაქსი

Syntax
serialize($value): string

მაგალითები

მასივის სერიალიზება
PHP
<?php
$data = ["name" => "ანა", "roles" => ["admin", "editor"]];
$string = serialize($data);
echo $string . "\n";
// a:2:{s:4:"name";s:6:"ანა";...}
💡 რჩევა: serialize ინახავs PHP-ის ტიპებსაც (მაგ. ობიექტის კლასს), მაგრამ მხოლოდ PHP-ს ესმის ის.

unserialize()

აღადგენs სერიალიზებულ სტრიქონს უკან PHP მნიშვნელობად. უსაფრთხოება: არასოდეს გაუშვა unserialize() არასანდო (მომხმარებლის) მონაცემზე — ეს საშიში თავდასხმის ვექტორია. გამოიყენე allowed_classes ოფცია.

სინტაქსი

Syntax
unserialize($string, ["allowed_classes" => false])

მაგალითები

აღდგენა და უსაფრთხოება
PHP
<?php
$string = serialize(["a" => 1, "b" => 2]);

$restored = unserialize($string, ["allowed_classes" => false]);
print_r($restored); // ["a" => 1, "b" => 2]

✗ ხშირი შეცდომები

  • unserialize()-ის გაშვება მომხმარებლის input-ზე — შესაძლო object injection. ამჯობინე JSON.

json_encode()

გადააქცევs PHP მონაცემს JSON ფორმატად — ენებს შორის უნივერსალური სტანდარტი. ქართულისთვის აუცილებელია JSON_UNESCAPED_UNICODE, თორემ სიმბოლოები \uXXXX-ად დაიწერება.

სინტაქსი

Syntax
json_encode($value, flags): string

მნიშვნელობები

მნიშვნელობააღწერა
JSON_UNESCAPED_UNICODE არ ააქცევს UTF-8-ს \uXXXX-ად (ქართულისთვის).
JSON_PRETTY_PRINT ლამაზად, წაკითხვადად ფორმატავს.
JSON_THROW_ON_ERROR შეცდომისას აგდებს JsonException-ს.

მაგალითები

JSON-ად კოდირება ფლაგებით
PHP
<?php
$data = ["name" => "ანა", "age" => 21, "active" => true];

echo json_encode($data) . "\n";
// {"name":"ანა",...} — ცუდია ქართულისთვის

echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
// {
//     "name": "ანა",
//     ...
// }

✓ საუკეთესო პრაქტიკა

  • ქართულისთვის ყოველთვის გამოიყენე JSON_UNESCAPED_UNICODE.
  • შეცდომების დასაჭერად დაამატე JSON_THROW_ON_ERROR.

json_decode()

აღადგენs JSON სტრიქონს PHP მნიშვნელობად. მეორე არგუმენტი true აბრუნებს ასოციაციურ მასივს; სხვა შემთხვევაში — stdClass ობიექტს.

სინტაქსი

Syntax
json_decode($json, associative = false)

მაგალითები

JSON → მასივი ან ობიექტი
PHP
<?php
$json = '{"name":"ანა","age":21}';

$obj = json_decode($json);
echo $obj->name . "\n"; // ობიექტი: ->name

$arr = json_decode($json, true);
echo $arr["name"] . "\n"; // მასივი: ["name"]

// უსაფრთხო ვარიანტი შეცდომების დაჭერით:
try {
    $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo "არასწორი JSON\n";
}

✗ ხშირი შეცდომები

  • არასწორ JSON-ზე json_decode აბრუნებს null-ს — დაამატე JSON_THROW_ON_ERROR შეცდომის დასაჭერად.

serialize vs JSON

ორივე ინახავs მონაცემს ტექსტურად, მაგრამ დანიშნულება განსხვავდება.

მნიშვნელობები

მნიშვნელობააღწერა
პორტაბელურობა JSON — უნივერსალური (JS, Python, ...). serialize — მხოლოდ PHP.
ტიპები serialize ინახავს PHP ობიექტებსაც. JSON-ს მხოლოდ მასივი/ობიექტი/სკალარი ესმის.
უსაფრთხოება unserialize არასანდო input-ზე საშიშია. JSON უსაფრთხოა.
წაკითხვადობა JSON ადამიანისთვის წასაკითხია; serialize — ნაკლებად.

✓ საუკეთესო პრაქტიკა

  • API-სა და გარე გაცვლისთვის — ყოველთვის JSON.
  • serialize გამოიყენე მხოლოდ შიდა, სანდო PHP↔PHP შემთხვევებში.

სავარჯიშოები — თავი 9

📝 სავარჯიშოები

ივარჯიშე სერიალიზაციაზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

გადააქციე მასივი ["a" => 1, "b" => 2] JSON-ად ისე, რომ ლამაზად დაიბეჭდოს.

✅ გადაწყვეტა
PHP
<?php
$data = ["a" => 1, "b" => 2];
echo json_encode($data, JSON_PRETTY_PRINT) . "\n";
საშუალო სავარჯიშო 2

JSON სტრიქონი {"city":"თბილისი","temp":28} გადააქციე ასოციაციურ მასივად და გამოიტანე ტემპერატურა. გაითვალისწინე ქართული.

💡 მინიშნება
გამოიყენე json_decode($json, true).
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$json = '{"city":"თბილისი","temp":28}';
$data = json_decode($json, true);
echo "{$data["city"]}: {$data["temp"]}°\n";
პრაქტიკული სავარჯიშო 3

დაწერე ფუნქცია, რომელიც მასივს ინახავs JSON ფაილში (ქართულის შენარჩუნებით) და შემდეგ კითხულობs უკან.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function saveJson(string $file, array $data): void
{
    $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
    file_put_contents($file, $json);
}

function loadJson(string $file): array
{
    $json = file_get_contents($file);
    return json_decode($json, true, 512, JSON_THROW_ON_ERROR);
}

saveJson("data.json", ["name" => "ანა", "roles" => ["admin"]]);
print_r(loadJson("data.json"));

ქვიზი — თავი 9

❓ ქვიზი

შეამოწმე ცოდნა სერიალიზაციაზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რომელი ფლაგი სჭირდება ქართულს json_encode-ში?

  2. 2. რას აბრუნებს json_decode($json, true)?

  3. 3. რატომ ჯობს JSON serialize-ს API-ებისთვის?

  4. 4. რა საფრთხეა unserialize()-ში?

  5. 5. რა მოხდება არასწორ JSON-ზე json_decode-ში (ფლაგების გარეშე)?

10 🗂️ თავი 10. XML და JSON

სტრუქტურირებული მონაცემის ფორმატები: XML-ის წაკითხვა და შექმნა SimpleXML-ით, JSON-ის კოდირება/დეკოდირება და მასივი vs ობიექტი.

SimpleXML

SimpleXML PHP-ის ჩაშენებული გაფართოებაა XML-თან მარტივად სამუშაოდ. ტვირთვა: simplexml_load_string() (სტრიქონიდან) ან simplexml_load_file() (ფაილიდან).

სინტაქსი

Syntax
simplexml_load_string($xml)
simplexml_load_file($path)

მაგალითები

XML-ის ჩატვირთვა
PHP
<?php
$xmlString = '<?xml version="1.0" encoding="UTF-8"?>
<book>
    <title>PHP საფუძვლები</title>
    <pages>320</pages>
</book>';

$book = simplexml_load_string($xmlString);
echo $book->title . "\n"; // PHP საფუძვლები
echo $book->pages . "\n"; // 320

✗ ხშირი შეცდომები

  • არასწორ (not well-formed) XML-ზე ფუნქცია აბრუნებs false-ს — შეამოწმე შედეგი.

XML-ის წაკითხვა

SimpleXML-ის ობიექტზე ელემენტებს წვდები თვისებებივით (->), ატრიბუტებს კი მასივივით ([]). გამეორებად ელემენტებზე გადადიხარ foreach-ით.

მაგალითები

ელემენტებსა და ატრიბუტებზე გადასვლა
PHP
<?php
$xml = simplexml_load_string('
<users>
    <user id="1"><name>ანა</name></user>
    <user id="2"><name>გია</name></user>
</users>');

foreach ($xml->user as $user) {
    $id = (string) $user["id"];     // ატრიბუტი
    $name = (string) $user->name;   // ელემენტი
    echo "#$id: $name\n";
}
💡 რჩევა: SimpleXML მნიშვნელობები ობიექტებია — გამოიყენე (string) ან (int) casting.

XML-ის შექმნა

ახალ XML-ს ქმნი SimpleXMLElement-ით, ელემენტებს ამატებ addChild()-ით, ატრიბუტებს — addAttribute()-ით, საბოლოოდ asXML() აბრუნებს ტექსტს.

მაგალითები

XML-ის აგება
PHP
<?php
$root = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><catalog/>');

$product = $root->addChild("product");
$product->addAttribute("id", "101");
$product->addChild("name", "კლავიატურა");
$product->addChild("price", "49");

echo $root->asXML();

JSON კოდირება

JSON დღეს ვებ-API-ების ძირითადი ფორმატია (XML-ზე მსუბუქი). PHP-ში json_encode() გადააქცევს მასივებსა და ობიექტებს JSON-ად.

მაგალითები

API პასუხის აგება
PHP
<?php
$response = [
    "status" => "ok",
    "data" => [
        ["id" => 1, "name" => "ანა"],
        ["id" => 2, "name" => "გია"],
    ],
];

header("Content-Type: application/json; charset=utf-8");
echo json_encode($response, JSON_UNESCAPED_UNICODE);

JSON დეკოდირება

შემომავალ JSON-ს (მაგ. API მოთხოვნის სხეული) კითხულობ json_decode()-ით. გადაწყვიტე: მასივად გინდა (true) თუ ობიექტად.

მაგალითები

შემომავალი JSON-ის წაკითხვა
PHP
<?php
$incoming = '{"product":"მაუსი","qty":3}';

$data = json_decode($incoming, true);
echo "{$data["product"]} x {$data["qty"]}\n";

მასივი vs ობიექტი

ასოციაციური მასივი vs ობიექტი

json_decode-ის მეორე არგუმენტი წყვეტს შედეგის ფორმას: true → ასოციაციური მასივი ([] წვდომა), false (ნაგულისხმევი) → stdClass ობიექტი (-> წვდომა).

მაგალითები

ორი ფორმა
PHP
<?php
$json = '{"user":{"name":"ნინო","age":25}}';

$obj = json_decode($json);
echo $obj->user->name . "\n";  // ობიექტი

$arr = json_decode($json, true);
echo $arr["user"]["name"] . "\n"; // მასივი

✓ საუკეთესო პრაქტიკა

  • მასივი (true) უფრო მოსახერხებელია ციკლებსა და მასივის ფუნქციებთან.

პრაქტიკა: XML → JSON

პრაქტიკული მაგალითი

რეალური ამოცანა: წავიკითხოთ XML კონფიგურაცია და დავაბრუნოთ JSON პასუხად — ეს ხშირია სისტემების ინტეგრაციისას.

მაგალითები

XML-ის წაკითხვა და JSON-ად გადაცემა
PHP
<?php
declare(strict_types=1);

$xml = simplexml_load_string('
<settings>
    <theme>dark</theme>
    <lang>ka</lang>
</settings>');

$config = [
    "theme" => (string) $xml->theme,
    "lang"  => (string) $xml->lang,
];

echo json_encode($config, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);

სავარჯიშოები — თავი 10

📝 სავარჯიშოები

ივარჯიშე XML-სა და JSON-ზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

XML <city><name>ბათუმი</name></city>-დან წაიკითხე ქალაქის სახელი.

✅ გადაწყვეტა
PHP
<?php
$xml = simplexml_load_string("<city><name>ბათუმი</name></city>");
echo (string) $xml->name . "\n"; // ბათუმი
საშუალო სავარჯიშო 2

აიღე მომხმარებელთა მასივი და გადააქციე JSON-ად ქართულის შენარჩუნებით, შემდეგ წაიკითხე უკან მასივად.

💡 მინიშნება
encode → decode(true).
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$users = [["name" => "ანა"], ["name" => "გია"]];
$json = json_encode($users, JSON_UNESCAPED_UNICODE);
$back = json_decode($json, true);

echo $back[1]["name"] . "\n"; // გია
პრაქტიკული სავარჯიშო 3

შექმენი XML პროდუქტების კატალოგი (2 პროდუქტი id ატრიბუტებით) SimpleXMLElement-ით და დაბეჭდე.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$catalog = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><catalog/>');

$items = [["id" => 1, "name" => "ჩანთა"], ["id" => 2, "name" => "კალამი"]];
foreach ($items as $item) {
    $p = $catalog->addChild("product", $item["name"]);
    $p->addAttribute("id", (string) $item["id"]);
}

echo $catalog->asXML();

ქვიზი — თავი 10

❓ ქვიზი

შეამოწმე ცოდნა XML-სა და JSON-ზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რომელი ფუნქცია ტვირთავs XML-ს სტრიქონიდan?

  2. 2. როგორ წვდები XML ატრიბუტს SimpleXML-ში?

  3. 3. რა აბრუნებს json_decode-ს ნაგულისხმევად (true-ს გარეშე)?

  4. 4. რომელი მეთოდი ამატებs ბავშვ ელემენტს SimpleXML-ში?

  5. 5. რატომ უპირატესობს JSON XML-ს ვებ-API-ებში?

11 ⏰ თავი 11. თარიღი და დრო

თარიღებთან მუშაობა ობიექტურად: DateTime, DateInterval, DatePeriod, ფორმატირება, პარსინგი და დროის სარტყლები.

DateTime

DateTime / DateTimeImmutable

DateTime არის ობიექტი თარიღ-დროის წარმოსადგენად. რეკომენდებულია DateTimeImmutable — ის არ იცვლება ადგილზე, რაც შეცდომებს ამცირებს.

სინტაქსი

Syntax
new DateTimeImmutable("now")
new DateTimeImmutable("2026-06-11")

მაგალითები

ობიექტის შექმნა
PHP
<?php
$now = new DateTimeImmutable("now");
echo $now->format("Y-m-d H:i:s") . "\n";

$birthday = new DateTimeImmutable("2000-05-20");
echo $birthday->format("d.m.Y") . "\n"; // 20.05.2000

✓ საუკეთესო პრაქტიკა

  • ამჯობინე DateTimeImmutable — დამატება/გამოკლება ახალ ობიექტს აბრუნებს და ორიგინალს არ ცვლის.

DateInterval

DateInterval წარმოადგენს დროის შუალედს (მაგ. „2 დღე", „1 წელი"). ფორმატი ISO 8601: P (period), T (time). მაგ. P1Y2M10D = 1 წელი 2 თვე 10 დღე.

მაგალითები

დამატება და გამოკლება
PHP
<?php
$date = new DateTimeImmutable("2026-06-11");

$later = $date->add(new DateInterval("P10D")); // +10 დღე
echo $later->format("Y-m-d") . "\n"; // 2026-06-21

$earlier = $date->sub(new DateInterval("P1M")); // -1 თვე
echo $earlier->format("Y-m-d") . "\n"; // 2026-05-11
💡 რჩევა: „2 საათის" შუალედი: PT2H (T აუცილებელია დროის ნაწილისთვის).

DatePeriod

DatePeriod ქმნის თარიღების მიმდევრობას მოცემული ბიჯით — მოსახერხებელია „ყოველი დღე/კვირა დიაპაზონში" გასავლელად.

მაგალითები

ყოველი დღე დიაპაზონში
PHP
<?php
$start = new DateTimeImmutable("2026-06-01");
$end   = new DateTimeImmutable("2026-06-05");
$period = new DatePeriod($start, new DateInterval("P1D"), $end);

foreach ($period as $day) {
    echo $day->format("Y-m-d") . "\n";
}
// 06-01 ... 06-04 (end არ შედის)

format()

თარიღის ფორმატირება

format() აქცევს თარიღს სასურველ ტექსტად. ძირითადი სიმბოლოები:

მნიშვნელობები

მნიშვნელობააღწერა
Y / y წელი 4/2 ციფრით (2026 / 26)
m / n თვე ნულით / ნულის გარეშე (06 / 6)
d / j დღე ნულით / ნულის გარეშე (09 / 9)
H / i / s საათი (24სთ) / წუთი / წამი
l / D კვირის დღე სრულად / შემოკლებით

მაგალითები

ფორმატები
PHP
<?php
$d = new DateTimeImmutable("2026-06-11 14:30:00");

echo $d->format("Y-m-d") . "\n";        // 2026-06-11
echo $d->format("d/m/Y H:i") . "\n";    // 11/06/2026 14:30
echo $d->format("l") . "\n";            // Thursday

createFromFormat()

თარიღის პარსინგი

როცა თარიღი არასტანდარტული ფორმატითაა (მაგ. 11/06/2026), გამოიყენე DateTimeImmutable::createFromFormat() — შენ ეუბნები PHP-ს, რა ფორმატია.

მაგალითები

სტრიქონიდან თარიღი
PHP
<?php
$d = DateTimeImmutable::createFromFormat("d/m/Y", "11/06/2026");

if ($d === false) {
    echo "არასწორი თარიღი\n";
} else {
    echo $d->format("Y-m-d") . "\n"; // 2026-06-11
}

✗ ხშირი შეცდომები

  • არასწორ ფორმატზე createFromFormat აბრუნებს false-ს — შეამოწმე შედეგი.

DateTimeZone

დროის სარტყლები

დროის სარტყელი მნიშვნელოვანია გლობალური აპლიკაციებისთვის. დააყენე DateTimeZone-ით ან გლობალურად date_default_timezone_set()-ით.

მაგალითები

თბილისის დროით
PHP
<?php
$tz = new DateTimeZone("Asia/Tbilisi");
$now = new DateTimeImmutable("now", $tz);
echo $now->format("Y-m-d H:i") . "\n";

// გლობალურად:
date_default_timezone_set("Asia/Tbilisi");
echo date("H:i") . "\n";

✓ საუკეთესო პრაქტიკა

  • ბაზაში დრო შეინახე UTC-ში, ხოლო მომხმარებელს აჩვენე მის სარტყელში.

სავარჯიშოები — თავი 11

📝 სავარჯიშოები

ივარჯიშე თარიღებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

გამოიტანე დღევანდელი თარიღი ფორმატით დ.თ.წ (d.m.Y).

✅ გადაწყვეტა
PHP
<?php
$today = new DateTimeImmutable("now");
echo $today->format("d.m.Y") . "\n";
საშუალო სავარჯიშო 2

დათვალე, რამდენი დღეა ორ თარიღს შორის: 2026-01-01 და 2026-06-11.

💡 მინიშნება
გამოიყენე $a->diff($b)->days.
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$a = new DateTimeImmutable("2026-01-01");
$b = new DateTimeImmutable("2026-06-11");
$diff = $a->diff($b);
echo $diff->days . " დღე\n";
პრაქტიკული სავარჯიშო 3

დაწერე ფუნქცია, რომელიც აბრუნებs მომხმარებლის ასაკს დაბადების თარიღიდან (წლებში).

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function ageInYears(string $birthDate): int
{
    $birth = new DateTimeImmutable($birthDate);
    $now = new DateTimeImmutable("now");
    return $now->diff($birth)->y;
}

echo ageInYears("2000-05-20") . "\n";

ქვიზი — თავი 11

❓ ქვიზი

შეამოწმე ცოდნა თარიღებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რატომ ჯობს DateTimeImmutable?

  2. 2. რას ნიშნავს P10D DateInterval-ში?

  3. 3. რომელი format სიმბოლო ნიშნავs 4-ციფრიან წელს?

  4. 4. რას აბრუნებს createFromFormat არასწორ თარიღზე?

  5. 5. სად ჯობს დროის შენახვა ბაზაში?

12 📄 თავი 12. ფაილებთან მუშაობა

ფაილების კითხვა და ჩაწერა: დაბალი დონის fopen/fread/fwrite/fclose და მოსახერხებელი file_get_contents/file_put_contents.

fopen()

fopen() ხსნის ფაილს და აბრუნებს „handle"-ს. რეჟიმი (mode) განსაზღვრავs რას აკეთებ.

მნიშვნელობები

მნიშვნელობააღწერა
"r" კითხვა (დასაწყისიდან)
"w" ჩაწერა — შლის არსებულ შიგთავსს
"a" ჩაწერა ბოლოში (append)
"r+" კითხვა და ჩაწერა
"x" ჩაწერა მხოლოდ თუ ფაილი არ არსებობს

მაგალითები

ფაილის გახსნა
PHP
<?php
$handle = fopen("log.txt", "a");
if ($handle === false) {
    exit("ფაილი ვერ გაიხსნა\n");
}
fwrite($handle, "ახალი ჩანაწერი\n");
fclose($handle);

✗ ხშირი შეცდომები

  • "w" რეჟიმი შლის არსებულ შიგთავსს — დასამატებლად გამოიყენე "a".

fread()

fread() კითხულობs მითითებულ ბაიტებს handle-დან. ხშირად ფაილის ზომაზე იყენებენ filesize()-ს.

მაგალითები

ფაილის წაკითხვა
PHP
<?php
$handle = fopen("data.txt", "r");
if ($handle) {
    $content = fread($handle, filesize("data.txt"));
    echo $content;
    fclose($handle);
}
💡 რჩევა: მთლიანი ფაილის წასაკითხად file_get_contents() ბევრად მარტივია.

fwrite()

fwrite() წერs მონაცემს გახსნილ handle-ში და აბრუნებს ჩაწერილ ბაიტებს.

მაგალითები

ხაზობრივი ჩაწერა
PHP
<?php
$handle = fopen("notes.txt", "w");
fwrite($handle, "ხაზი 1\n");
fwrite($handle, "ხაზი 2\n");
fclose($handle);

fclose()

fclose() ხურავs handle-ს და ათავისუფლებს რესურსს. ყოველთვის დახურე ფაილი მუშაობის შემდეგ.

მაგალითები

handle-ის დახურვა
PHP
<?php
$handle = fopen("temp.txt", "r");
// ... მუშაობა ...
fclose($handle); // აუცილებელია

✓ საუკეთესო პრაქტიკა

  • handle ყოველთვის დახურე — ღია ფაილები რესურსს ხარჯავს.

file_get_contents()

კითხულობs მთელ ფაილს ერთ სტრიქონში ერთი ხაზით. მუშაობს URL-ებზეც. ერთ-ერთი ყველაზე ხშირად გამოყენებადი ფუნქცია.

მაგალითები

მთელი ფაილის წაკითხვა
PHP
<?php
$content = file_get_contents("config.json");
if ($content === false) {
    exit("ფაილი ვერ წაიკითხა\n");
}
$config = json_decode($content, true);
print_r($config);

✗ ხშირი შეცდომები

  • არარსებულ ფაილზე აბრუნებs false და warning-ს — შეამოწმე ჯერ file_exists().

file_put_contents()

წერs სტრიქონს ფაილში ერთი ხაზით (ხსნის, წერს, ხურავს). ფლაგი FILE_APPEND ამატებს ბოლოში, LOCK_EX — კეტავs პარალელური ჩაწერისგან.

სინტაქსი

Syntax
file_put_contents($file, $data, flags)

მაგალითები

ჩაწერა და დამატება
PHP
<?php
// გადააწერს:
file_put_contents("out.txt", "გამარჯობა\n");

// დაამატებს ბოლოში, კეტავს პარალელური ჩაწერისგან:
file_put_contents("log.txt", "ჩანაწერი\n", FILE_APPEND | LOCK_EX);

✓ საუკეთესო პრაქტიკა

  • ლოგებისთვის გამოიყენე FILE_APPEND | LOCK_EX.

სავარჯიშოები — თავი 12

📝 სავარჯიშოები

ივარჯიშე ფაილებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

ჩაწერე ტექსტი „გამარჯობა" ფაილში hello.txt და წაიკითხე უკან.

✅ გადაწყვეტა
PHP
<?php
file_put_contents("hello.txt", "გამარჯობა");
echo file_get_contents("hello.txt") . "\n";
საშუალო სავარჯიშო 2

დაამატე ლოგ-ფაილში ხაზი მიმდინარე დროით ყოველ გაშვებაზე (append).

💡 მინიშნება
გამოიყენე FILE_APPEND და date().
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$line = date("Y-m-d H:i:s") . " — გაშვება\n";
file_put_contents("app.log", $line, FILE_APPEND | LOCK_EX);
echo "ჩაიწერა\n";
პრაქტიკული სავარჯიშო 3

დაწერე ფუნქცია, რომელიც კითხულობს JSON ფაილს, ამატებს მასივში ახალ ელემენტს და უკან ინახავს.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function appendToJsonList(string $file, mixed $item): void
{
    $list = file_exists($file)
        ? json_decode(file_get_contents($file), true)
        : [];
    $list[] = $item;
    file_put_contents($file, json_encode($list, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}

appendToJsonList("items.json", ["name" => "ახალი"]);
echo file_get_contents("items.json") . "\n";

ქვიზი — თავი 12

❓ ქვიზი

შეამოწმე ცოდნა ფაილებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რომელი რეჟიმი შლის ფაილის არსებულ შიგთავსს?

  2. 2. რა ფლაგი ამატებs ფაილის ბოლოში წაშლის გარეშე?

  3. 3. რას აბრუნებს file_get_contents არარსებულ ფაილზე?

  4. 4. რა აუცილებელია fopen-ის შემდეგ მუშაობის ბოლოს?

  5. 5. რომელი ფუნქცია კითხულობს მთელ ფაილს ერთ ხაზში?

13 📁 თავი 13. დირექტორიები

საქაღალდეებთან მუშაობა: scandir, mkdir, rmdir, rename და არსებობის შემოწმება file_exists/is_file/is_dir.

scandir()

scandir() აბრუნებs საქაღალდის ფაილებისა და ქვესაქაღალდეების სიას მასივად (მათ შორის . და ..).

მაგალითები

საქაღალდის შიგთავსი
PHP
<?php
$files = scandir(".");
foreach ($files as $file) {
    if ($file === "." || $file === "..") {
        continue; // ვტოვებთ სპეციალურ ჩანაწერებს
    }
    echo $file . "\n";
}
💡 რჩევა: მხოლოდ გარკვეული გაფართოების ფაილებისთვის გამოიყენე glob("*.php").

mkdir()

ქმნის ახალ საქაღალდეს. მესამე არგუმენტი true ქმნის ჩალაგებულ საქაღალდეებსაც (recursive).

სინტაქსი

Syntax
mkdir($path, 0755, recursive = false)

მაგალითები

საქაღალდის შექმნა
PHP
<?php
if (!is_dir("uploads")) {
    mkdir("uploads", 0755);
}

// ჩალაგებული საქაღალდეები ერთბაშად:
mkdir("data/2026/06", 0755, true);

✗ ხშირი შეცდომები

  • არსებულ საქაღალდეზე mkdir აბრუნებს warning-ს — ჯერ შეამოწმე is_dir().

rmdir()

შლის ცარიელ საქაღალდეს. სავსე საქაღალდის წასაშლელად ჯერ მისი შიგთავსი უნდა წაიშალოს.

მაგალითები

ცარიელი საქაღალდის წაშლა
PHP
<?php
if (is_dir("temp")) {
    rmdir("temp"); // მუშაობს მხოლოდ ცარიელზე
}

✗ ხშირი შეცდომები

  • სავსე საქაღალდის წაშლის მცდელობა — rmdir ვერ შლის; ჯერ ფაილები წაშალე.

rename()

ცვლის ფაილის ან საქაღალდის სახელს, ან გადააქვს სხვა ადგილას (move).

მაგალითები

სახელის შეცვლა / გადატანა
PHP
<?php
rename("old.txt", "new.txt");          // სახელის შეცვლა
rename("new.txt", "archive/new.txt");  // გადატანა

file_exists()

ამოწმებs არსებობს თუ არა ფაილი ან საქაღალდე მითითებულ გზაზე.

მაგალითები

არსებობის შემოწმება
PHP
<?php
if (file_exists("config.json")) {
    echo "კონფიგი მოიძებნა\n";
} else {
    echo "კონფიგი არ არსებობს\n";
}

is_file()

აბრუნებs true-ს, თუ გზა არსებობს და ფაილია (და არა საქაღალდე).

მაგალითები

ფაილის შემოწმება
PHP
<?php
var_dump(is_file("index.php")); // true თუ ფაილია
var_dump(is_file("uploads"));   // false თუ საქაღალდეა

is_dir()

აბრუნებs true-ს, თუ გზა არსებობს და საქაღალდეა.

მაგალითები

საქაღალდის შემოწმება
PHP
<?php
if (!is_dir("logs")) {
    mkdir("logs", 0755);
}
echo "logs მზადაა\n";

✓ საუკეთესო პრაქტიკა

  • საქაღალდეში ჩაწერამდე ყოველთვის შეამოწმე/შექმენი is_dir() + mkdir().

სავარჯიშოები — თავი 13

📝 სავარჯიშოები

ივარჯიშე დირექტორიებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

შექმენი საქაღალდე uploads, თუ ის არ არსებობს.

✅ გადაწყვეტა
PHP
<?php
if (!is_dir("uploads")) {
    mkdir("uploads", 0755);
    echo "შეიქმნა\n";
}
საშუალო სავარჯიშო 2

ჩამოთვალე მიმდინარე საქაღალდის მხოლოდ ფაილები (საქაღალდეების გარეშე).

💡 მინიშნება
გამოიყენე scandir() + is_file().
✅ გადაწყვეტა
PHP
<?php
foreach (scandir(".") as $entry) {
    if (is_file($entry)) {
        echo $entry . "\n";
    }
}
პრაქტიკული სავარჯიშო 3

დაწერე ფუნქცია, რომელიც ითვლის საქაღალდეში ფაილების რაოდენობას (გარდა . და ..).

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function countFiles(string $dir): int
{
    if (!is_dir($dir)) {
        return 0;
    }
    $count = 0;
    foreach (scandir($dir) as $e) {
        if ($e !== "." && $e !== ".." && is_file($dir . "/" . $e)) {
            $count++;
        }
    }
    return $count;
}

echo countFiles(".") . "\n";

ქვიზი — თავი 13

❓ ქვიზი

შეამოწმე ცოდნა დირექტორიებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რას აბრუნებს scandir(".")?

  2. 2. როგორ შევქმნათ ჩალაგებული საქაღალდეები ერთბაშად?

  3. 3. რა საქაღალდეს შლის rmdir()?

  4. 4. რა აბრუნებს is_file("uploads")-ს, თუ uploads საქაღალდეა?

  5. 5. რა ფუნქცია ცვლის ფაილის სახელს ან გადააქვს?

14 ⚠️ თავი 14. შეცდომების დამუშავება

გამონაკლისების მართვა try/catch/finally-ით, throw, საკუთარი Exception კლასები და შეცდომების ჟურნალირება.

try / catch

try ბლოკში თავსდება კოდი, რომელმაც შესაძლოა გამონაკლისი (exception) გამოიწვიოს; catch იჭერს მას და განაგრძობს მუშაობას პროგრამის გაჩერების გარეშე.

სინტაქსი

Syntax
try {
    // საშიში კოდი
} catch (Exception $e) {
    // შეცდომის დამუშავება
}

მაგალითები

გამონაკლისის დაჭერა
PHP
<?php
declare(strict_types=1);

function divide(int $a, int $b): float
{
    if ($b === 0) {
        throw new InvalidArgumentException("ნულზე გაყოფა აკრძალულია");
    }
    return $a / $b;
}

try {
    echo divide(10, 0);
} catch (InvalidArgumentException $e) {
    echo "შეცდომა: " . $e->getMessage() . "\n";
}

✓ საუკეთესო პრაქტიკა

  • დაიჭირე კონკრეტული გამონაკლისი, არა ზოგადი \Throwable, თუ რეალურად არ გჭირდება ყველაფერი.

finally

finally ბლოკი სრულდება ყოველთვის — იქნება exception თუ არა. იდეალურია რესურსების დასახურად (ფაილი, კავშირი).

მაგალითები

რესურსის გათავისუფლება
PHP
<?php
$handle = fopen("data.txt", "r");

try {
    $content = fread($handle, 1024);
    echo $content;
} catch (Exception $e) {
    echo "შეცდომა: " . $e->getMessage() . "\n";
} finally {
    fclose($handle); // ყოველთვის სრულდება
    echo "ფაილი დაიხურა\n";
}

throw

throw „აგდებs" გამონაკლისს. PHP 8-ში throw გამოსახულებაა — შეიძლება გამოიყენო ternary-სა და ??-ში.

მაგალითები

throw როგორც გამოსახულება
PHP
<?php
declare(strict_types=1);

function getConfig(array $config, string $key): string
{
    return $config[$key] ?? throw new RuntimeException("გასაღები '$key' აკლია");
}

$config = ["host" => "localhost"];
echo getConfig($config, "host") . "\n"; // localhost
// getConfig($config, "port"); // აგდებს RuntimeException-ს

Exceptions

გამონაკლისი არის ობიექტი, რომელიც აღწერს შეცდომას. PHP-ს აქვს ჩაშენებული იერარქია (Exception, RuntimeException, InvalidArgumentException...). შეიძლება საკუთარი კლასის შექმნაც. ერთ try-ს შეიძლება ჰქონდეს რამდენიმე catch ან გაერთიანებული catch (A | B).

მნიშვნელობები

მნიშვნელობააღწერა
getMessage() შეცდომის ტექსტი
getCode() შეცდომის კოდი
getFile() / getLine() სად მოხდა
Throwable საბაზისო ინტერფეისი (Error + Exception)

მაგალითები

საკუთარი exception და მრავალი catch
PHP
<?php
declare(strict_types=1);

class ValidationException extends Exception {}

function register(string $email): void
{
    if (!str_contains($email, "@")) {
        throw new ValidationException("არასწორი ელფოსტა");
    }
    echo "რეგისტრირებულია: $email\n";
}

try {
    register("invalid");
} catch (ValidationException | InvalidArgumentException $e) {
    echo "ვალიდაცია ჩავარდა: " . $e->getMessage() . "\n";
} catch (Throwable $e) {
    echo "სხვა შეცდომა: " . $e->getMessage() . "\n";
}

✓ საუკეთესო პრაქტიკა

  • შექმენი დომენ-სპეციფიკური exception კლასები (მაგ. ValidationException).

ლოგირება

შეცდომების ჟურნალირება

პროდაქშენში არასოდეს აჩვენო შეცდომის დეტალები მომხმარებელს — ისინი ჟურნალში (log) ჩაწერე. გამოიყენე error_log().

მაგალითები

ლოგირება error_log-ით
PHP
<?php
declare(strict_types=1);

try {
    throw new RuntimeException("ბაზასთან კავშირი ვერ შედგა");
} catch (Throwable $e) {
    // მომხმარებელს — ზოგადი შეტყობინება:
    echo "სამწუხაროდ, მოხდა შეცდომა. სცადეთ მოგვიანებით.\n";
    // ჟურნალში — დეტალები:
    error_log("[" . date("Y-m-d H:i:s") . "] " . $e->getMessage());
}

✓ საუკეთესო პრაქტიკა

  • მომხმარებელს — ზოგადი ტექსტი; ჟურნალში — სრული დეტალი (stack trace).

✗ ხშირი შეცდომები

  • პროდაქშენში display_errors = On — გაჟონვის რისკი; გამორთე და დაეყრდენი ლოგებს.

სავარჯიშოები — თავი 14

📝 სავარჯიშოები

ივარჯიშე შეცდომების დამუშავებაზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დაიჭირე გაყოფა ნულზე try/catch-ით (გამოიყენე intdiv()).

✅ გადაწყვეტა
PHP
<?php
try {
    echo intdiv(10, 0);
} catch (DivisionByZeroError $e) {
    echo "ნულზე გაყოფა\n";
}
საშუალო სავარჯიშო 2

შექმენი NotFoundException კლასი და დააგდე, თუ მასივში გასაღები არ მოიძებნა.

💡 მინიშნება
კლასი extends Exception; გამოიყენე throw ... ?? throw.
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

class NotFoundException extends Exception {}

function find(array $data, string $key): mixed
{
    return $data[$key] ?? throw new NotFoundException("'$key' ვერ მოიძებნა");
}

try {
    find(["a" => 1], "b");
} catch (NotFoundException $e) {
    echo $e->getMessage() . "\n";
}
პრაქტიკული სავარჯიშო 3

დაწერე ფუნქცია, რომელიც კითხულობს JSON ფაილს და დასაშვებად ამუშავებს შეცდომებს: ფაილი არ არსებობს, არასწორი JSON. დეტალები ჩაწერე ლოგში.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function readJsonSafe(string $file): ?array
{
    try {
        if (!is_file($file)) {
            throw new RuntimeException("ფაილი არ არსებობს: $file");
        }
        $raw = file_get_contents($file);
        return json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
    } catch (Throwable $e) {
        error_log("readJsonSafe: " . $e->getMessage());
        return null;
    }
}

var_dump(readJsonSafe("missing.json")); // null + ლოგი

ქვიზი — თავი 14

❓ ქვიზი

შეამოწმე ცოდნა შეცდომების დამუშავებაზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რომელი ბლოკი სრულდება ყოველთვის?

  2. 2. რა აკეთებs throw?

  3. 3. რომელი საბაზისო ინტერფეისი მოიცავს Error-სა და Exception-ს?

  4. 4. როგორ დავიჭიროთ ორი ტიპის exception ერთ catch-ში?

  5. 5. რა უნდა გავაკეთოთ შეცდომის დეტალებთან პროდაქშენში?

15 🍪 თავი 15. სესიები და Cookie-ები

მდგომარეობის შენახვა მოთხოვნებს შორის: session_start, $_SESSION და cookie-ების შექმნა, წაკითხვა, წაშლა.

session_start()

HTTP უმეხსიეროა (stateless). სესია სერვერზე ინახავს მონაცემს მომხმარებლისთვის (მაგ. ვინ არის შესული). session_start() უნდა გამოიძახო ნებისმიერ output-მდე, ფაილის თავში.

სინტაქსი

Syntax
session_start();

მაგალითები

სესიის დაწყება
PHP
<?php
declare(strict_types=1);
session_start(); // აუცილებლად output-მდე!

$_SESSION["visits"] = ($_SESSION["visits"] ?? 0) + 1;
echo "ეს თქვენი {$_SESSION["visits"]}-ე ვიზიტია\n";

✗ ხშირი შეცდომები

  • session_start() output-ის (echo, HTML) შემდეგ — „headers already sent" შეცდომა.

$_SESSION

სესიის ცვლადები

სესიის მონაცემი ინახება სუპერგლობალურ $_SESSION მასივში. წასაშლელად — unset(); სესიის სრულად დასასრულებლად — session_destroy().

მაგალითები

შენახვა, წაკითხვა, წაშლა
PHP
<?php
session_start();

// შენახვა (მაგ. login-ის შემდეგ):
$_SESSION["user_id"] = 42;

// წაკითხვა:
$userId = $_SESSION["user_id"] ?? null;

// კონკრეტული ცვლადის წაშლა:
unset($_SESSION["user_id"]);

// სრული გასვლა (logout):
$_SESSION = [];
session_destroy();

✓ საუკეთესო პრაქტიკა

  • login-ის შემდეგ გამოიძახე session_regenerate_id(true) — იცავs session fixation-ისგან.

სავარჯიშოები — თავი 15

📝 სავარჯიშოები

ივარჯიშე სესიებსა და cookie-ებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დათვალე, რამდენჯერ ეწვია მომხმარებელი გვერდს, სესიის ცვლადით.

✅ გადაწყვეტა
PHP
<?php
session_start();
$_SESSION["count"] = ($_SESSION["count"] ?? 0) + 1;
echo "ვიზიტი #{$_SESSION["count"]}\n";
საშუალო სავარჯიშო 2

შეინახე მომხმარებლის არჩეული ენა cookie-ში 30 დღით და წაიკითხე იგი.

💡 მინიშნება
setcookie expires-ით, შემდეგ $_COOKIE.
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$lang = $_GET["lang"] ?? ($_COOKIE["lang"] ?? "ka");

setcookie("lang", $lang, [
    "expires"  => time() + 86400 * 30,
    "path"     => "/",
    "httponly" => true,
    "samesite" => "Lax",
]);

echo "ენა: $lang\n";
პრაქტიკული სავარჯიშო 3

დაწერე მარტივი „logout" ლოგიკა: გაასუფთავე სესია და გაანადგურე იგი უსაფრთხოდ.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);
session_start();

// მონაცემის გასუფთავება
$_SESSION = [];

// სესიის cookie-ს გაუქმება
if (ini_get("session.use_cookies")) {
    $p = session_get_cookie_params();
    setcookie(session_name(), "", [
        "expires" => time() - 3600,
        "path"    => $p["path"],
    ]);
}

session_destroy();
echo "გამოსვლა შესრულდა\n";

ქვიზი — თავი 15

❓ ქვიზი

შეამოწმე ცოდნა სესიებსა და cookie-ებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. სად ინახება სესიის მონაცემი?

  2. 2. როდის უნდა გამოვიძახოთ session_start()?

  3. 3. სად ინახება cookie?

  4. 4. როგორ წავშალოთ cookie?

  5. 5. რას აკეთებs httponly ოფცია?

16 🌐 თავი 16. HTTP მოთხოვნები და პარამეტრები

HTTP მეთოდები (GET/POST/PUT/DELETE), პარამეტრების მიღება $_GET/$_POST/php://input-ით და მარტივი API ენდპოინტის აგება.

GET / POST / PUT / DELETE

HTTP მეთოდები

HTTP მეთოდი აღწერs მოქმედების ტიპს. REST-ში თითოეულს თავისი დანიშნულება აქვს.

მნიშვნელობები

მნიშვნელობააღწერა
GET მონაცემის წაკითხვა (არ ცვლის სერვერის მდგომარეობას)
POST ახალი რესურსის შექმნა / ფორმის გაგზავნა
PUT არსებული რესურსის განახლება/ჩანაცვლება
DELETE რესურსის წაშლა

მაგალითები

მეთოდის დადგენა
PHP
<?php
$method = $_SERVER["REQUEST_METHOD"];

echo match ($method) {
    "GET"    => "მონაცემის წაკითხვა\n",
    "POST"   => "ახლის შექმნა\n",
    "PUT"    => "განახლება\n",
    "DELETE" => "წაშლა\n",
    default  => "უცნობი მეთოდი\n",
};

$_GET

$_GET შეიცავს URL-ის query string-ის პარამეტრებს (მაგ. ?page=2&q=php). გამოიყენება ფილტრაცია/ძიება/გვერდები. ⚠ მონაცემი მომხმარებლისგან მოდის — გადაამოწმე.

მაგალითები

query პარამეტრები
PHP
<?php
declare(strict_types=1);

// URL: /search.php?q=php&page=2
$query = trim($_GET["q"] ?? "");
$page  = max(1, (int) ($_GET["page"] ?? 1));

echo "ძიება: '$query', გვერდი: $page\n";

✗ ხშირი შეცდომები

  • $_GET-ის პირდაპირ გამოყენება HTML-ში — XSS-ის რისკი; გაიტანე htmlspecialchars()-ით.

$_POST

$_POST შეიცავs ფორმის (POST სხეულის) მონაცემს. გამოიყენება მონაცემის შესაქმნელად/გასაგზავნად, რადგან URL-ში არ ჩანს.

მაგალითები

ფორმის დამუშავება
PHP
<?php
declare(strict_types=1);

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $name  = trim($_POST["name"] ?? "");
    $email = trim($_POST["email"] ?? "");

    if ($name === "" || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
        echo "შეავსეთ ველები სწორად\n";
    } else {
        echo "მიღებულია: $name <$email>\n";
    }
}

php://input (JSON body)

php://input

როცა კლიენტი აგზავნის JSON-ს (და არა ფორმას), მონაცემი $_POST-ში არ ხვდება — წაიკითხე ნედლი სხეული php://input-ით და გააანალიზე json_decode-ით.

მაგალითები

JSON სხეულის წაკითხვა
PHP
<?php
declare(strict_types=1);

$raw = file_get_contents("php://input");
$data = json_decode($raw, true) ?? [];

$title = trim($data["title"] ?? "");
echo "მიღებული title: $title\n";
💡 რჩევა: API-ები ხშირად JSON-ით საუბრობენ — php://input + json_decode სტანდარტული წყვილია.

მარტივი API

მარტივი API ენდპოინტი

გავაერთიანოთ ცოდნა: მეთოდის მიხედვით მარშრუტიზაცია, JSON სხეულის წაკითხვა, JSON პასუხი სწორი header-ით და status კოდით.

მაგალითები

მინი REST endpoint
PHP
<?php
declare(strict_types=1);
header("Content-Type: application/json; charset=utf-8");

$method = $_SERVER["REQUEST_METHOD"];
$body = json_decode(file_get_contents("php://input"), true) ?? [];

function respond(int $status, array $data): never
{
    http_response_code($status);
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

match ($method) {
    "GET"  => respond(200, ["items" => ["ა", "ბ", "გ"]]),
    "POST" => isset($body["name"])
        ? respond(201, ["created" => $body["name"]])
        : respond(422, ["error" => "name აკლია"]),
    default => respond(405, ["error" => "მეთოდი დაუშვებელია"]),
};

✓ საუკეთესო პრაქტიკა

  • ყოველთვис დააბრუნე სწორი status code (200, 201, 404, 422, 405...).
  • შემავალი მონაცემი გადაამოწმე პასუხის გაცემამდე.

სავარჯიშოები — თავი 16

📝 სავარჯიშოები

ივარჯიშე HTTP-ზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

წაიკითხე query პარამეტრი name და გამოიტანე მისალმება (ნაგულისხმევი — „სტუმარი").

✅ გადაწყვეტა
PHP
<?php
$name = trim($_GET["name"] ?? "სტუმარი");
echo "გამარჯობა, " . htmlspecialchars($name) . "\n";
საშუალო სავარჯიშო 2

დაამუშავე POST ფორმა: მიიღე email, გადაამოწმე FILTER_VALIDATE_EMAIL-ით და დააბრუნე შესაბამისი ტექსტი.

💡 მინიშნება
შეამოწმე $_SERVER["REQUEST_METHOD"] === "POST".
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $email = trim($_POST["email"] ?? "");
    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
        echo "სწორი ელფოსტა: $email\n";
    } else {
        echo "არასწორი ელფოსტა\n";
    }
}
პრაქტიკული სავარჯიშო 3

ააგე endpoint, რომელიც GET-ზე აბრუნებs პროდუქტების სიას JSON-ად, POST-ზე კი ამატებს ახალს (validation-ით) და აბრუნებს 201-ს.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);
header("Content-Type: application/json; charset=utf-8");

$method = $_SERVER["REQUEST_METHOD"];
$products = ["ჩანთა", "კალამი"];

if ($method === "GET") {
    echo json_encode($products, JSON_UNESCAPED_UNICODE);
} elseif ($method === "POST") {
    $body = json_decode(file_get_contents("php://input"), true) ?? [];
    if (empty($body["name"])) {
        http_response_code(422);
        echo json_encode(["error" => "name აკლია"], JSON_UNESCAPED_UNICODE);
    } else {
        http_response_code(201);
        echo json_encode(["created" => $body["name"]], JSON_UNESCAPED_UNICODE);
    }
}

ქვიზი — თავი 16

❓ ქვიზი

შეამოწმე ცოდნა HTTP-ზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რომელი მეთოდი გამოიყენება მონაცემის წაკითხვისთვის?

  2. 2. სად მოდის URL-ის query პარამეტრები?

  3. 3. როგორ წავიკითხოთ JSON სხეული მოთხოვნიდან?

  4. 4. რომელი status code ნიშნავs „შეიქმნა"?

  5. 5. რა რისკია $_GET-ის პირდაპირ HTML-ში გამოტანის?

17 🏛️ თავი 17. ობიექტზე ორიენტირებული პროგრამირება

OOP-ის საფუძვლები PHP 8-ში: კლასები, ობიექტები, თვისებები, მეთოდები, კონსტრუქტორები, ხილვადობა, მემკვიდრეობა, ინტერფეისები, trait-ები და აბსტრაქცია.

კლასები (class)

კლასები

კლასი არის „ნახაზი" ობიექტისთვის — ის აერთიანებს მონაცემს (თვისებებს) და ქცევას (მეთოდებს). ცხადდება class-ით.

სინტაქსი

Syntax
class ClassName {
    // თვისებები და მეთოდები
}

მაგალითები

მარტივი კლასი
PHP
<?php
declare(strict_types=1);

class Car
{
    public string $brand = "Toyota";

    public function honk(): string
    {
        return "ბიპ-ბიპ!";
    }
}

✓ საუკეთესო პრაქტიკა

  • კლასის სახელი იწერება PascalCase-ით.
  • ერთი კლასი — ერთი პასუხისმგებლობა.

ობიექტები (new)

ობიექტები

ობიექტი არის კლასის კონკრეტული „ეგზემპლარი". იქმნება new-ით. ერთი კლასიდან შეიძლება მრავალი დამოუკიდებელი ობიექტი.

მაგალითები

ობიექტის შექმნა და გამოყენება
PHP
<?php
declare(strict_types=1);

class Car
{
    public string $brand = "Toyota";
    public function honk(): string { return "ბიპ!"; }
}

$car1 = new Car();
$car2 = new Car();
$car2->brand = "BMW";

echo $car1->brand . "\n"; // Toyota
echo $car2->brand . "\n"; // BMW
echo $car1->honk() . "\n";

თვისებები (properties)

თვისებები

თვისება არის ცვლადი კლასის შიგნით — ობიექტის მონაცემი. რეკომენდებულია ტიპის მითითება. წვდომა — $object->property.

მაგალითები

ტიპიზებული თვისებები
PHP
<?php
declare(strict_types=1);

class Product
{
    public string $name = "";
    public float $price = 0.0;
    public bool $inStock = true;
}

$p = new Product();
$p->name = "კლავიატურა";
$p->price = 49.99;

echo "{$p->name}: {$p->price} ₾\n";

მეთოდები

მეთოდი არის ფუნქცია კლასის შიგნით. ობიექტის საკუთარ თვისებებს წვდება $this-ით.

მაგალითები

$this და მეთოდები
PHP
<?php
declare(strict_types=1);

class BankAccount
{
    public float $balance = 0.0;

    public function deposit(float $amount): void
    {
        $this->balance += $amount;
    }

    public function getBalance(): float
    {
        return $this->balance;
    }
}

$acc = new BankAccount();
$acc->deposit(100);
$acc->deposit(50);
echo $acc->getBalance() . "\n"; // 150

__construct

კონსტრუქტორები

__construct() ავტომატურად სრულდება ობიექტის შექმნისას — გამოიყენება საწყისი მნიშვნელობების დასაყენებლად. PHP 8-ში არსებობს constructor property promotion: პარამეტრები პირდაპირ ხდება თვისებები.

მაგალითები

promotion-ით
PHP
<?php
declare(strict_types=1);

class User
{
    // პარამეტრები ავტომატურად ხდება თვისებები (PHP 8):
    public function __construct(
        public string $name,
        public string $email,
        public bool $active = true,
    ) {}
}

$user = new User("ანა", "ana@example.com");
echo "{$user->name} <{$user->email}>\n";

✓ საუკეთესო პრაქტიკა

  • constructor property promotion ბევრად ამოკლებს boilerplate-ს.

__destruct

დესტრუქტორები

__destruct() სრულდება, როცა ობიექტი ნადგურდება (აღარ აქვს მიმართვა ან სკრიპტი მთავრდება). გამოიყენება გაწმენდისთვის (მაგ. კავშირის დახურვა).

მაგალითები

გაწმენდა
PHP
<?php
declare(strict_types=1);

class Logger
{
    public function __construct(private string $name)
    {
        echo "Logger '{$this->name}' შეიქმნა\n";
    }

    public function __destruct()
    {
        echo "Logger '{$this->name}' განადგურდა\n";
    }
}

$log = new Logger("app");
// სკრიპტის ბოლოს ავტომატურად გამოიძახება __destruct

public / protected / private

ხილვადობა

ხილვადობა აკონტროლებს, საიდან არის წვდომადი წევრი:

მნიშვნელობები

მნიშვნელობააღწერა
public ყველგან წვდომადი (ნაგულისხმევი)
protected მხოლოდ კლასსა და მის მემკვიდრეებში
private მხოლოდ თვით ამ კლასში

მაგალითები

ინკაფსულაცია
PHP
<?php
declare(strict_types=1);

class BankAccount
{
    private float $balance = 0.0; // გარედან ვერ შეცვლი

    public function deposit(float $amount): void
    {
        if ($amount <= 0) {
            throw new InvalidArgumentException("თანხა დადებითი უნდა იყოს");
        }
        $this->balance += $amount;
    }

    public function getBalance(): float
    {
        return $this->balance;
    }
}

$acc = new BankAccount();
$acc->deposit(100);
echo $acc->getBalance() . "\n";
// $acc->balance = 999999; // შეცდომა: private

✓ საუკეთესო პრაქტიკა

  • თვისებები ჩვეულებრივ private/protected გახადე და წვდომა მეთოდებით მართე (ინკაფსულაცია).

static

static წევრები

static წევრი ეკუთვნის კლასს, და არა კონკრეტულ ობიექტს. წვდომა — ClassName::member ან კლასის შიგნით self::.

მაგალითები

static თვისება და მეთოდი
PHP
<?php
declare(strict_types=1);

class Counter
{
    public static int $count = 0;

    public static function increment(): void
    {
        self::$count++;
    }
}

Counter::increment();
Counter::increment();
echo Counter::$count . "\n"; // 2
💡 რჩევა: self:: მიუთითებს მიმდინარე კლასს; static:: — late static binding (მემკვიდრეს).

extends

მემკვიდრეობა

მემკვიდრეობა საშუალებას აძლევს კლასს „მემკვიდრეობით" მიიღოს სხვა კლასის წევრები extends-ით. მეთოდი შეიძლება გადაიფაროს (override); მშობელს წვდები parent::-ით. final კრძალავs შემდგომ მემკვიდრეობას/override-ს.

მაგალითები

extends და override
PHP
<?php
declare(strict_types=1);

class Animal
{
    public function __construct(protected string $name) {}

    public function speak(): string
    {
        return "{$this->name} ხმას გამოსცემს";
    }
}

class Dog extends Animal
{
    public function speak(): string // override
    {
        return parent::speak() . ": ჰავ-ჰავ!";
    }
}

$dog = new Dog("რექსი");
echo $dog->speak() . "\n";

✗ ხშირი შეცდომები

  • ღრმა მემკვიდრეობის იერარქია — ხშირად სჯობs კომპოზიცია (composition over inheritance).

interface

ინტერფეისები

ინტერფეისი განსაზღვრავs „კონტრაქტს" — რა მეთოდები უნდა ჰქონდეს კლასს, იმპლემენტაციის გარეშე. კლასი იყენებს implements-ით და შეიძლება რამდენიმე ინტერფეისი ერთდროულად.

მაგალითები

კონტრაქტი
PHP
<?php
declare(strict_types=1);

interface PaymentGateway
{
    public function pay(float $amount): bool;
}

class PayPal implements PaymentGateway
{
    public function pay(float $amount): bool
    {
        echo "PayPal: გადახდილია $amount ₾\n";
        return true;
    }
}

function checkout(PaymentGateway $gateway, float $sum): void
{
    $gateway->pay($sum); // ნებისმიერი იმპლემენტაცია მუშაობს
}

checkout(new PayPal(), 100);

✓ საუკეთესო პრაქტიკა

  • დაიწერე კოდი ინტერფეისზე დაყრდნობით — გაამარტივებს ჩანაცვლებასა და ტესტირებას.

trait

trait-ები

trait არის კოდის (მეთოდების) ხელახლა გამოყენებადი ნაკრები, რომელსაც რამდენიმე კლასი იზიარებს use-ით — PHP-ში ერთჯერადი მემკვიდრეობის შეზღუდვის გვერდის ასავლელად.

მაგალითები

საერთო ქცევის გაზიარება
PHP
<?php
declare(strict_types=1);

trait Timestampable
{
    public function touch(): string
    {
        return "განახლდა: " . date("Y-m-d H:i:s");
    }
}

class Article
{
    use Timestampable;
    public function __construct(public string $title) {}
}

$a = new Article("ჩემი სტატია");
echo $a->touch() . "\n";

✗ ხშირი შეცდომები

  • trait-ების გადამეტებული გამოყენება — ფარული დამოკიდებულებები; გამოიყენე გონივრულად.

abstract

აბსტრაქტული კლასები

აბსტრაქტული კლასი ვერ იქმნება პირდაპირ (new არ შეიძლება) — ის მხოლოდ მემკვიდრეობისთვისაა. შეიძლება ჰქონდეs როგორც მზა, ისე abstract მეთოდები, რომელთა იმპლემენტაცია მემკვიდრემ უნდა უზრუნველყოს.

მნიშვნელობები

მნიშვნელობააღწერა
interface მხოლოდ კონტრაქტი, იმპლემენტაცია არ აქვს; მრავალს implements
abstract შეიძლება ჰქონდეს მზა მეთოდებიც; მხოლოდ ერთს extends

მაგალითები

აბსტრაქცია
PHP
<?php
declare(strict_types=1);

abstract class Shape
{
    abstract public function area(): float; // იმპლემენტაცია მემკვიდრეს

    public function describe(): string
    {
        return "ფართობი: " . round($this->area(), 2);
    }
}

class Circle extends Shape
{
    public function __construct(private float $radius) {}

    public function area(): float
    {
        return 3.14159 * $this->radius ** 2;
    }
}

$c = new Circle(5);
echo $c->describe() . "\n"; // ფართობი: 78.54
// new Shape(); // შეცდომა — აბსტრაქტული

სავარჯიშოები — თავი 17

📝 სავარჯიშოები

ივარჯიშე OOP-ზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

შექმენი კლასი Rectangle თვისებებით width, height და მეთოდით area().

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

class Rectangle
{
    public function __construct(
        public float $width,
        public float $height,
    ) {}

    public function area(): float
    {
        return $this->width * $this->height;
    }
}

$r = new Rectangle(4, 5);
echo $r->area() . "\n"; // 20
საშუალო სავარჯიშო 2

შექმენი აბსტრაქტული Employee მეთოდით salary() და ორი მემკვიდრე: Manager და Developer სხვადასხვა ანაზღაურებით.

💡 მინიშნება
აბსტრაქტული მეთოდი, შემდეგ override მემკვიდრეებში.
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

abstract class Employee
{
    public function __construct(public string $name) {}
    abstract public function salary(): float;
}

class Manager extends Employee
{
    public function salary(): float { return 5000; }
}

class Developer extends Employee
{
    public function salary(): float { return 4500; }
}

$staff = [new Manager("ანა"), new Developer("გია")];
foreach ($staff as $e) {
    echo "{$e->name}: {$e->salary()} ₾\n";
}
პრაქტიკული სავარჯიშო 3

აღწერe Repository ინტერფეისი მეთოდებით save() და all(), შემდეგ შექმენი InMemoryRepository, რომელიც მასივში ინახავs ჩანაწერებს.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

interface Repository
{
    public function save(array $item): void;
    public function all(): array;
}

class InMemoryRepository implements Repository
{
    private array $items = [];

    public function save(array $item): void
    {
        $this->items[] = $item;
    }

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

$repo = new InMemoryRepository();
$repo->save(["name" => "ანა"]);
$repo->save(["name" => "გია"]);
print_r($repo->all());

ქვიზი — თავი 17

❓ ქვიზი

შეამოწმე ცოდნა OOP-ზე.

🧠 შეამოწმე შენი ცოდნა 0 / 6
  1. 1. რა არის ობიექტი?

  2. 2. რომელ თვისებას ვერ წვდები კლასის გარედან?

  3. 3. რას აკეთებs __construct()?

  4. 4. რა გამოიყენება სხვა კლასისგან მემკვიდრეობისთვის?

  5. 5. რა განასხვავებs interface-ს abstract კლასისგან?

  6. 6. რა არის trait?

18 🔐 თავი 18. დაშიფვრა და უსაფრთხოება

უსაფრთხოების საფუძვლები: პაროლების ჰეშირება, hash, შემთხვევითი ტოკენები და დაცვა SQL Injection-ისა და XSS-ისგან, input-ის ვალიდაცია.

password_hash()

პაროლი არასოდეს ინახება ღია ტექსტად. password_hash() ქმნის უსაფრთხო ჰეშს (ნაგულისხმევად bcrypt) ჩაშენებული „მარილით" (salt).

სინტაქსი

Syntax
password_hash($password, PASSWORD_DEFAULT)

მაგალითები

პაროლის ჰეშირება
PHP
<?php
declare(strict_types=1);

$password = "ჩემიSecret123";
$hash = password_hash($password, PASSWORD_DEFAULT);

echo $hash . "\n"; // $2y$10$... (60 სიმბოლო)
// სწორედ ეს $hash ინახება ბაზაში, არა თვითონ პაროლი

✓ საუკეთესო პრაქტიკა

  • ყოველთვის PASSWORD_DEFAULT — ის ავტომატურად ახლდება ძლიერ ალგორითმზე.

✗ ხშირი შეცდომები

  • პაროლის შენახვა ღია ტექსტად ან md5/sha1-ით — დაუშვებელია.
  • ბაზაში ველის ზომა < 60 სიმბოლო — ჰეში მოიჭრება. გამოიყენე VARCHAR(255).

password_verify()

შესვლისას password_verify() ადარებს შეყვანილ პაროლს შენახულ ჰეშს. ჰეშის „გაშიფვრა" შეუძლებელია — მხოლოდ ვერიფიკაცია.

მაგალითები

პაროლის შემოწმება
PHP
<?php
declare(strict_types=1);

$hash = password_hash("Secret123", PASSWORD_DEFAULT);

if (password_verify("Secret123", $hash)) {
    echo "პაროლი სწორია\n";
} else {
    echo "არასწორი პაროლი\n";
}
💡 რჩევა: password_needs_rehash()-ით შეგიძლია ძველი ჰეშები ახალ ალგორითმზე გადაიყვანო შესვლისას.

hash()

hash() ქმნის მონაცემის ჩამოსხმას (checksum), მაგ. ფაილის მთლიანობის შესამოწმებლად. ⚠ პაროლებისთვის არ გამოიყენო — სწრაფი ალგორითმები (sha256) brute-force-ისთვის სუსტია; პაროლს password_hash სჭირდება.

მაგალითები

checksum
PHP
<?php
$data = "მნიშვნელოვანი ტექსტი";
echo hash("sha256", $data) . "\n";

// ფაილის ჩამოსხმა მთლიანობისთვის:
// echo hash_file("sha256", "file.zip");

✗ ხშირი შეცდომები

  • პაროლის hash("sha256", ...)-ით შენახვა — უსაფრთხო არ არის.

random_bytes()

random_bytes() აგენერირებს კრიპტოგრაფიულად უსაფრთხო შემთხვევით ბაიტებს — გამოსადეგია ტოკენების, CSRF-ის და სესიის იდენტიფიკატორებისთვის. ტექსტად გადასაყვანად — bin2hex().

მაგალითები

უსაფრთხო ტოკენი
PHP
<?php
declare(strict_types=1);

$token = bin2hex(random_bytes(16)); // 32-სიმბოლოიანი hex
echo $token . "\n";

✗ ხშირი შეცდომები

  • rand()/mt_rand()-ის გამოყენება უსაფრთხო ტოკენებისთვის — ისინი პროგნოზირებადია.

SQL Injection

SQL Injection-ის გაცნობიერება

SQL Injection ხდება, როცა მომხმარებლის input პირდაპირ ჩაისმება SQL მოთხოვნაში. დაცვა: prepared statements (PDO) — მონაცემი არასოდეს ერევა მოთხოვნის ტექსტს.

მაგალითები

არასწორი vs სწორი (PDO)
PHP
<?php
declare(strict_types=1);

// ❌ საშიში — ინექციის რისკი:
// $sql = "SELECT * FROM users WHERE name = '" . $_GET["name"] . "'";

// ✅ უსაფრთხო — prepared statement:
$pdo = new PDO("sqlite::memory:");
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->execute(["name" => $_GET["name"] ?? ""]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

✓ საუკეთესო პრაქტიკა

  • არასოდეს გააერთიანო input SQL-თან კონკატენაციით — ყოველთვის prepared statements.

XSS

XSS-ის გაცნობიერება

XSS (Cross-Site Scripting) ხდება, როცა მომხმარებლის input გამოგვაქვს HTML-ში გადაუმუშავებლად და ის სკრიპტად სრულდება. დაცვა: გამოტანისას ყოველთვის htmlspecialchars().

მაგალითები

escape გამოტანისას
PHP
<?php
declare(strict_types=1);

$comment = $_GET["comment"] ?? "";

// ❌ საშიში:
// echo "<p>$comment</p>";

// ✅ უსაფრთხო:
echo "<p>" . htmlspecialchars($comment, ENT_QUOTES, "UTF-8") . "</p>";

✓ საუკეთესო პრაქტიკა

  • წესი: input-ს ვალიდაცია შესვლისას, escaping — გამოტანისას.
  • გამოიყენე ENT_QUOTES, რომ ორმაგი და ერთმაგი ბრჭყალებიც დაიფაროს.

Input ვალიდაცია

შემავალი მონაცემების ვალიდაცია

„არასოდეს ენდო მომხმარებლის input-ს". გადაამოწმე ტიპი, ფორმატი და დიაპაზონი. PHP-ს აქვს filter_var() ხშირი შემთხვევებისთვის.

მაგალითები

filter_var-ით ვალიდაცია
PHP
<?php
declare(strict_types=1);

$email = $_POST["email"] ?? "";
$age   = $_POST["age"] ?? "";

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    exit("არასწორი ელფოსტა\n");
}

$age = filter_var($age, FILTER_VALIDATE_INT, [
    "options" => ["min_range" => 1, "max_range" => 120],
]);
if ($age === false) {
    exit("არასწორი ასაკი\n");
}

echo "ვალიდურია: $email, $age\n";

✓ საუკეთესო პრაქტიკა

  • გამოიყენე „whitelist" მიდგომა — დაუშვი მხოლოდ ის, რაც აშკარად სწორია.
  • ვალიდაცია სერვერზე აუცილებელია — კლიენტის (JS) ვალიდაცია მხოლოდ მოხერხებულობისთვისაა.

სავარჯიშოები — თავი 18

📝 სავარჯიშოები

ივარჯიშე უსაფრთხოებაზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დაჰეშე პაროლი და შემდეგ გადაამოწმე password_verify()-ით.

✅ გადაწყვეტა
PHP
<?php
$hash = password_hash("MyPass1", PASSWORD_DEFAULT);
var_dump(password_verify("MyPass1", $hash)); // true
var_dump(password_verify("wrong", $hash));   // false
საშუალო სავარჯიშო 2

დაწერე ფუნქცია safe(string $input): string, რომელიც აბრუნებs XSS-უსაფრთხო ტექსტს გამოსატანად.

💡 მინიშნება
htmlspecialchars + ENT_QUOTES + UTF-8.
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function safe(string $input): string
{
    return htmlspecialchars($input, ENT_QUOTES, "UTF-8");
}

echo safe('<script>alert("x")</script>') . "\n";
პრაქტიკული სავარჯიშო 3

გამოიყენე PDO prepared statement, რომ უსაფრთხოდ მოძებნო მომხმარებელი სახელით (SQLite in-memory ბაზაზე).

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$pdo = new PDO("sqlite::memory:");
$pdo->exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
$pdo->exec("INSERT INTO users (name) VALUES ('ანა'), ('გია')");

$name = "ანა"; // წარმოიდგინე, რომ input-ია
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->execute(["name" => $name]);

print_r($stmt->fetchAll(PDO::FETCH_ASSOC));

ქვიზი — თავი 18

❓ ქვიზი

შეამოწმე ცოდნა უსაფრთხოებაზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. როგორ უნდა შევინახოთ პაროლი?

  2. 2. რა იცავs SQL Injection-ისგან?

  3. 3. რა იცავs XSS-ისგან?

  4. 4. რომელი ფუნქცია ვარგა უსაფრთხო ტოკენისთვის?

  5. 5. სად უნდა მოხდეს input-ის ვალიდაცია?

19 🛠️ თავი 19. პრაქტიკული მინი სერვისები

წინა თავების ცოდნის გაერთიანება რეალურ მინი-სერვისებში: კონტაქტის ფორმა, რეგისტრაცია, შესვლა, ფაილის ატვირთვა, JSON API და CRUD.

კონტაქტის ფორმა

კონტაქტის ფორმის backend

კლასიკური ამოცანა: მიიღე ფორმის მონაცემი, გადაამოწმე და დააბრუნე შედეგი. ვალიდაცია + XSS-დაცვა ერთად.

მაგალითები

contact.php
PHP
<?php
declare(strict_types=1);

$errors = [];

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $name    = trim($_POST["name"] ?? "");
    $email   = trim($_POST["email"] ?? "");
    $message = trim($_POST["message"] ?? "");

    if ($name === "") {
        $errors[] = "სახელი აუცილებელია";
    }
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = "არასწორი ელფოსტა";
    }
    if (mb_strlen($message) < 10) {
        $errors[] = "შეტყობინება ძალიან მოკლეა";
    }

    if (empty($errors)) {
        // აქ შეიძლება შენახვა ფაილში / გაგზავნა ელფოსტაზე
        echo "მადლობა, " . htmlspecialchars($name) . "! შეტყობინება მიღებულია.\n";
    } else {
        foreach ($errors as $e) {
            echo "- $e\n";
        }
    }
}

რეგისტრაცია

მომხმარებლის რეგისტრაცია

რეგისტრაცია: ვალიდაცია, პაროლის ჰეშირება და შენახვა. აქ ვინახავთ JSON ფაილში სიმარტივისთვის (რეალურად — ბაზაში).

მაგალითები

register.php
PHP
<?php
declare(strict_types=1);

function registerUser(string $email, string $password): array
{
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        return ["ok" => false, "error" => "არასწორი ელფოსტა"];
    }
    if (mb_strlen($password) < 8) {
        return ["ok" => false, "error" => "პაროლი მინ. 8 სიმბოლო"];
    }

    $users = file_exists("users.json")
        ? json_decode(file_get_contents("users.json"), true)
        : [];

    foreach ($users as $u) {
        if ($u["email"] === $email) {
            return ["ok" => false, "error" => "ელფოსტა უკვე დარეგისტრირებულია"];
        }
    }

    $users[] = [
        "email" => $email,
        "hash"  => password_hash($password, PASSWORD_DEFAULT),
    ];
    file_put_contents("users.json", json_encode($users, JSON_UNESCAPED_UNICODE));

    return ["ok" => true];
}

print_r(registerUser("ana@example.com", "secret123"));

შესვლა (Login)

შესვლის სისტემა

შესვლა: მოძებნე მომხმარებელი, გადაამოწმე პაროლი password_verify-ით და დაიწყე სესია.

მაგალითები

login.php
PHP
<?php
declare(strict_types=1);
session_start();

function login(string $email, string $password): bool
{
    $users = file_exists("users.json")
        ? json_decode(file_get_contents("users.json"), true)
        : [];

    foreach ($users as $u) {
        if ($u["email"] === $email && password_verify($password, $u["hash"])) {
            session_regenerate_id(true); // session fixation-ისგან დაცვა
            $_SESSION["user"] = $email;
            return true;
        }
    }
    return false;
}

if (login("ana@example.com", "secret123")) {
    echo "მოგესალმებით, {$_SESSION["user"]}\n";
} else {
    echo "არასწორი მონაცემები\n";
}

ფაილის ატვირთვა

ფაილის ატვირთვის ჰენდლერი

ფაილის ატვირთვა $_FILES-ით. აუცილებელია: ტიპის და ზომის ვალიდაცია და move_uploaded_file()-ის გამოყენება.

მაგალითები

upload.php
PHP
<?php
declare(strict_types=1);

const MAX_SIZE = 2 * 1024 * 1024; // 2MB
const ALLOWED = ["image/jpeg" => "jpg", "image/png" => "png"];

if (($_FILES["photo"]["error"] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) {
    $tmp  = $_FILES["photo"]["tmp_name"];
    $size = (int) $_FILES["photo"]["size"];

    $mime = mime_content_type($tmp);
    if (!isset(ALLOWED[$mime])) {
        exit("დაუშვებელი ფაილის ტიპი\n");
    }
    if ($size > MAX_SIZE) {
        exit("ფაილი ძალიან დიდია\n");
    }

    if (!is_dir("uploads")) {
        mkdir("uploads", 0755, true);
    }
    $name = bin2hex(random_bytes(8)) . "." . ALLOWED[$mime];

    if (move_uploaded_file($tmp, "uploads/$name")) {
        echo "ატვირთულია: $name\n";
    }
}

✗ ხშირი შეცდომები

  • ფაილის ტიპის გასაღების ნდობა $_FILES[..]["type"]-ზე — ის ყალბდება; გამოიყენე mime_content_type().
  • ორიგინალი სახელის გამოყენება — საშიშია; დააგენერირე უსაფრთხო სახელი.

JSON API

JSON API endpoint

endpoint, რომელიც კითხულობs JSON სხეულს, ამუშავებს და აბრუნებs JSON პასუხს სწორი header-ითა და status კოდით.

მაგალითები

api.php
PHP
<?php
declare(strict_types=1);
header("Content-Type: application/json; charset=utf-8");

function json(int $status, array $payload): never
{
    http_response_code($status);
    echo json_encode($payload, JSON_UNESCAPED_UNICODE);
    exit;
}

if ($_SERVER["REQUEST_METHOD"] !== "POST") {
    json(405, ["error" => "მხოლოდ POST"]);
}

$body = json_decode(file_get_contents("php://input"), true) ?? [];
$a = $body["a"] ?? null;
$b = $body["b"] ?? null;

if (!is_numeric($a) || !is_numeric($b)) {
    json(422, ["error" => "a და b რიცხვები უნდა იყოს"]);
}

json(200, ["sum" => $a + $b]);

CRUD (PDO)

მარტივი CRUD სერვისი

CRUD = Create, Read, Update, Delete. ეს არის ნებისმიერი აპლიკაციის ბირთვი. ქვემოთ — PDO prepared statements-ით, SQLite-ზე.

მაგალითები

tasks CRUD
PHP
<?php
declare(strict_types=1);

$pdo = new PDO("sqlite::memory:");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec("CREATE TABLE tasks (id INTEGER PRIMARY KEY, title TEXT, done INTEGER DEFAULT 0)");

// CREATE
$create = $pdo->prepare("INSERT INTO tasks (title) VALUES (:title)");
$create->execute(["title" => "PHP-ის სწავლა"]);
$id = (int) $pdo->lastInsertId();

// READ
$read = $pdo->prepare("SELECT * FROM tasks WHERE id = :id");
$read->execute(["id" => $id]);
print_r($read->fetch(PDO::FETCH_ASSOC));

// UPDATE
$pdo->prepare("UPDATE tasks SET done = 1 WHERE id = :id")->execute(["id" => $id]);

// DELETE
$pdo->prepare("DELETE FROM tasks WHERE id = :id")->execute(["id" => $id]);

echo "CRUD დასრულდა\n";

✓ საუკეთესო პრაქტიკა

  • ჩართე PDO::ERRMODE_EXCEPTION — შეცდომები გამონაკლისებად მიიღება.
  • ყოველთვის prepared statements — არასოდეს კონკატენაცია.

სავარჯიშოები — თავი 19

📝 სავარჯიშოები

ივარჯიშე მინი-სერვისებზე.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დაწერე ფუნქცია, რომელიც ამოწმებს, არის თუ არა ფორმის ველი ცარიელი trim-ის შემდეგ.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

function isBlank(?string $value): bool
{
    return trim($value ?? "") === "";
}

var_dump(isBlank("  "));   // true
var_dump(isBlank("ანა"));  // false
საშუალო სავარჯიშო 2

დაწერე ფუნქცია authenticate(), რომელიც მასივ-„ბაზაში" ამოწმებს ელფოსტა/პაროლს (ჰეშით).

💡 მინიშნება
წინასწარ შექმნილ მასივზე password_verify.
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

$db = [
    ["email" => "ana@example.com", "hash" => password_hash("secret123", PASSWORD_DEFAULT)],
];

function authenticate(array $db, string $email, string $pass): bool
{
    foreach ($db as $u) {
        if ($u["email"] === $email && password_verify($pass, $u["hash"])) {
            return true;
        }
    }
    return false;
}

var_dump(authenticate($db, "ana@example.com", "secret123")); // true
პრაქტიკული სავარჯიშო 3

ააგე მინი JSON API: POST-ზე იღებს {"text": "..."} და აბრუნებს მის სიგრძეს (mb_strlen) JSON-ად, ვალიდაციით.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);
header("Content-Type: application/json; charset=utf-8");

$body = json_decode(file_get_contents("php://input"), true) ?? [];
$text = $body["text"] ?? null;

if (!is_string($text) || $text === "") {
    http_response_code(422);
    echo json_encode(["error" => "text აუცილებელია"], JSON_UNESCAPED_UNICODE);
    exit;
}

echo json_encode(["length" => mb_strlen($text)], JSON_UNESCAPED_UNICODE);

ქვიზი — თავი 19

❓ ქვიზი

შეამოწმე ცოდნა მინი-სერვისებზე.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რა უნდა გავაკეთოთ რეგისტრაციისას პაროლთან?

  2. 2. რომელი ფუნქცია გადააქვს ატვირთულ ფაილს უსაფრთხოდ?

  3. 3. რას ვერ ვენდობით ფაილის ატვირთვისას?

  4. 4. რას ნიშნავs CRUD?

  5. 5. რა უნდა ჩავრთოთ login-ის შემდეგ session fixation-ისგან დასაცავად?

20 🎓 თავი 20. დასკვნითი პროექტი

სრული მინი ვებ-სერვისი, რომელიც აერთიანებs ავთენტიფიკაციას, სესიებს, ფორმებს, ფაილებს, JSON API-ს, OOP-ს და შეცდომების დამუშავებას.

მიმოხილვა და მოთხოვნები

პროექტის მიმოხილვა

ავაგებთ მცირე „დავალებების მენეჯერ" (Task Manager) სერვისს. ის გააერთიანებs კურსის ცოდნას:

  • ავთენტიფიკაცია — რეგისტრაცია/შესვლა, პაროლის ჰეშირება;
  • სესიები — შესული მომხმარებლის მდგომარეობა;
  • JSON API — დავალებების CRUD;
  • OOP — სუფთა კლასების სტრუქტურა;
  • შეცდომების დამუშავება — ერთგვაროვანი JSON შეცდომები.

სტრუქტურა

ფაილების სტრუქტურა

კარგი პროექტი დალაგებულია. შემოთავაზებული სტრუქტურა:

მაგალითები

პროექტის layout
სტრუქტურა
task-manager/
├── public/
│   └── index.php          # შესვლის წერტილი (router)
├── src/
│   ├── Database.php       # PDO გადასახვევი
│   ├── User.php           # მომხმარებლის მოდელი
│   ├── TaskRepository.php # დავალებების CRUD
│   └── Auth.php           # რეგისტრაცია/შესვლა/სესია
├── storage/
│   └── app.sqlite         # ბაზა
└── config.php             # კონფიგურაცია

✓ საუკეთესო პრაქტიკა

  • გამიჯნე public/ (ვებიდან ხელმისაწვდომი) src/-ისგან (კოდი).
  • მომავალში გამოიყენე Composer და PSR-4 autoload.

OOP ბირთვი (Database + Repository)

OOP ბირთვი

მონაცემთა ფენა OOP-ით: Database ქმნის PDO კავშირს, TaskRepository კი ახორციელებს CRUD-ს prepared statements-ით.

მაგალითები

Database.php + TaskRepository.php
PHP
<?php
declare(strict_types=1);

final class Database
{
    private PDO $pdo;

    public function __construct(string $dsn)
    {
        $this->pdo = new PDO($dsn);
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->pdo->exec(
            "CREATE TABLE IF NOT EXISTS tasks (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user TEXT NOT NULL,
                title TEXT NOT NULL,
                done INTEGER NOT NULL DEFAULT 0
            )"
        );
    }

    public function pdo(): PDO
    {
        return $this->pdo;
    }
}

final class TaskRepository
{
    public function __construct(private Database $db) {}

    public function create(string $user, string $title): int
    {
        $stmt = $this->db->pdo()->prepare(
            "INSERT INTO tasks (user, title) VALUES (:user, :title)"
        );
        $stmt->execute(["user" => $user, "title" => $title]);
        return (int) $this->db->pdo()->lastInsertId();
    }

    public function allForUser(string $user): array
    {
        $stmt = $this->db->pdo()->prepare("SELECT * FROM tasks WHERE user = :user");
        $stmt->execute(["user" => $user]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function complete(int $id, string $user): void
    {
        $stmt = $this->db->pdo()->prepare(
            "UPDATE tasks SET done = 1 WHERE id = :id AND user = :user"
        );
        $stmt->execute(["id" => $id, "user" => $user]);
    }

    public function delete(int $id, string $user): void
    {
        $stmt = $this->db->pdo()->prepare(
            "DELETE FROM tasks WHERE id = :id AND user = :user"
        );
        $stmt->execute(["id" => $id, "user" => $user]);
    }
}

Auth კლასი

ავთენტიფიკაცია და სესიები

Auth კლასი აერთიანებს რეგისტრაციას, შესვლასა და სესიის მართვას — ერთ, გასაგებ ადგილას.

მაგალითები

Auth.php
PHP
<?php
declare(strict_types=1);

final class Auth
{
    public function __construct(private string $usersFile) {}

    public function register(string $email, string $password): array
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return ["ok" => false, "error" => "არასწორი ელფოსტა"];
        }
        if (mb_strlen($password) < 8) {
            return ["ok" => false, "error" => "პაროლი მინ. 8 სიმბოლო"];
        }
        $users = $this->load();
        if (isset($users[$email])) {
            return ["ok" => false, "error" => "უკვე არსებობს"];
        }
        $users[$email] = password_hash($password, PASSWORD_DEFAULT);
        $this->save($users);
        return ["ok" => true];
    }

    public function login(string $email, string $password): bool
    {
        $users = $this->load();
        if (isset($users[$email]) && password_verify($password, $users[$email])) {
            session_regenerate_id(true);
            $_SESSION["user"] = $email;
            return true;
        }
        return false;
    }

    public function user(): ?string
    {
        return $_SESSION["user"] ?? null;
    }

    public function logout(): void
    {
        $_SESSION = [];
        session_destroy();
    }

    private function load(): array
    {
        return is_file($this->usersFile)
            ? json_decode(file_get_contents($this->usersFile), true)
            : [];
    }

    private function save(array $users): void
    {
        file_put_contents($this->usersFile, json_encode($users, JSON_UNESCAPED_UNICODE));
    }
}

Router + API

JSON API ენდპოინტები

public/index.php — ერთიანი შესვლის წერტილი. ის მარშრუტავს მოთხოვნებს მეთოდისა და მისამართის მიხედვით და აბრუნებs JSON-ს. წვდომა მხოლოდ ავტორიზებულ მომხმარებელს.

მაგალითები

public/index.php
PHP
<?php
declare(strict_types=1);
session_start();
header("Content-Type: application/json; charset=utf-8");

require "../src/Database.php";
require "../src/TaskRepository.php";
require "../src/Auth.php";

function respond(int $status, array $data): never
{
    http_response_code($status);
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

$auth = new Auth("../storage/users.json");
$repo = new TaskRepository(new Database("sqlite:../storage/app.sqlite"));

$method = $_SERVER["REQUEST_METHOD"];
$path   = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
$body   = json_decode(file_get_contents("php://input"), true) ?? [];

// --- public endpoints ---
if ($path === "/login" && $method === "POST") {
    $ok = $auth->login($body["email"] ?? "", $body["password"] ?? "");
    respond($ok ? 200 : 401, ["ok" => $ok]);
}

// --- protected endpoints ---
$user = $auth->user();
if ($user === null) {
    respond(401, ["error" => "ავტორიზაცია საჭიროა"]);
}

match (true) {
    $path === "/tasks" && $method === "GET"
        => respond(200, ["tasks" => $repo->allForUser($user)]),

    $path === "/tasks" && $method === "POST"
        => empty($body["title"])
            ? respond(422, ["error" => "title აუცილებელია"])
            : respond(201, ["id" => $repo->create($user, $body["title"])]),

    default => respond(404, ["error" => "ვერ მოიძებნა"]),
};

ერთგვაროვანი შეცდომები

შეცდომების დამუშავება

API-ში ყველა შეცდომა ერთნაირი ფორმით უნდა დაბრუნდეს. გლობალური try/catch (ან exception handler) დეტალებს წერs ლოგში, კლიენტს კი — სუფთა JSON-ს.

მაგალითები

გლობალური დაცვა
PHP
<?php
declare(strict_types=1);
header("Content-Type: application/json; charset=utf-8");

set_exception_handler(function (Throwable $e): void {
    error_log("[API] " . $e->getMessage() . " @ " . $e->getFile() . ":" . $e->getLine());
    http_response_code(500);
    echo json_encode(["error" => "შიდა შეცდომა"], JSON_UNESCAPED_UNICODE);
});

// ნებისმიერი დაუჭერელი exception ერთიან JSON-ად დაბრუნდება:
throw new RuntimeException("ბაზა მიუწვდომელია");

✓ საუკეთესო პრაქტიკა

  • ერთიანი error ფორმატი: {"error": "..."} + სწორი status code.
  • დეტალები — ლოგში, არასოდეს კლიენტს.

შეჯამება

შეჯამება და შემდეგი ნაბიჯები

გილოცავ — გაიარე PHP-ის სრული საფუძვლები ცვლადებიდan სრულ ვებ-სერვისამდე! 🎉

შემდეგი ნაბიჯები:

  • Composer — დამოკიდებულებების მენეჯერი და PSR-4 autoload;
  • ფრეიმვორკები — Laravel ან Symfony;
  • მონაცემთა ბაზები — MySQL/PostgreSQL, migrations;
  • ტესტირება — PHPUnit;
  • სტანდარტები — PSR-12 კოდის სტილი, static analysis (PHPStan).
💡 რჩევა: საუკეთესო გზა სწავლისა — საკუთარი პროექტის აგება. გააფართოვე ეს Task Manager: დაამატე დავალების რედაქტირება, ვადები, კატეგორიები.

სავარჯიშოები — თავი 20

📝 სავარჯიშოები

გააფართოვე დასკვნითი პროექტი.

📝 სავარჯიშოები

დამწყები სავარჯიშო 1

დაამატე TaskRepository-ს მეთოდი count(string $user): int, რომელიც აბრუნებs მომხმარებლის დავალებების რაოდენობას.

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

// TaskRepository-ში დასამატებელი მეთოდი:
public function count(string $user): int
{
    $stmt = $this->db->pdo()->prepare(
        "SELECT COUNT(*) FROM tasks WHERE user = :user"
    );
    $stmt->execute(["user" => $user]);
    return (int) $stmt->fetchColumn();
}
საშუალო სავარჯიშო 2

დაამატე API-ში DELETE /tasks/{id} ენდპოინტი, რომელიც წაშლის მომხმარებლის დავალებას.

💡 მინიშნება
amოიღე id მისამართიდan preg_match-ით და გამოიძახე $repo->delete().
✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

// index.php-ის match-ში დასამატებელი შტო:
if ($method === "DELETE" && preg_match("#^/tasks/(\d+)$#", $path, $m)) {
    $repo->delete((int) $m[1], $user);
    respond(200, ["ok" => true]);
}
პრაქტიკული სავარჯიშო 3

დაამატე რეგისტრაციის ენდპოინტი POST /register, რომელიც იყენებს Auth::register()-ს და აბრუნებს სათანადო status კოდს (201 ან 422).

✅ გადაწყვეტა
PHP
<?php
declare(strict_types=1);

// index.php-ის public endpoints-ში:
if ($path === "/register" && $method === "POST") {
    $result = $auth->register($body["email"] ?? "", $body["password"] ?? "");
    respond($result["ok"] ? 201 : 422, $result);
}

ქვიზი — თავი 20

❓ ქვიზი

დასკვნითი ქვიზი — შეაჯამე კურსი.

🧠 შეამოწმე შენი ცოდნა 0 / 5
  1. 1. რატომ ვაცალკევებთ public/-ს src/-ისგან?

  2. 2. რა მიდგომით უნდა მუშაობდეს API ბაზასთან?

  3. 3. როგორ უნდა დაბრუნდეს API შეცდომა?

  4. 4. რა ხდება, თუ $auth->user() აბრუნებს null-ს?

  5. 5. რომელი ხელსაწყო გამოიყენება დამოკიდებულებების სამართავად PHP-ში?