Налаштування фаєрвола на Debian 13: закриваємо всі порти, залишаємо тільки SSH

Після встановлення чистого сервера Debian 13 одним із перших кроків має бути налаштування мережевої безпеки. Якщо доступ до сервера здійснюється виключно через SSH з використанням ключів, доцільно повністю закрити всі вхідні порти та залишити відкритим лише порт 22 для SSH.

У Debian 13 стандартним інструментом для фільтрації мережевого трафіку є nftables. Він входить до складу сучасних версій Linux і дозволяє гнучко налаштовувати правила фаєрвола.

Встановлення nftables

Оновіть список пакетів та встановіть nftables:

apt update
apt install nftables -y

Створення конфігурації фаєрвола

Відкрийте файл конфігурації:

nano /etc/nftables.conf

Вставте наступний вміст:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {

    chain input {
        type filter hook input priority 0;
        policy drop;

        # localhost
        iif lo accept

        # вже встановлені з'єднання
        ct state established,related accept

        # SSH
        tcp dport 22 accept

        # ping (необов'язково)
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept
    }

    chain forward {
        type filter hook forward priority 0;
        policy drop;
    }

    chain output {
        type filter hook output priority 0;
        policy accept;
    }
}

Перевірка конфігурації

Перед застосуванням правил необхідно переконатися, що конфігурація не містить помилок:

nft -c -f /etc/nftables.conf

Якщо помилок не виявлено, можна активувати фаєрвол.

Запуск та автозавантаження

systemctl enable nftables
systemctl restart nftables

Перевірити завантажені правила можна командою:

nft list ruleset

Що роблять ці правила

  • Блокують усі вхідні підключення за замовчуванням.
  • Дозволяють локальний трафік через інтерфейс localhost.
  • Дозволяють вже встановлені та пов’язані з’єднання.
  • Відкривають лише порт 22 для SSH.
  • Дозволяють вихідні з’єднання сервера.
  • Блокують пересилання пакетів (forward).

Перевірка відкритих портів

Після налаштування фаєрвола рекомендується перевірити сервер з іншого пристрою:

nmap -Pn IP_СЕРВЕРА

У результаті повинен залишитися доступним лише SSH-порт:

22/tcp open ssh

Додатковий захист SSH

Якщо ви підключаєтесь до сервера лише з однієї статичної IP-адреси, можна дозволити доступ до SSH тільки з неї.

Замість правила:

tcp dport 22 accept

використовуйте:

ip saddr 203.0.113.55 tcp dport 22 accept

де 203.0.113.55 необхідно замінити на власну IP-адресу.

У такому випадку SSH буде доступний виключно з вказаної адреси, а всі інші спроби підключення будуть автоматично блокуватися.

Важливе застереження

Перед застосуванням нових правил рекомендується відкрити другу SSH-сесію та не закривати її до завершення перевірки. Це дозволить уникнути втрати доступу до сервера у випадку помилки в конфігурації фаєрвола.

Use To Find Hidden Website Pages

Most companies have an admin portal page, giving their staff access to basic admin controls for day-to-day operations. For a bank, an employee might need to transfer money to and from client accounts. Due to human error or negligence, there may be instances when these pages are not made private, allowing attackers to find hidden pages that show or give access to admin controls or sensitive data.

To begin, type the following command into the terminal to find potentially hidden pages on FakeBank’s website using (a command-line security application).

gobuster -u http://fakebank.thm -w wordlist.txt dir

The command will run and show you an output similar to this:

ubuntu@tryhackme:~/Desktop$ gobuster -u http://fakebank.thm -w wordlist.txt dir

=====================================================
Gobuster v2.0.1              OJ Reeves (@TheColonial)
=====================================================
[+] Mode         : dir
[+] Url/Domain   : http://fakebank.thm/
[+] Threads      : 10
[+] Wordlist     : wordlist.txt
[+] Status codes : 200,204,301,302,307,403
[+] Timeout      : 10s
=====================================================
2024/05/21 10:04:38 Starting gobuster
=====================================================
/images (Status: 301)
/bank-transfer (Status: 200)
=====================================================
2024/05/21 10:04:44 Finished
=====================================================

MikroTik Traffic Classification Using Mangle

This guide explains how to classify network traffic in
RouterOS running on
MikroTik routers using
Firewall Mangle.
The goal is to divide traffic into five logical directions:

  • LocalToGlobal – client traffic going to the Internet
  • GlobalToLocal – Internet traffic coming to clients
  • LocalToLocal – traffic between subscribers in the same network
  • LocalToMikrotik – traffic from subscribers to the router
  • MikrotikToLocal – traffic generated by the router to clients

This traffic model is very useful for Internet Service Providers and large
local networks because it allows:

  • bandwidth shaping using Queue Tree and PCQ
  • blocking client-to-client traffic
  • prioritizing management or service traffic
  • monitoring router-generated traffic

1. Network Concept

The following diagram represents the logical traffic directions:

Client → Internet      LocalToGlobal
Internet → Client      GlobalToLocal
Client → Client        LocalToLocal
Client → Router        LocalToMikrotik
Router → Client        MikrotikToLocal

To implement this architecture we will use two steps:

  1. Mark connections
  2. Mark packets based on those connections

2. Creating an Address List for Local Networks

First create an address list that contains all subscriber networks.
This list will be used to detect internal traffic.

/ip firewall address-list

add list=Local address=10.0.0.0/8
add list=Local address=172.16.0.0/12
add list=Local address=192.168.0.0/16

If your network uses a different addressing scheme, replace these ranges with
your actual subscriber networks.


3. Marking Connections

Connection marking is the first step.
Each new connection will be assigned a specific direction label.

LocalToGlobal (Client → Internet)

/ip firewall mangle
add chain=prerouting \
src-address-list=Local \
dst-address-list=!Local \
connection-mark=no-mark \
action=mark-connection \
new-connection-mark=LocalToGlobal_conn \
passthrough=yes

GlobalToLocal (Internet → Client)

add chain=prerouting \
src-address-list=!Local \
dst-address-list=Local \
connection-mark=no-mark \
action=mark-connection \
new-connection-mark=GlobalToLocal_conn \
passthrough=yes

LocalToLocal (Client → Client)

add chain=prerouting \
src-address-list=Local \
dst-address-list=Local \
connection-mark=no-mark \
action=mark-connection \
new-connection-mark=LocalToLocal_conn \
passthrough=yes

LocalToMikrotik (Client → Router)

add chain=input \
src-address-list=Local \
connection-mark=no-mark \
action=mark-connection \
new-connection-mark=LocalToMikrotik_conn \
passthrough=yes

MikrotikToLocal (Router → Client)

add chain=output \
dst-address-list=Local \
connection-mark=no-mark \
action=mark-connection \
new-connection-mark=MikrotikToLocal_conn \
passthrough=yes

4. Marking Packets

Once connections are marked, we classify packets belonging to those connections.
These packet marks are later used for traffic shaping and filtering.

Internet Upload Traffic

add chain=forward \
connection-mark=LocalToGlobal_conn \
action=mark-packet \
new-packet-mark=LocalToGlobal_pkt \
passthrough=no

Internet Download Traffic

add chain=forward \
connection-mark=GlobalToLocal_conn \
action=mark-packet \
new-packet-mark=GlobalToLocal_pkt \
passthrough=no

Local Network Traffic

add chain=forward \
connection-mark=LocalToLocal_conn \
action=mark-packet \
new-packet-mark=LocalToLocal_pkt \
passthrough=no

Traffic to Router

add chain=input \
connection-mark=LocalToMikrotik_conn \
action=mark-packet \
new-packet-mark=LocalToMikrotik_pkt \
passthrough=no

Router Generated Traffic

add chain=output \
connection-mark=MikrotikToLocal_conn \
action=mark-packet \
new-packet-mark=MikrotikToLocal_pkt \
passthrough=no

5. Practical Use Cases

Bandwidth Limiting

The marks LocalToGlobal_pkt and GlobalToLocal_pkt
can be used with Queue Tree to limit upload and download speeds per subscriber.

Blocking Client-to-Client Traffic

/ip firewall filter
add chain=forward connection-mark=LocalToLocal_conn action=drop

This prevents subscribers from accessing devices of other subscribers.

Allowing Router Access

Even when LocalToLocal traffic is blocked, clients can still reach the router
using the LocalToMikrotik direction for services like:

  • DNS
  • DHCP
  • WinBox
  • API
  • Monitoring

6. Important Notes

  • Disable FastTrack if Queue Tree shaping is used.
  • Ensure address lists correctly represent your network.
  • Keep connection marking rules above packet marking rules.
  • Test rules using /ip firewall mangle print stats.

Conclusion

This five-direction traffic classification model provides a flexible and
powerful framework for managing large subscriber networks on MikroTik routers.
By clearly separating Internet traffic, local traffic, and router traffic,
network administrators gain precise control over bandwidth usage, security,
and service prioritization.

Source: AiBlockLab.com – MikroTik Traffic Classification Using Mangle

How to Create a New WireGuard Client on a Linux Server (Debian/Ubuntu)

Wireguard is a modern, fast, and secure VPN solution.
This guide explains how to create a new WireGuard client on a Linux server
(Debian or Ubuntu) and connect it from a mobile device or desktop.


Prerequisites

  • A Linux server with WireGuard already installed
  • Root or sudo access
  • An existing WireGuard interface (e.g. wg0)

Example network used in this guide:

  • VPN subnet: 10.0.0.0/24
  • Server IP: 10.0.0.1
  • New client IP: 10.0.0.2

Step 1: Generate Client Keys

Navigate to the WireGuard configuration directory and set a secure file creation mask:

cd /etc/wireguard
umask 077

Generate the client private and public keys:

wg genkey | tee client1.key | wg pubkey > client1.pub

This creates:

  • client1.key — private key (keep secret)
  • client1.pub — public key

Step 2: Add the Client to the Server Configuration

Edit the server configuration file:

nano /etc/wireguard/wg0.conf

Add a new [Peer] section:

[Peer]
PublicKey = CLIENT1_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32

Important: Each client must have a unique IP address.


Step 3: Apply the Configuration

Reload WireGuard without disconnecting active clients:

wg syncconf wg0 <(wg-quick strip wg0)

Or restart the interface:

systemctl restart wg-quick@wg0

Step 4: Create the Client Configuration File

Create a client configuration file:

nano client1.conf

Insert the following configuration:

[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY
Address = 10.0.0.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = SERVER_IP:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25

Step 5: Generate a QR Code (Optional)

For mobile devices, you can generate a QR code:

apt install qrencode
qrencode -t ansiutf8 < client1.conf

Scan the QR code using the WireGuard mobile app to import the tunnel instantly.


Step 6: Verify the Connection

On the server, check the tunnel status:

wg show

If the client is connected, you will see:

  • Latest handshake timestamp
  • Data transfer statistics

Common Configuration Variants

Split Tunnel (VPN only for internal network)

AllowedIPs = 10.0.0.0/24

Full Tunnel (all traffic via VPN)

AllowedIPs = 0.0.0.0/0, ::/0

Security Notes

  • Never reuse client IP addresses
  • Protect private keys with file permissions (600)
  • Use PersistentKeepalive = 25 for mobile clients behind NAT

Conclusion

WireGuard makes VPN client management simple and secure.
By following this guide, you can safely add new clients,
generate configuration files, and connect from any modern device.

This setup works equally well for Android, iOS, Linux, Windows, and macOS clients.

Source: AIBlockLab.com

Your Gateway to Practical AI Solutions and Intelligent Automation

Artificial Intelligence is no longer a concept of the future — it is a powerful tool that is already transforming businesses, technologies, and entire industries. AIBLOCKLAB.com is a modern platform dedicated to helping individuals, startups, and companies unlock the real potential of AI through practical solutions, expert insights, and cutting-edge automation strategies.

What Is AIBLOCKLAB.com?

AIBLOCKLAB.com is an AI-focused knowledge hub and innovation platform that covers everything from artificial intelligence technologies to real-world business automation. The site is designed for entrepreneurs, developers, marketers, and tech enthusiasts who want to stay ahead in the rapidly evolving AI landscape.

Unlike generic AI blogs, this AI solutions platform focuses on practical implementation, real use cases, and actionable insights that can be applied immediately.

Key Areas Covered by AIBLOCKLAB.com

🔹 AI Solutions Development

The platform explores how custom AI solutions can be designed and implemented for various industries, including finance, e-commerce, crypto, and digital services. Articles explain complex AI concepts in a clear and structured way, making them accessible even to non-technical readers.

🔹 Business Automation

Automation is one of the strongest advantages of artificial intelligence. AIBLOCKLAB.com provides in-depth guides on automating workflows, optimizing business processes, reducing operational costs, and improving productivity using AI-powered tools.

🔹 Machine Learning and Data Intelligence

From basic machine learning principles to advanced models and real-world applications, the site offers high-quality educational content that helps readers understand how data-driven decisions are made using modern AI algorithms.

🔹 AI and Emerging Technologies

The website also acts as a reliable source of AI and business automation insights, covering trends related to blockchain, cryptocurrency, smart systems, and digital transformation.

Why AIBLOCKLAB.com Stands Out

✔ High-quality, original content
✔ SEO-optimized structure
✔ Focus on real-world AI use cases
✔ Coverage of modern AI and automation trends
✔ English-language content for a global audience

Who Should Use AIBLOCKLAB.com?

  • Business owners looking to automate processes
  • Startups searching for AI-driven growth strategies
  • Developers and engineers exploring AI technologies
  • Marketers interested in AI-powered tools
  • Crypto and fintech professionals leveraging AI insights

Final Thoughts

Artificial Intelligence is reshaping the digital world, and platforms like AIBLOCKLAB.com play a crucial role in making AI accessible, practical, and profitable. Whether you want to automate your business, understand machine learning, or explore AI-powered innovations, this platform delivers real value.

To learn more about practical AI applications, visit https://aiblocklab.com and explore how intelligent automation can transform the way you work.

How to create Telegram Bot in PHP

Creating a Telegram bot in PHP is a great way to automate tasks, provide customer support, send notifications, or build interactive services. Telegram offers a powerful and simple Bot API, and PHP makes it easy to handle web requests and responses.

In this step-by-step guide, you will learn how to create a Telegram bot using PHP, set up a webhook, and build a simple working example.

Step 1: Create a Telegram Bot with BotFather

Before writing any PHP code, you need to create your bot inside Telegram.

  1. Open Telegram and search for BotFather.
  2. Start a chat and send the command: /newbot
  3. Follow the instructions and choose a bot name and username.
  4. After creation, you will receive a Bot Token.

Save this token — you will need it to connect your PHP script to the Telegram Bot API.

Step 2: Set Up Your PHP Environment

To create a Telegram bot in PHP, you need:

  • A web server (Apache or Nginx)
  • PHP 7.4 or higher
  • HTTPS domain (Telegram requires SSL for webhooks)

Upload a PHP file (for example, bot.php) to your server.

Step 3: Basic Telegram Bot PHP Script

Below is a simple example of a Telegram bot written in PHP:

<?php
$token = "YOUR_BOT_TOKEN";
$apiURL = "https://api.telegram.org/bot" . $token . "/";


$content = file_get_contents("php://input");
$update = json_decode($content, true);


if (isset($update["message"])) {
$chatId = $update["message"]["chat"]["id"];
$text = $update["message"]["text"];


$response = "You said: " . $text;


file_get_contents($apiURL . "sendMessage?chat_id=" . $chatId . "&text=" . urlencode($response));
}
?>

Replace YOUR_BOT_TOKEN with the token provided by BotFather.

Step 4: Set Up a Webhook

Telegram needs to know where to send updates. You can set a webhook using this URL in your browser:

https://api.telegram.org/botYOUR_BOT_TOKEN/setWebhook?url=https://yourdomain.com/bot.php

If successful, Telegram will send messages directly to your PHP script whenever someone interacts with your bot.

Step 5: Testing Your Telegram PHP Bot

Now open your bot in Telegram and send a message. If everything is configured correctly, the bot will reply with:

You said: your message

Using cURL Instead of file_get_contents

For better performance and error handling, it is recommended to use cURL in PHP:

<?php
function sendMessage($chatId, $message, $token) {
$url = "https://api.telegram.org/bot$token/sendMessage";


$data = [
"chat_id" => $chatId,
"text" => $message
];


$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
?>

Best Practices for Telegram Bots in PHP

  • Always validate incoming data.
  • Use a framework for larger projects (Laravel, Symfony).
  • Store user data in a database (MySQL, PostgreSQL).
  • Use logging for debugging.
  • Secure your webhook endpoint.

Conclusion

Now you know how to create a Telegram bot in PHP using the Telegram Bot API and webhooks. With just a few lines of PHP code, you can build automated systems, chat assistants, and business tools.

Telegram bots are scalable, fast, and easy to maintain. As your project grows, you can expand your bot with inline keyboards, commands, databases, and third-party integrations.

Start building your PHP Telegram bot today and automate your workflow efficiently.

Source: AiBlockLab.com: How to Create a Telegram Bot in PHP – Step-by-Step Guide

Як підключити USB 3G-модем Huawei E173 до Linux Mint

Huawei E173 — популярний USB 3G-модем, який добре підтримується Linux, але на сучасних версіях Linux Mint часто виникає ситуація, коли ModemManager не бачить модем, навіть якщо драйвери завантажені правильно.

У цій статті показано реальний робочий спосіб підключення Huawei E173 до Linux Mint, включно з діагностикою та стабільним рішенням через wvdial.


1. Перевірка визначення модема системою

Після підключення модема до USB перевіримо, чи бачить його система:

lsusb

Очікуваний результат:

Bus 001 Device 010: ID 12d1:1506 Huawei Technologies Co., Ltd. Modem/Networkcard

Важливо: ID 12d1:1506 означає, що модем уже в режимі модема (не CD-ROM), і usb_modeswitch не потрібен.


2. Перевірка портів ttyUSB

Модем Huawei E173 працює через послідовні порти. Перевіримо їх наявність:

ls /dev/ttyUSB*

Нормальний результат:

/dev/ttyUSB0
/dev/ttyUSB1
/dev/ttyUSB2

3. Перевірка драйверів ядра

Переконаємося, що драйвер option завантажений:

lsmod | grep option

Очікувано:

option      69632  1
usb_wwan    24576  1 option
usbserial   57344  4 usb_wwan,option

Це означає, що ядро Linux повністю готове до роботи з модемом.


4. Чому ModemManager не бачить Huawei E173

Команда:

mmcli -L

може повертати:

No modems were found

Це типова проблема на нових версіях Linux Mint. ModemManager орієнтований на LTE / QMI / MBIM і часто ігнорує старі 3G-модеми Huawei.

Рішення — використати перевірений класичний метод через wvdial.


5. Встановлення wvdial

sudo apt update
sudo apt install wvdial

6. Налаштування wvdial

Відкрий конфігураційний файл:

sudo nano /etc/wvdial.conf

Встав наступний конфіг:

[Dialer Defaults]
Init1 = ATZ
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2
Init3 = AT+CGDCONT=1,"IP","internet"
Modem Type = Analog Modem
Phone = *99#
Username = user
Password = pass
Modem = /dev/ttyUSB0
Baud = 460800
Stupid Mode = 1
Auto DNS = 1
Carrier Check = no

APN для українських операторів

  • Kyivstarwww.kyivstar.net
  • Vodafone UAinternet
  • Lifecellinternet

APN змінюється в рядку Init3.


7. Підключення до інтернету

sudo wvdial

Успішне підключення виглядає так:

--> Dialing *99#
--> Starting pppd
--> local  IP address 10.x.x.x
--> remote IP address 10.x.x.x
--> primary   DNS address

Це означає, що з’єднання встановлено і інтернет працює.

Для відключення використовуйте Ctrl + C.


8. Якщо не підключається

Спробуйте змінити порт модема:

Modem = /dev/ttyUSB1

або

Modem = /dev/ttyUSB2

І повторіть команду sudo wvdial.


9. Перевірка мережевого інтерфейсу

Після підключення перевірте:

ip a

Має з’явитися інтерфейс ppp0.


Як у PDO перейти з MySQL на PostgreSQL

PDO (PHP Data Objects) дозволяє змінити базу даних з MySQL на PostgreSQL без переписування PHP-коду, якщо код написаний правильно. Однак потрібно розуміти, що саме змінюється, а що — ні.


1. Головне правило

PDO прибирає залежність від драйвера БД, але не від SQL-діалекту.

  • ✔ PHP-код зазвичай не змінюється
  • ✔ змінюється DSN підключення
  • ❌ SQL-запити та структура БД можуть потребувати адаптації

2. Заміна DSN (обовʼязково)

Було: MySQL

$pdo = new PDO(
    "mysql:host=localhost;dbname=test_db;charset=utf8mb4",
    "db_user",
    "db_pass",
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]
);

Стало: PostgreSQL

$pdo = new PDO(
    "pgsql:host=localhost;port=5432;dbname=test_db",
    "db_user",
    "db_pass",
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
    ]
);

Весь інший PHP-код залишається без змін.


3. PDO-код, який працює і в MySQL, і в PostgreSQL

$stmt = $pdo->prepare(
    "SELECT id, name FROM users WHERE active = :active"
);

$stmt->execute([
    'active' => true
]);

$rows = $stmt->fetchAll();

4. Що ламається при переході (і як виправити)

LIMIT offset, count

MySQL:

LIMIT 10, 20

PostgreSQL:

LIMIT 20 OFFSET 10

Універсальний варіант:

LIMIT :limit OFFSET :offset

AUTO_INCREMENT

MySQL:

id INT AUTO_INCREMENT

PostgreSQL:

id SERIAL
-- або
id INT GENERATED ALWAYS AS IDENTITY

PHP-код не змінюється, якщо використовується lastInsertId().


INSERT IGNORE

MySQL:

INSERT IGNORE INTO users (...)

PostgreSQL:

INSERT INTO users (...)
ON CONFLICT DO NOTHING

ON DUPLICATE KEY UPDATE

MySQL:

ON DUPLICATE KEY UPDATE name = VALUES(name)

PostgreSQL:

ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name

IF()

MySQL:

IF(active = 1, 'yes', 'no')

PostgreSQL:

CASE WHEN active THEN 'yes' ELSE 'no' END

5. Типи даних, які потрібно змінити

MySQL PostgreSQL
TINYINT(1) BOOLEAN
DATETIME TIMESTAMP
LONGTEXT TEXT
DOUBLE DOUBLE PRECISION

6. JSON — різний синтаксис

MySQL:

JSON_EXTRACT(data, '$.name')

PostgreSQL:

data->>'name'

7. Рекомендовані налаштування PDO

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false
];

8. Коли можна змінити БД без змін PHP

  • ✔ використовується PDO
  • ✔ немає MySQL-специфічного SQL
  • ✔ відсутні FULLTEXT, ENUM, REPLACE INTO

9. Висновок

Перехід з MySQL на PostgreSQL у PDO — це перш за все:

  • 🔹 заміна DSN
  • 🔹 адаптація SQL-запитів
  • 🔹 міграція структури БД

PDO значно спрощує міграцію, але не є повною абстракцією від SQL.

Рекомендація: завжди пишіть портований SQL, щоб у майбутньому легко змінювати СУБД.

Як використовувати цикл for з PDO SELECT у PHP

PDO не дозволяє напряму перебирати результат запиту циклом for. Але цикл for можна використовувати, якщо попередньо отримати дані у вигляді масиву.


1. Основна ідея

Алгоритм такий:

  1. Виконати SELECT через PDO
  2. Отримати всі рядки методом fetchAll()
  3. Перебрати масив циклом for

2. Простий приклад SELECT + for

$stmt = $pdo->query(
    "SELECT id, name, email FROM users"
);

$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

for ($i = 0; $i < count($rows); $i++) {
    echo $rows[$i]['id'] . ' - ';
    echo $rows[$i]['name'] . ' - ';
    echo $rows[$i]['email'] . '<br>';
}

3. Оптимізований варіант (рекомендовано)

Щоб не викликати count() на кожній ітерації, краще зберегти кількість елементів у змінну:

$total = count($rows);

for ($i = 0; $i < $total; $i++) {
    echo $rows[$i]['name'] . '<br>';
}

4. SELECT з параметрами + for

$stmt = $pdo->prepare(
    "SELECT id, name FROM users WHERE active = :active"
);

$stmt->execute([
    'active' => 1
]);

$users = $stmt->fetchAll(PDO::FETCH_ASSOC);

for ($i = 0, $c = count($users); $i < $c; $i++) {
    echo $users[$i]['id'] . ' - ';
    echo $users[$i]['name'] . '<br>';
}

5. Поширена помилка (НЕПРАВИЛЬНО)

for ($i = 0; $row = $stmt->fetch(); $i++) {
    // ❌ так робити не можна
}

Метод fetch() повертає false, а цикл for не призначений для такої логіки.


6. Кращі альтернативи циклу for

while (рекомендовано для великих вибірок)

while ($row = $stmt->fetch()) {
    echo $row['name'] . '<br>';
}

foreach (найчистіший код)

foreach ($users as $row) {
    echo $row['name'] . '<br>';
}

7. Коли варто використовувати for

  • ✔ Потрібен індекс елемента
  • ✔ Дані вже у масиві
  • ✔ Є логіка, залежна від позиції

Коли НЕ варто

  • ❌ Для великих SELECT-запитів
  • ❌ Коли не потрібен індекс

8. Висновок

Цикл for можна використовувати з PDO лише після fetchAll(). У більшості випадків foreach або while будуть простішими та ефективнішими.

Рекомендація: використовуйте for тільки тоді, коли це дійсно виправдано.

Повний гайд: як користуватися PDO в PHP

PDO (PHP Data Objects) — це сучасний, безпечний та універсальний спосіб роботи з базами даних у PHP. PDO підтримує MySQL, PostgreSQL, SQLite та інші СУБД і дозволяє писати портований код без прив’язки до конкретної бази.

У цій статті ви навчитеся користуватися PDO на практиці: від підключення до бази даних до виконання складних запитів і транзакцій.


1. Чому варто використовувати PDO

  • ✔ Захист від SQL-інʼєкцій
  • ✔ Prepared statements
  • ✔ Єдиний API для різних СУБД
  • ✔ Підтримка транзакцій
  • ✔ Зручна обробка помилок

2. Підключення до бази даних через PDO

<?php
$dsn  = "mysql:host=localhost;dbname=test_db;charset=utf8mb4";
$user = "db_user";
$pass = "db_password";

try {
    $pdo = new PDO($dsn, $user, $pass, [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,
    ]);
} catch (PDOException $e) {
    die("Помилка підключення: " . $e->getMessage());
}

3. SELECT-запити

Простий SELECT

$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();

SELECT з параметрами (prepared statement)

$stmt = $pdo->prepare(
    "SELECT * FROM users WHERE email = :email"
);

$stmt->execute([
    'email' => '[email protected]'
]);

$user = $stmt->fetch();

4. INSERT, UPDATE, DELETE

INSERT

$stmt = $pdo->prepare(
    "INSERT INTO users (name, email)
     VALUES (:name, :email)"
);

$stmt->execute([
    'name'  => 'Ivan',
    'email' => '[email protected]'
]);

$lastId = $pdo->lastInsertId();

UPDATE

$stmt = $pdo->prepare(
    "UPDATE users SET name = :name WHERE id = :id"
);

$stmt->execute([
    'name' => 'Petro',
    'id'   => 5
]);

DELETE

$stmt = $pdo->prepare(
    "DELETE FROM users WHERE id = :id"
);

$stmt->execute([
    'id' => 10
]);

5. Плейсхолдери в PDO

Іменовані

WHERE id = :id

Позиційні

WHERE id = ?
$stmt->execute([5]);

6. Типи даних та bindValue

Для числових значень, LIMIT та OFFSET рекомендується явно вказувати тип:

$stmt = $pdo->prepare(
    "SELECT * FROM users
     WHERE active = :active
     LIMIT :limit OFFSET :offset"
);

$stmt->bindValue(':active', true, PDO::PARAM_BOOL);
$stmt->bindValue(':limit', 20, PDO::PARAM_INT);
$stmt->bindValue(':offset', 0, PDO::PARAM_INT);

$stmt->execute();

7. Отримання результатів

  • fetch() — один рядок
  • fetchAll() — всі рядки
  • rowCount() — не рекомендовано для SELECT

8. Транзакції

try {
    $pdo->beginTransaction();

    $pdo->exec(
        "UPDATE accounts
         SET balance = balance - 100
         WHERE id = 1"
    );

    $pdo->exec(
        "UPDATE accounts
         SET balance = balance + 100
         WHERE id = 2"
    );

    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
    echo "Помилка транзакції";
}

9. Обробка помилок

try {
    $pdo->query("SELECT * FROM unknown_table");
} catch (PDOException $e) {
    echo $e->getMessage();
}

10. Типові помилки при роботі з PDO

  • ❌ Конкатенація змінних у SQL
  • ❌ Відсутність prepared statements
  • ❌ ATTR_EMULATE_PREPARES = true
  • ❌ rowCount() для SELECT

11. PDO у WordPress

Хоча WordPress використовує $wpdb, PDO можна застосовувати:

  • у власних плагінах
  • у REST API
  • у CLI-скриптах

12. Висновок

PDO — це стандарт для сучасного PHP. Використання prepared statements, транзакцій та портованого SQL робить код безпечним, масштабованим і готовим до майбутніх змін.

Рекомендація: завжди використовуйте PDO у нових PHP-проєктах.